)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":9914,"name":"Ade Lee","email":"alee@redhat.com","username":"alee"},"change_message_id":"6d21e0b7fbe0ba2899fe6a55fa8817dace7fdf0b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"198ef03b_d6c794f9","updated":"2026-07-22 09:46:01.000000000","message":"Looks like what was originally proposed - with some small changes - specifically that we modify an existing ACL to handle the new permissions.","commit_id":"5ca0f92c5df8888725bb2f940546768d63bbb85c"},{"author":{"_account_id":27900,"name":"Artem Goncharov","email":"artem.goncharov@gmail.com","username":"gtema"},"change_message_id":"17492e0b8aa81a3ef6f8f5603f45b0b585d8a98b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"2514f51f_5126ac8b","updated":"2026-10-02 17:07:25.000000000","message":"# Explicit user IDs spec\n\nSpec line numbers are approximate.\n\n## 1. [HIGH] Conceptual: federated and LDAP users already have deterministic surrogate IDs\n\nThe spec\u0027s motivation (:20-47) is cross-region ID consistency, and it concedes\n(:51-55) that LDAP shadow-user IDs are already consistent once domain IDs match.\n`create_federated_user` (identity/shadow_backends/sql.py:33-45) computes\n`generate_public_ID({\u0027domain_id\u0027, \u0027local_id\u0027: unique_id, \u0027entity_type\u0027: \u0027user\u0027})`\n(sha256, identity/id_generators/sha256.py) and writes it as `user.id`. Mapped\nLDAP gets the same generator through `id_mapping`\n(mapping_backends/sql.py:74-79; `_is_mapping_needed`, identity/core.py:848).\nKeystone already treats the user ID as an internal surrogate; the identity is\n`(idp_id, protocol_id, unique_id)` or `(domain_id, local_id)`.\n\n**Precision: deterministic given `(domain_id, unique_id)`, not free of\ncoordination.** The ID is a pure function of external inputs: `domain_id` (the\nIdP\u0027s domain), `local_id` (the federated `unique_id`, i.e. whatever the mapping\nrules emit as `user.id`), and the constant entity type. Nothing else is hashed.\n\n- Reproducible across regions only if they share the same `domain_id` and\n  receive the same `unique_id` (same IdP, equivalent mapping rules). Anyone who\n  knows the two values can compute the ID offline (this is also what the spec\u0027s\n  collision attack relies on).\n- Not reproducible when the `unique_id` differs per region (separate IdP or\n  tenant per region, different claim selected by mapping rules). Changing a\n  mapping rule later silently yields a new user ID for the same person.\n- Callers cannot choose the ID: they pick the inputs, and the result is a\n  64-char hex hash, never a canonical UUID. An operator who needs a specific\n  externally-issued UUID has no federated/LDAP path; that residual case is the\n  only real gap the spec fills, and it should be argued as such.\n- Open item (unverified, needs a test): `idp_id` and `protocol_id` are not part\n  of the hash, so the same `unique_id` from two IdPs in one domain yields the\n  same `user.id`. The `federated_user` unique key\n  `(idp_id, protocol_id, unique_id)` would not prevent it, and the second login\n  would hit a PK conflict on `user.id`.\n\nConsequences for the spec:\n\n- The feature only serves local SQL users, which the spec states in one sentence\n  (:55) and never develops.\n- The ID Format Constraints section (:111-190) lists AD `objectGUID`, Entra\n  `id`, and `entryUUID` as IDs operators will want to reuse. If identity is in\n  an IdP, the user should come from LDAP or federation, where Keystone derives\n  the ID. Copying IdP IDs into local users creates a parallel local identity\n  (local password, no directory lifecycle, no sync).\n- Alternatives (:266-269) dismisses only K2K. Plain OIDC/SAML or LDAP against\n  one corporate IdP is the obvious alternative and is not discussed.\n- \"Extra mapping table\" argument (:44-47) is weak. `(domain_id, name)` is unique\n  for local users and domain IDs can already be explicit, so tooling can resolve\n  a user by name per region. State why that is insufficient.\n- The real requirement is external systems that store `user_id` (Nova/Cinder/\n  Glance ownership, quotas, audit/CADF correlation, moving resources between\n  regions). The spec never says this.\n- `create_user` already accepts a `federated` object\n  (identity/core.py:1133-1149), so an admin can pre-provision a user linked to\n  `(idp, protocol, unique_id)`. Not mentioned; may cover part of the use case.\n- Ensuring IDs can be kept consistent between independent Keystones (regions)\n  does not address synchronization of the other objects attached or owned by the\n  user (application credentials, credentials, trusts). The spec reasoning does\n  not mention this, and without it the need for the change does not stand.\n\nFix: narrow scope to local users, state the concrete requirement and why\nname-based lookup fails, and add Alternatives entries for LDAP/OIDC and\nname-based reconciliation. When narrowing to local users mention how passwords\nare synchronized or state why it is not necessary.\n\n## 2. [HIGH] Proposed policy rule drops the domain-scope check\n\nCurrent default (common/policies/user.py:38-41):\n\n    (rule:admin_required) or\n    (role:manager and token.domain.id:%(target.user.domain_id)s)\n\nSpec rule (:510):\n`rule:admin_required or (role:manager and target.user.id:None)`. The\n`token.domain.id` clause is gone, so a manager could create users in any domain.\nKeep the domain match and add the \"no `id`\" condition. Also verify oslo.policy\nsemantics for a missing `target.user.id` (`None` string compare vs absent key).\n`rule:admin_required` is the deprecated scope-unaware form (user.py:59-64); say\nwhether the new rule builds on the scope-aware defaults. The same\n`ADMIN_OR_DOMAIN_MANAGER` string is shared by update/delete, so the change needs\na separate create-only rule.\n\n## 3. [HIGH] Explicit ID can corrupt `id_mapping` on non-default drivers\n\n`Manager.create_user` inserts into the driver first (identity/core.py:1174),\nthen `_set_domain_id_and_mapping` -\u003e `_insert_new_public_id` passes the explicit\nID as `public_id` for UUID-generating drivers (core.py:751-760).\n`create_id_mapping` swallows `DBDuplicateEntry` and returns\n`get_public_id(local_entity)` (mapping_backends/sql.py:85-88). If the explicit\nID collides with an existing `id_mapping.public_id` belonging to a different\nentity (for example an LDAP user\u0027s sha256), the lookup returns `None`: the user\nrow is already committed and the API returns `id: None`.\n\nThe uniqueness check (:700-739) only queries projects, users, domains. Add\n`id_mapping` to it, and make the create atomic (or roll back on mapping\nfailure). Add a test with a domain-specific SQL driver.\n\n## 4. [HIGH] Federation/LDAP collision analysis is wrong\n\nSection :388-425 is built on incorrect mechanics.\n\n- \"Collision before federation -\u003e impersonation\" is false.\n  `shadow_federated_user` looks up by\n  `federated_user (idp, protocol, unique_id)`, not by user ID\n  (identity/core.py:1766, :1718-1750). A pre-planted local user has no\n  `federated_user` row, so the code takes the `UserNotFound` branch,\n  `create_federated_user` inserts a `user` row with the same PK and fails with\n  409 (`handle_conflicts`). Outcome: login DoS for the victim, not\n  impersonation.\n- \"When `domain_specific_drivers_enabled \u003d true`\" is the wrong condition.\n  Mapping is used when the driver is not the default, or the default driver does\n  not generate UUIDs and `backward_compatible_ids` is off (core.py:848-863).\n  Federation does not use `id_mapping` at all (IDs are written directly to\n  `user`).\n- For mapped LDAP, a planted ID that is later mapped makes\n  `_get_domain_driver_and_entity_id` (core.py:933-945) route lookups to LDAP\n  first. Result is confusion/shadowing of the local user, not a path to the LDAP\n  user\u0027s identity (authentication routes to LDAP).\n- The stated mitigation (\"uuid mode prevents collision with 64-char sha256\") is\n  true but follows from the wrong threat model. Rewrite the section separately\n  for federated, LDAP-mapped, and `id_mapping` cases, and state the actual\n  impact (DoS / routing confusion).\n\n## 5. [HIGH] Validation and uniqueness are specified but never wired in\n\n`validate_user_id` (:675-698) and `validate_global_uniqueness` (:710-739) are\ndefined, but no work item calls them from `api/users.py` or\n`Manager.create_user`. Item 2 says the schema validates length so \"no additional\nruntime validation is needed\" (:604-606), contradicting the `id_validation`\nmodule. Numbering skips item 9. Also:\n\n- `get_user` in the uniqueness sketch can hit LDAP, does not see soft-deleted\n  rows, and does not see `id_mapping` (issue 3). The prose (:358-360) claims\n  soft-deleted rows are covered; the code does not.\n- Check-then-insert is a TOCTOU race. The DB PK is the real guard; the check\n  only gives a friendlier 409. Say so, and catch the PK conflict.\n- The `Manager.create_user` change (`if \u0027id\u0027 not in user`, :629-630) lets `id`\n  through for every caller, including a `PATCH`-style update body or internal\n  callers. Specify that only the API layer may pass it.\n\n## 6. [HIGH] Pattern modes contradict the rest of the spec\n\n- Default `^[a-zA-Z0-9-]*$` (:191) uses `*`, so the empty string matches (the\n  spec says length 1-64; the schema has `minLength: 1` but the validator does\n  not).\n- Default still allows 64 hex characters, so it does not prevent the sha256\n  collision the Security Impact section tells operators to avoid. The\n  vulnerability table (:329-352) says non-UUID IDs are unsafe and that only\n  `uuid` mode prevents it, yet `default` is the default.\n- `uuid` mode `^[0-9a-f]{32}$` rejects dashed UUIDs, but every IdP ID the spec\n  cites as the use case (objectGUID, Entra id, entryUUID, :124-134) is dashed.\n  The recommended secure mode cannot hold the headline IDs.\n- `predefined_user_id_custom_pattern` default `^[a-zA-Z0-9._@-]+$` (:489)\n  includes `.` and `@`, violating the spec\u0027s own \"must be a subset of Nova\" rule\n  (:223-225, :323-327) and the Nova dot vulnerability row.\n- Schema work item (:567-600) and tests (:767-779) say any string \u003c\u003d 64 chars is\n  accepted, including DN-style strings and dashed UUIDs \"stored as-is\". That\n  contradicts all three pattern modes.\n- CLI section (:840) references `explicit_user_id_pattern`; the option is\n  `predefined_user_id_pattern`.\n- Nova\u0027s real constraint should be cited from nova source, not asserted; the\n  table claims for Barbican/Horizon are likewise unverified.\n- Pre-compiled `PATTERN_DEFAULT.match` with `$` accepts a trailing newline; use\n  `fullmatch`. `custom` recompiles per request.\n\n## 7. [HIGH] Use case is only partially met: roles and groups\n\nCentral role-assignment management across regions (:42-44) also needs consistent\nrole (and group) IDs. Without it the user ID alone does not remove the mapping\ntable. Add roles/groups to scope or state the limitation.\n\n## 8. [MED] DR claim is false as written\n\n:57-60 says re-creating a user with the original UUID lets \"previously issued\ntokens reference the restored entity\". `delete_user` emits `Audit.deleted` and\nthe revoke listener persists a user revocation event (identity/core.py:1413;\nrevoke/core.py:86), so tokens issued before the delete stay revoked after\nre-creation. The use case also contradicts the spec\u0027s own statement that ID\nreuse after delete is the biggest risk (:374). Drop it or restate it without\ntokens.\n\n## 9. [MED] `keystone-manage` / bootstrap justification inaccurate\n\n:632-636 says the manager \"already receives a pre-set `id` from internal callers\nsuch as `keystone-manage bootstrap`\". `user_setup` (cmd/idutils.py:68-91)\nbypasses `Manager.create_user` and calls `_create_user_with_federated_objects`\ndirectly after setting `user[\u0027id\u0027] \u003d self.user_id`; bootstrap creates\ndomain/project/role with explicit IDs (cmd/bootstrap.py:83,107,129), not users.\nSo there is no precedent for `Manager.create_user` honoring a caller ID.\n\nThe `idutils.py:89` bug is real (`user[\u0027id\u0027] \u003d self.user_id` can be `None`,\n:815-846), but the fix should reuse the same validator as the API, not a bare\n`len \u003e 64` check (:842).\n\n## 10. [MED] Silent-ignore on older servers\n\n`\"additionalProperties\": true` on the user schema means old servers create a\nuser with a random ID and return 201 (:906-910). The spec offers only docs and\noptional version checks. Add a concrete client behavior: compare the returned\n`id` with the requested one and error, or use a discovery flag.\n\n## 11. [MED] Soft-delete phasing is scope creep inside this spec\n\n:931-990 specifies a three-phase soft-delete rollout (columns, partial indexes,\nMySQL generated columns, purge job, mandatory in Phase 3). There is a separate\nsoft-delete spec; this duplicates and may conflict with it (`local_user` columns\nhere, `user` table there; `soft_delete_users` option here). Reference the other\nspec, drop the DDL details, and make the ID-reuse protection explicitly a\ndependency, not a plan inside this one.\n\n## 12. [LOW] Test list stale / incomplete\n\n- Missing: federated-collision test (expect 409), `id_mapping` collision, PATCH\n  with `id`, rejection of `id` by the update schema, policy test for a manager\n  from another domain, `keystone-manage` validator parity.\n- `test_create_user_with_id_arbitrary_string` (`jsmith`) conflicts with\n  uuid-mode semantics unless tied to a configured mode.\n\n## 13. [LOW / nits]\n\n- Typos: \"adminstrator\", \"Doman Managers\", \"specifiy\", \"Updatd\".\n- \"Primary assignee: alee\" -- confirm; bug 1832848 history differs.\n- Performance Impact (:457) says \"one length check\"; the design adds pattern\n  checks and up to three DB lookups per create.\n- Upgrade Impact (:530) cites \"v3.15+\" without a source; check the current API\n  version.\n- Documentation Impact should cover the new `[identity]` options and sample\n  config; a release note is needed for the options, not only the feature.\n- Dependencies section (:923-929) says client changes are \"described below\" but\n  they are described above.\n\n## Verified-accurate claims (context for reviewers)\n\n- `Manager.create_user` sets `user[\u0027id\u0027] \u003d uuid.uuid4().hex` unconditionally\n  (identity/core.py:1173).\n- `user_setup` sets `user[\u0027id\u0027] \u003d self.user_id` with no fallback\n  (cmd/idutils.py:89).\n- Federated shadow user ID is sha256 of `(domain_id, unique_id, \u0027user\u0027)`\n  (shadow_backends/sql.py:33-45).\n- `id_mapping.public_id` is `String(64)` primary key with unique\n  `(domain_id, local_id, entity_type)` (mapping_backends/sql.py:24-40).\n- Current `identity:create_user` default is `ADMIN_OR_DOMAIN_MANAGER`\n  (common/policies/user.py:127-134).\n- Role IDs are assigned at the API layer (api/roles.py:89).\n- `delete_user` emits `Audit.deleted` (identity/core.py:1413), which the revoke\n  subsystem consumes.\n\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\nOverall I feel the reasoning is weak for me. So current philosophy of keystone is not to try to ensure IDs of remote systems are reused blindly and have reliable mappings instead. This spec changes this concept in the root. If I get it correctly you have a real user demand for this. I suggest you describe it precisely for reviewers to get the bigger scope, especially that this demand sounds like not well thought to the end (many independent keystones and need to have same user IDs across \"regions\") which leaves too many open questions on whether it really helps end-2-end.","commit_id":"f383d32989649b200088d9c7476bc50099b2a8c2"}],"specs/keystone/2026.2/explicit-user-ids.rst":[{"author":{"_account_id":34120,"name":"Andre Aranha","display_name":"afariasa","email":"afariasa@redhat.com","username":"afariasa"},"change_message_id":"9cfddae7b35477bfb39ee012875bcc03272f7ca6","unresolved":true,"context_lines":[{"line_number":86,"context_line":"* The field is consumed during creation and does not appear as a separate"},{"line_number":87,"context_line":"  attribute in the response body — the resulting entity\u0027s ``id`` field"},{"line_number":88,"context_line":"  contains the value, as with auto-generated IDs."},{"line_number":89,"context_line":"* For users: ``id`` accepts alphanumeric characters and hyphens up to 64"},{"line_number":90,"context_line":"  characters (``^[a-zA-Z0-9-]*$``), matching Nova\u0027s user_id validation to"},{"line_number":91,"context_line":"  ensure compatibility across OpenStack services. Operators can optionally"},{"line_number":92,"context_line":"  configure ``[identity] predefined_user_id_pattern \u003d uuid`` for stricter"}],"source_content_type":"text/x-rst","patch_set":2,"id":"e073a595_546ffadb","line":89,"updated":"2026-09-24 12:06:04.000000000","message":"On the project/domain explicit ID spec we have the `id` being differente: https://review.opendev.org/c/openstack/keystone-specs/+/997320/4/specs/keystone/2026.2/explicit-project-domain-ids.rst\n\"For both projects and domains: ``id`` accepts a UUID in dashless hex form\n  only (``xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx``, 32 lowercase hexadecimal\n  characters), as required by Fernet token.\"\nWe should decide which id format to follow in order to have the id field consistent on keystone, as it seems we should follow the project/domain since they are more restrict on this matter.","commit_id":"f383d32989649b200088d9c7476bc50099b2a8c2"},{"author":{"_account_id":34120,"name":"Andre Aranha","display_name":"afariasa","email":"afariasa@redhat.com","username":"afariasa"},"change_message_id":"a68f5f333710b0efe61acc2b1a67c6040b5159b6","unresolved":false,"context_lines":[{"line_number":86,"context_line":"* The field is consumed during creation and does not appear as a separate"},{"line_number":87,"context_line":"  attribute in the response body — the resulting entity\u0027s ``id`` field"},{"line_number":88,"context_line":"  contains the value, as with auto-generated IDs."},{"line_number":89,"context_line":"* For users: ``id`` accepts alphanumeric characters and hyphens up to 64"},{"line_number":90,"context_line":"  characters (``^[a-zA-Z0-9-]*$``), matching Nova\u0027s user_id validation to"},{"line_number":91,"context_line":"  ensure compatibility across OpenStack services. Operators can optionally"},{"line_number":92,"context_line":"  configure ``[identity] predefined_user_id_pattern \u003d uuid`` for stricter"}],"source_content_type":"text/x-rst","patch_set":2,"id":"829e19c1_ae343460","line":89,"in_reply_to":"e073a595_546ffadb","updated":"2026-09-30 10:29:25.000000000","message":"Done","commit_id":"f383d32989649b200088d9c7476bc50099b2a8c2"}]}
