Skip to content

feat(domain-config): Dispatch identity ops per domain - #1202

Merged
gtema merged 1 commit into
mainfrom
feat/domain-config-identity-dispatch
Sep 4, 2026
Merged

gtema merged 1 commit into
mainfrom
feat/domain-config-identity-dispatch

Conversation

@gtema

@gtema gtema commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What

Phase 5 of #954 (issue #960): wire the domain configuration resolution
layer into identity backend selection. Until now DomainConfigResolver was
constructed on the provider but nothing consulted it — IdentityService held a
single global backend_driver chosen once from [identity] driver.

Changes

  • IdentityService carries the full registered-backend map and an optional
    DomainConfigResolver (Some only when domain_specific_drivers_enabled).
    Every backend call site (~50 — user/group CRUD, auth, list, password,
    membership) now routes through driver_for / driver_for_user /
    driver_for_group, which resolve a domain's stored identity/driver and
    dispatch to the named backend. Fallback to the global driver when per-domain
    drivers are off, the domain is unknown, no identity/driver is stored, or
    resolution errors.
  • Per-domain resolution is cached with no invalidation — a driver change
    through the config API takes effect on restart (follow-up issue to be filed).
  • IdentityService::from_driver keeps a None resolver, so the existing
    single-mock unit tests are unchanged.
  • LDAP identity backend registers whenever per-domain drivers are enabled,
    not only when it is the global driver. Writes to an LDAP-backed domain surface
    the existing read-only error.
  • SQL identity-driver registration lock (deferred from Phase 4: Domain config API handlers and policy #959):
    DomainConfigService claims the SQL registration before storing an
    identity/driver = sql configuration and releases it when the driver changes
    away or the config is deleted, while domain_configurations_from_database is
    on. A second domain claiming it gets HTTP 409.

Out of scope

  • Resolved-driver cache invalidation on config API writes (follow-up).
  • keystone-manage domain_config_upload / mapping_purge CLI (follow-up).

Tests

  • identity/service.rs: per-domain dispatch — resolver absent → global driver
    regression; stored identity/driver selects the named backend; empty config →
    global; resolution cached per domain; resolver error → global fallback.
  • domain_config/service.rs: registration lock — skipped without database
    configs; sql claims a free registration then writes; held by another domain
    → 409, no write; held by self → write proceeds; switch to non-sql releases
    after the write; delete releases.
  • Full suites green: openstack-keystone-core (757), openstack-keystone
    (991), openstack-keystone-core-types domain_config (79), ldap crate;
    cargo build -p openstack-keystone; opa test policy/domain_config 15/15;
    cargo fmt --all -- --check.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦢 Load Test Results

Goose Attack Report

Plan Overview

Action Started Stopped Elapsed Users
Increasing 26-09-04 11:48:09 26-09-04 11:48:31 00:00:22 0 → 45
Maintaining 26-09-04 11:48:31 26-09-04 11:49:01 00:00:30 45
Decreasing 26-09-04 11:49:01 26-09-04 11:49:02 00:00:01 0 ← 45

Request Metrics

Method Name # Requests # Fails Average (ms) Min (ms) Max (ms) RPS Failures/s
DELETE DELETE /v3/auth/tokens 48 0 491.69 86 666 1.60 0.00
DELETE DELETE /v3/projects/:id (teardown) 1 0 142.00 142 142 0.03 0.00
DELETE DELETE /v3/roles/:id (teardown) 1 0 56.00 56 56 0.03 0.00
DELETE DELETE /v3/users/:id (teardown) 2 0 138.50 66 211 0.07 0.00
DELETE DELETE /v4/mappings/rulesets/:mapping_id (teardown) 1 0 333.00 333 333 0.03 0.00
DELETE DELETE /v4/oauth2/:domain_id/clients/:provider_id (teardown) 1 0 183.00 183 183 0.03 0.00
GET 651 0 540.74 316 817 21.70 0.00
GET GET /SCIM/v2/:domain_id/Users (sync reconcile list) 1958 1958 15.03 1 920 65.27 65.27
GET GET /v3/auth/tokens (validate new) 48 0 514.83 402 650 1.60 0.00
GET GET /v3/auth/tokens (validate revoked) 48 0 446.94 28 640 1.60 0.00
GET GET /v3/projects/:id 61 0 498.08 370 682 2.03 0.00
GET GET /v3/projects/:id (catalog) 89 0 521.53 298 712 2.97 0.00
GET GET /v3/role_assignments 46 0 510.04 378 666 1.53 0.00
GET GET /v3/role_assignments?role.id 45 0 521.11 380 668 1.50 0.00
GET GET /v3/role_assignments?scope.domain.id 45 0 473.16 339 617 1.50 0.00
GET GET /v3/role_assignments?user.id 46 0 511.43 342 720 1.53 0.00
GET GET /v3/roles 119 0 509.53 338 734 3.97 0.00
GET GET /v3/roles/:id 60 0 509.13 329 673 2.00 0.00
GET GET /v3/users/:id 113 0 542.56 392 737 3.77 0.00
GET GET /v3/users/:id (catalog) 111 0 534.71 366 752 3.70 0.00
GET GET /v4/api-keys 30 0 504.13 340 687 1.00 0.00
GET GET /v4/api-keys/:client_id 29 0 538.90 354 700 0.97 0.00
GET GET /v4/mappings/rulesets 29 0 527.07 356 708 0.97 0.00
GET GET /v4/mappings/rulesets/:mapping_id 30 0 495.60 394 666 1.00 0.00
GET GET /v4/oauth2/:domain_id/.well-known/openid-configuration 896 0 50.21 9 169 29.87 0.00
GET GET /v4/oauth2/:domain_id/clients 30 0 520.33 349 686 1.00 0.00
GET GET /v4/oauth2/:domain_id/clients/:provider_id 30 0 495.67 334 628 1.00 0.00
GET GET /v4/oauth2/:domain_id/jwks 895 0 49.57 8 219 29.83 0.00
GET GET /v4/scim_realms 19 0 498.21 312 694 0.63 0.00
GET GET /v4/scim_realms/:domain_id/:provider_id 20 0 489.65 369 648 0.67 0.00
POST POST /v3/auth/tokens 47 0 471.77 312 601 1.57 0.00
POST POST /v3/auth/tokens (password, invalid) 32 0 979.91 528 1387 1.07 0.00
POST POST /v3/auth/tokens (password, project-scoped) 53 0 1273.51 807 1886 1.77 0.00
POST POST /v3/auth/tokens (password, unscoped) 50 0 1104.44 603 1999 1.67 0.00
POST POST /v3/auth/tokens (rescope to system) 106 0 575.97 396 818 3.53 0.00
POST POST /v3/auth/tokens (rescope, invalid project) 65 0 469.65 332 689 2.17 0.00
POST POST /v3/auth/tokens (system-scoped, bootstrap admin) 50 0 1238.64 642 1773 1.67 0.00
POST POST /v4/api-keys/:client_id/revoke (teardown) 1 0 171.00 171 171 0.03 0.00
PUT PUT /v4/scim_realms/:domain_id/:provider_id 19 0 580.00 433 730 0.63 0.00
Aggregated 5925 1958 231.82 1 1999 197.50 65.27

Response Time Metrics

Method Name 50%ile (ms) 60%ile (ms) 70%ile (ms) 80%ile (ms) 90%ile (ms) 95%ile (ms) 99%ile (ms) 100%ile (ms)
DELETE DELETE /v3/auth/tokens 500 500 500 600 600 600 666 666
DELETE DELETE /v3/projects/:id (teardown) 142 142 142 142 142 142 142 142
DELETE DELETE /v3/roles/:id (teardown) 56 56 56 56 56 56 56 56
DELETE DELETE /v3/users/:id (teardown) 66 66 66 210 210 210 210 210
DELETE DELETE /v4/mappings/rulesets/:mapping_id (teardown) 333 333 333 333 333 333 333 333
DELETE DELETE /v4/oauth2/:domain_id/clients/:provider_id (teardown) 183 183 183 183 183 183 183 183
GET 500 600 600 600 600 700 700 800
GET GET /SCIM/v2/:domain_id/Users (sync reconcile list) 6 7 9 11 15 19 430 900
GET GET /v3/auth/tokens (validate new) 500 500 600 600 600 600 650 650
GET GET /v3/auth/tokens (validate revoked) 470 480 500 500 600 600 600 600
GET GET /v3/projects/:id 490 500 500 500 600 600 600 682
GET GET /v3/projects/:id (catalog) 500 500 600 600 600 700 700 700
GET GET /v3/role_assignments 500 500 600 600 600 600 666 666
GET GET /v3/role_assignments?role.id 500 500 600 600 600 600 668 668
GET GET /v3/role_assignments?scope.domain.id 480 490 500 500 600 600 600 600
GET GET /v3/role_assignments?user.id 490 500 500 600 600 700 700 700
GET GET /v3/roles 500 500 600 600 600 600 700 700
GET GET /v3/roles/:id 500 500 500 600 600 600 600 673
GET GET /v3/users/:id 500 600 600 600 700 700 700 700
GET GET /v3/users/:id (catalog) 500 600 600 600 600 700 700 752
GET GET /v4/api-keys 500 500 500 500 600 600 687 687
GET GET /v4/api-keys/:client_id 500 600 600 600 600 600 700 700
GET GET /v4/mappings/rulesets 500 500 600 600 600 700 700 700
GET GET /v4/mappings/rulesets/:mapping_id 470 480 500 600 600 600 666 666
GET GET /v4/oauth2/:domain_id/.well-known/openid-configuration 43 50 59 70 87 110 150 169
GET GET /v4/oauth2/:domain_id/clients 500 500 500 600 600 600 686 686
GET GET /v4/oauth2/:domain_id/clients/:provider_id 490 500 500 600 600 600 600 600
GET GET /v4/oauth2/:domain_id/jwks 43 50 60 70 85 100 140 219
GET GET /v4/scim_realms 500 500 500 500 600 600 694 694
GET GET /v4/scim_realms/:domain_id/:provider_id 470 490 500 500 600 600 600 600
POST POST /v3/auth/tokens 470 490 500 500 600 600 600 600
POST POST /v3/auth/tokens (password, invalid) 900 1,000 1,000 1,000 1,000 1,000 1,000 1,000
POST POST /v3/auth/tokens (password, project-scoped) 1,000 1,000 1,000 1,886 1,886 1,886 1,886 1,886
POST POST /v3/auth/tokens (password, unscoped) 1,000 1,000 1,000 1,000 1,000 1,999 1,999 1,999
POST POST /v3/auth/tokens (rescope to system) 600 600 600 600 700 700 800 800
POST POST /v3/auth/tokens (rescope, invalid project) 460 470 500 500 600 600 600 689
POST POST /v3/auth/tokens (system-scoped, bootstrap admin) 1,000 1,000 1,000 1,000 1,773 1,773 1,773 1,773
POST POST /v4/api-keys/:client_id/revoke (teardown) 171 171 171 171 171 171 171 171
PUT PUT /v4/scim_realms/:domain_id/:provider_id 600 600 600 600 700 700 700 700
Aggregated 48 87 450 500 600 700 1,000 1,999

Status Code Metrics

Method Name Status Codes
DELETE DELETE /v3/auth/tokens 48 [204]
DELETE DELETE /v3/projects/:id (teardown) 1 [204]
DELETE DELETE /v3/roles/:id (teardown) 1 [204]
DELETE DELETE /v3/users/:id (teardown) 2 [204]
DELETE DELETE /v4/mappings/rulesets/:mapping_id (teardown) 1 [204]
DELETE DELETE /v4/oauth2/:domain_id/clients/:provider_id (teardown) 1 [204]
GET 651 [200]
GET GET /SCIM/v2/:domain_id/Users (sync reconcile list) 1,927 [429], 31 [401]
GET GET /v3/auth/tokens (validate new) 48 [200]
GET GET /v3/auth/tokens (validate revoked) 48 [404]
GET GET /v3/projects/:id 61 [200]
GET GET /v3/projects/:id (catalog) 89 [200]
GET GET /v3/role_assignments 46 [200]
GET GET /v3/role_assignments?role.id 45 [200]
GET GET /v3/role_assignments?scope.domain.id 45 [200]
GET GET /v3/role_assignments?user.id 46 [200]
GET GET /v3/roles 119 [200]
GET GET /v3/roles/:id 60 [200]
GET GET /v3/users/:id 113 [200]
GET GET /v3/users/:id (catalog) 111 [200]
GET GET /v4/api-keys 30 [200]
GET GET /v4/api-keys/:client_id 29 [200]
GET GET /v4/mappings/rulesets 29 [200]
GET GET /v4/mappings/rulesets/:mapping_id 30 [200]
GET GET /v4/oauth2/:domain_id/.well-known/openid-configuration 896 [200]
GET GET /v4/oauth2/:domain_id/clients 30 [200]
GET GET /v4/oauth2/:domain_id/clients/:provider_id 30 [200]
GET GET /v4/oauth2/:domain_id/jwks 895 [200]
GET GET /v4/scim_realms 19 [200]
GET GET /v4/scim_realms/:domain_id/:provider_id 20 [200]
POST POST /v3/auth/tokens 47 [201]
POST POST /v3/auth/tokens (password, invalid) 32 [401]
POST POST /v3/auth/tokens (password, project-scoped) 53 [201]
POST POST /v3/auth/tokens (password, unscoped) 50 [201]
POST POST /v3/auth/tokens (rescope to system) 106 [201]
POST POST /v3/auth/tokens (rescope, invalid project) 65 [401]
POST POST /v3/auth/tokens (system-scoped, bootstrap admin) 50 [201]
POST POST /v4/api-keys/:client_id/revoke (teardown) 1 [200]
PUT PUT /v4/scim_realms/:domain_id/:provider_id 19 [200]
Aggregated 128 [401], 3,462 [200], 306 [201], 48 [404], 1,927 [429], 54 [204]

Transaction Metrics

Transaction # Times Run # Fails Average (ms) Min (ms) Max (ms) RPS Failures/s
ReadHeavy
0.0 0 0 0.00 0 0 0.00 0.00
0.1 117 0 537.21 322 667 3.90 0.00
0.2 115 0 527.40 354 697 3.83 0.00
0.3 118 0 512.08 380 752 3.93 0.00
TokenLifecycle
1.0 0 0 0.00 0 0 0.00 0.00
1.1 48 0 1927.88 988 2255 1.60 0.00
ValidateToken
2.0 0 0 0.00 0 0 0.00 0.00
2.1 100 0 609.10 376 817 3.33 0.00
UserCRUD
3.0 0 0 0.00 0 0 0.00 0.00
3.1 0 0 0.00 0 0 0.00 0.00
3.2 113 0 542.57 392 737 3.77 0.00
3.3 2 0 138.50 66 211 0.07 0.00
ProjectCRUD
4.0 0 0 0.00 0 0 0.00 0.00
4.1 0 0 0.00 0 0 0.00 0.00
4.2 61 0 498.08 370 682 2.03 0.00
4.3 1 0 142.00 142 142 0.03 0.00
RoleCRUD
5.0 0 0 0.00 0 0 0.00 0.00
5.1 0 0 0.00 0 0 0.00 0.00
5.2 60 0 509.15 329 673 2.00 0.00
5.3 1 0 56.00 56 56 0.03 0.00
RoleList
6.0 0 0 0.00 0 0 0.00 0.00
6.1 119 0 509.59 338 734 3.97 0.00
RoleAssignmentList
7.0 0 0 0.00 0 0 0.00 0.00
7.1 46 0 510.07 378 666 1.53 0.00
7.2 46 0 511.46 342 720 1.53 0.00
7.3 45 0 473.22 339 617 1.50 0.00
7.4 45 0 521.13 380 668 1.50 0.00
UserRead
8.0 0 0 0.00 0 0 0.00 0.00
8.1 112 0 554.17 399 744 3.73 0.00
8.2 111 0 534.72 366 752 3.70 0.00
ProjectRead
9.0 0 0 0.00 0 0 0.00 0.00
9.1 89 0 507.21 316 693 2.97 0.00
9.2 89 0 521.57 298 712 2.97 0.00
PasswordAuth
10.0 50 0 1104.44 603 1999 1.67 0.00
10.1 53 0 1273.51 807 1886 1.77 0.00
PasswordAuthNegative
11.0 32 0 979.97 528 1387 1.07 0.00
Rescope
12.0 0 0 0.00 0 0 0.00 0.00
12.1 106 0 576.03 396 818 3.53 0.00
RescopeNegative
13.0 0 0 0.00 0 0 0.00 0.00
13.1 65 0 469.68 332 689 2.17 0.00
SystemScopeAuth
14.0 50 0 1238.70 642 1773 1.67 0.00
ApiKeyCRUD
15.0 0 0 0.00 0 0 0.00 0.00
15.1 0 0 0.00 0 0 0.00 0.00
15.2 29 0 538.97 354 700 0.97 0.00
15.3 30 0 504.17 340 687 1.00 0.00
15.4 1 0 171.00 171 171 0.03 0.00
MappingCRUD
16.0 0 0 0.00 0 0 0.00 0.00
16.1 0 0 0.00 0 0 0.00 0.00
16.2 30 0 495.60 394 666 1.00 0.00
16.3 29 0 527.10 356 708 0.97 0.00
16.4 1 0 333.00 333 333 0.03 0.00
OAuth2ClientCRUD
17.0 0 0 0.00 0 0 0.00 0.00
17.1 0 0 0.00 0 0 0.00 0.00
17.2 30 0 495.67 334 628 1.00 0.00
17.3 30 0 520.37 349 686 1.00 0.00
17.4 1 0 183.00 183 183 0.03 0.00
OAuth2Discovery
18.0 895 0 49.60 8 219 29.83 0.00
18.1 896 0 50.23 9 169 29.87 0.00
ScimRealmRead
19.0 0 0 0.00 0 0 0.00 0.00
19.1 20 0 489.70 369 648 0.67 0.00
19.2 19 0 498.21 312 694 0.63 0.00
19.3 19 0 580.11 433 730 0.63 0.00
ScimSync
20.0 0 0 0.00 0 0 0.00 0.00
20.1 0 0 0.00 0 0 0.00 0.00
20.2 0 0 0.00 0 0 0.00 0.00
20.3 1958 0 15.05 1 920 65.27 0.00
20.4 1957 0 0.00 0 0 65.23 0.00
20.5 1957 0 0.00 0 0 65.23 0.00
20.6 1 0 0.00 0 0 0.03 0.00
Aggregated 9697 0 141.64 1 2255 323.23 0.00

Scenario Metrics

Transaction # Users # Times Run Average (ms) Min (ms) Max (ms) Scenarios/s Iterations
ReadHeavy 6 115 1586.90 1314 1872 3.83 19.17
TokenLifecycle 3 45 1972.31 1588 2255 1.50 15.00
ValidateToken 2 98 611.68 401 817 3.27 49.00
UserCRUD 2 111 543.68 392 737 3.70 55.50
ProjectCRUD 1 60 498.33 370 682 2.00 60.00
RoleCRUD 1 59 512.20 382 673 1.97 59.00
RoleList 2 117 509.66 338 734 3.90 58.50
RoleAssignmentList 3 45 2024.00 1758 2295 1.50 15.00
UserRead 4 110 1093.00 884 1333 3.67 27.50
ProjectRead 3 88 1033.34 760 1241 2.93 29.33
PasswordAuth 4 49 2374.59 1672 3309 1.63 12.25
PasswordAuthNegative 1 31 966.84 528 1335 1.03 31.00
Rescope 2 104 576.40 396 818 3.47 52.00
RescopeNegative 1 64 468.83 332 689 2.13 64.00
SystemScopeAuth 2 48 1244.83 786 1773 1.60 24.00
ApiKeyCRUD 1 29 1050.28 918 1253 0.97 29.00
MappingCRUD 1 29 1023.93 838 1237 0.97 29.00
OAuth2ClientCRUD 1 29 1020.59 860 1187 0.97 29.00
OAuth2Discovery 3 893 100.25 20 304 29.77 297.67
ScimRealmRead 1 19 1569.95 1325 1809 0.63 19.00
ScimSync 1 1957 14.83 1 920 65.23 1957.00
Aggregated 45 4100 328.70 1 3309 136.67 2931.92

Error Metrics

Method Name # Error
GET GET /SCIM/v2/:domain_id/Users (sync reconcile list) 49 401 Unauthorized: GET /SCIM/v2/:domain_id/Users (sync reconcile list)
POST POST /SCIM/v2/:domain_id/Users (sync provision) 1 401 Unauthorized: POST /SCIM/v2/:domain_id/Users (sync provision)
POST POST /v3/auth/tokens (password, invalid) 74 401 Unauthorized: POST /v3/auth/tokens (password, invalid)
POST POST /v3/auth/tokens (rescope, invalid project) 164 401 Unauthorized: POST /v3/auth/tokens (rescope, invalid project)
GET GET /v3/auth/tokens (validate revoked) 164 404 Not Found: GET /v3/auth/tokens (validate revoked)
GET GET /SCIM/v2/:domain_id/Users (sync reconcile list) 2042 429 Too Many Requests: GET /SCIM/v2/:domain_id/Users (sync reconcile list)

View full report

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

🧪 Tempest Identity Compatibility Results (advisory, non-blocking)

rust

Totals
======
Ran: 133 tests in 33.0046 sec.
 - Passed: 66
 - Skipped: 4
 - Expected Fail: 0
 - Unexpected Success: 0
 - Failed: 63
Sum of execute time for each test: 23.3924 sec.
Failed test IDs
===== FAILED TESTS (target=rust) =====
tempest.api.identity.admin.v3.test_credentials.CredentialsTestJSON.test_credentials_create_get_update_delete[id-7cd59bf9-bda4-4c72-9467-d21cab278355,smoke]
tempest.api.identity.admin.v3.test_application_credentials.ApplicationCredentialsV3AdminTest.test_create_application_credential_with_roles[id-3b3dd48f-3388-406a-a9e6-4d078a552d0e]
tempest.api.identity.admin.v3.test_default_project_id.TestDefaultProjectId.test_default_project_id[id-d6110661-6a71-49a7-a453-b5e26640ff6d]
tempest.api.identity.admin.v3.test_groups.GroupsV3TestJSON.test_group_users_add_list_delete[id-1598521a-2f36-4606-8df9-30772bd51339,smoke]
tempest.api.identity.admin.v3.test_groups.GroupsV3TestJSON.test_list_user_groups[id-64573281-d26a-4a52-b899-503cb0f4e4ec]
tempest.api.identity.admin.v3.test_domain_configuration.DomainConfigurationTestJSON.test_create_domain_config_and_show_config_groups_and_options[id-9e3ff13c-f597-4f01-9377-d6c06c2a1477]
tempest.api.identity.admin.v3.test_domain_configuration.DomainConfigurationTestJSON.test_create_update_and_delete_domain_config_groups_and_opts[id-c7510fa2-6661-4170-9c6b-4783a80651e9]
tempest.api.identity.admin.v3.test_domain_configuration.DomainConfigurationTestJSON.test_show_default_group_config_and_options[id-11a02bf0-6f94-4380-b3b0-c8dc18fc0d22]
tempest.api.identity.admin.v3.test_domains.DomainsTestJSON.test_create_domain_without_description[id-2abf8764-309a-4fa9-bc58-201b799817ad]
tempest.api.identity.admin.v3.test_domains.DomainsTestJSON.test_create_update_delete_domain[id-f2f5b44a-82e8-4dad-8084-0661ea3b18cf,smoke]
tempest.api.identity.admin.v3.test_domains_negative.DomainsNegativeTestJSON.test_create_domain_with_name_length_over_64[id-37b1bbf2-d664-4785-9a11-333438586eae,negative]
tempest.api.identity.admin.v3.test_domains_negative.DomainsNegativeTestJSON.test_delete_active_domain[gate,id-1f3fbff5-4e44-400d-9ca1-d953f05f609b,negative]
tempest.api.identity.admin.v3.test_domains.DomainsTestJSON.test_domain_delete_cascades_content[id-d8d318b7-d1b3-4c37-94c5-3c5ba0b121ea]
tempest.api.identity.admin.v3.test_domains.DomainsTestJSON.test_list_domains_filter_by_enabled[id-3fd19840-65c1-43f8-b48c-51bdd066dff9]
tempest.api.identity.admin.v3.test_list_projects.ListProjectsStaticTestJSON.test_list_projects[id-1d830662-22ad-427c-8c3e-4ec854b0af44]
setUpClass (tempest.api.identity.admin.v3.test_endpoint_groups.EndPointGroupsTest)
tempest.api.identity.admin.v3.test_inherits.InheritsV3TestJSON.test_inherit_assign_check_revoke_roles_on_projects_group[id-26021436-d5a4-4256-943c-ded01e0d4b45]
tempest.api.identity.admin.v3.test_inherits.InheritsV3TestJSON.test_inherit_assign_check_revoke_roles_on_projects_user[id-18b70e45-7687-4b72-8277-b8f1a47d7591]
tempest.api.identity.admin.v3.test_inherits.InheritsV3TestJSON.test_inherit_assign_list_check_revoke_roles_on_domains_group[id-c7a8dda2-be50-4fb4-9a9c-e830771078b1]
tempest.api.identity.admin.v3.test_list_projects.ListProjectsTestJSON.test_list_projects_with_enabled[id-0fe7a334-675a-4509-b00e-1c4b95d5dae8]
tempest.api.identity.admin.v3.test_list_projects.ListProjectsTestJSON.test_list_projects_with_parent[id-6edc66f5-2941-4a17-9526-4073311c1fac]
tempest.api.identity.admin.v3.test_inherits.InheritsV3TestJSON.test_inherit_assign_list_check_revoke_roles_on_domains_user[id-4e6f0366-97c8-423c-b2be-41eae6ac91c8]
tempest.api.identity.admin.v3.test_inherits.InheritsV3TestJSON.test_inherit_assign_list_revoke_user_roles_on_domain[id-3acf666e-5354-42ac-8e17-8b68893bcd36]
tempest.api.identity.admin.v3.test_inherits.InheritsV3TestJSON.test_inherit_assign_list_revoke_user_roles_on_project_tree[id-9f02ccd9-9b57-46b4-8f77-dd5a736f3a06]
tempest.api.identity.admin.v3.test_projects.ProjectsTestJSON.test_create_is_domain_project[id-a7eb9416-6f9b-4dbb-b71b-7f73aaef59d5]
tempest.api.identity.admin.v3.test_projects.ProjectsTestJSON.test_project_create_with_parent[id-1854f9c0-70bc-4d11-a08a-1c789d339e3d]
tempest.api.identity.admin.v3.test_projects.ProjectsTestJSON.test_project_get_equals_list[id-d1db68b6-aebe-4fa0-b79d-d724d2e21162]
tempest.api.identity.admin.v3.test_project_tags.IdentityV3ProjectTagsTest.test_list_update_delete_project_tags[id-7c123aac-999d-416a-a0fb-84b915ab10de]
tempest.api.identity.admin.v3.test_projects_negative.ProjectsNegativeStaticTestJSON.test_create_projects_name_length_over_64[id-502b6ceb-b0c8-4422-bf53-f08fdb21e2f0,negative]
tempest.api.identity.admin.v3.test_list_users.UsersV3TestJSON.test_list_users_with_not_enabled[id-bff8bf2f-9408-4ef5-b63a-753c8c2124eb]
tempest.api.identity.admin.v3.test_tokens.TokensV3TestJSON.test_get_available_domain_scopes[id-ec5ecb05-af64-4c04-ac86-4d9f6f12f185]
tempest.api.identity.admin.v3.test_tokens.TokensV3TestJSON.test_rescope_token[id-565fa210-1da1-4563-999b-f7b5b67cf112]
tempest.api.identity.admin.v3.test_oauth_consumers.OAUTHConsumersV3Test.test_create_and_show_consumer[id-c8307ea6-a86c-47fd-ae7b-5b3b2caca76d]
tempest.api.identity.admin.v3.test_oauth_consumers.OAUTHConsumersV3Test.test_delete_consumer[id-fdfa1b7f-2a31-4354-b2c7-f6ae20554f93]
tempest.api.identity.admin.v3.test_oauth_consumers.OAUTHConsumersV3Test.test_list_consumers[id-09ca50de-78f2-4ffb-ac71-f2254036b2b8]
tempest.api.identity.admin.v3.test_oauth_consumers.OAUTHConsumersV3Test.test_update_consumer[id-080a9b1a-c009-47c0-9979-5305bf72e3dc]
tempest.api.identity.v3.test_application_credentials.ApplicationCredentialsV3Test.test_create_application_credential[id-8080c75c-eddc-4786-941a-c2da7039ae61]
tempest.api.identity.v3.test_application_credentials.ApplicationCredentialsV3Test.test_create_application_credential_expires[id-852daf0c-42b5-4239-8466-d193d0543ed3]
tempest.api.identity.v3.test_application_credentials.ApplicationCredentialsV3Test.test_list_application_credentials[id-ff0cd457-6224-46e7-b79e-0ada4964a8a6]
tempest.api.identity.v3.test_application_credentials.ApplicationCredentialsV3Test.test_query_application_credentials[id-9bb5e5cc-5250-493a-8869-8b665f6aa5f6]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_assignments_for_domain_roles[id-3859df7e-5b78-4e4d-b10e-214c8953842a]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_assignments_for_implied_roles_create_delete[id-c8828027-df48-4021-95df-b65b92c7429e]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_grant_list_revoke_role_to_group_on_domain[id-4bf8a70b-e785-413a-ad53-9f91ce02faa7]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_grant_list_revoke_role_to_group_on_project[id-cbf11737-1904-4690-9613-97bcbb3df1c4]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_grant_list_revoke_role_to_group_on_system[id-c888fe4f-8018-48db-b959-542225c1b4b6]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_grant_list_revoke_role_to_user_on_domain[id-6c9a2940-3625-43a3-ac02-5dcec62ef3bd]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_implied_domain_roles[id-eb1e1c24-1bc4-4d47-9748-e127a1852c82]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_implied_roles_create_check_show_delete[id-c90c316c-d706-4728-bcba-eb1912081b69]
tempest.api.identity.admin.v3.test_users.UsersV3TestJSON.test_list_user_projects[id-a831e70c-e35b-430b-92ed-81ebbc5437b8]
tempest.api.identity.admin.v3.test_roles.RolesV3TestJSON.test_role_create_update_show_list[id-18afc6c0-46cf-4911-824e-9989cc056c3a,smoke]
tempest.api.identity.admin.v3.test_trusts.TrustsV3TestJSON.test_get_trusts_all[id-4773ebd5-ecbf-4255-b8d8-b63e6f72b65d,smoke]
tempest.api.identity.admin.v3.test_users_negative.UsersNegativeTest.test_create_user_for_non_existent_domain[id-e75f006c-89cc-477b-874d-588e4eab4b17,negative]
tempest.api.identity.admin.v3.test_trusts.TrustsV3TestJSON.test_get_trusts_query[id-6268b345-87ca-47c0-9ce3-37792b43403a]
tempest.api.identity.v3.test_catalog.IdentityCatalogTest.test_catalog_standardization[id-56b57ced-22b8-4127-9b8a-565dfb0207e2]
tempest.api.identity.admin.v3.test_trusts.TrustsV3TestJSON.test_trust_expire[id-0ed14b66-cefd-4b5c-a964-65759453e292]
tempest.api.identity.admin.v3.test_trusts.TrustsV3TestJSON.test_trust_impersonate[id-5a0a91a4-baef-4a14-baba-59bf4d7fcace]
tempest.api.identity.admin.v3.test_trusts.TrustsV3TestJSON.test_trust_noimpersonate[id-ed2a8779-a7ac-49dc-afd7-30f32f936ed2]
tempest.api.identity.v3.test_tokens.TokensV3Test.test_validate_token[id-a9512ac3-3909-48a4-b395-11f438e16260]
tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_api_media_types[id-657c1970-4722-4189-8831-7325f3bc4265,smoke]
tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_api_version_resources[id-b9232f5e-d9e5-4d97-b96c-28d3db4de1bd,smoke]
tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_identity_v3_existence[id-79aec9ae-710f-4c54-a4fc-3aa25b4feac3]
tempest.api.identity.v3.test_api_discovery.TestApiDiscovery.test_list_api_versions[id-721f480f-35b6-46c7-846e-047e6acea0dc,smoke]
tempest.api.identity.v3.test_projects.IdentityV3ProjectsTest.test_list_projects_returns_only_authorized_projects[id-86128d46-e170-4644-866a-cc487f699e1d]
===== END FAILED TESTS (target=rust) =====

View full run

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Projectkeystone
Branchfeat/domain-config-identity-dispatch
Testbedubuntu-latest

🚨 1 Alert

BenchmarkMeasure
Units
ViewBenchmark Result
(Result Δ%)
Upper Boundary
(Limit %)
get_keyspaceLatency
nanoseconds (ns)
📈 plot
🚷 threshold
🚨 alert (🔔)
14.13 ns
(+139.50%)Baseline: 5.90 ns
13.79 ns
(102.44%)

Click to view all benchmark results
BenchmarkLatencyBenchmark Result
nanoseconds (ns)
(Result Δ%)
Upper Boundary
nanoseconds (ns)
(Limit %)
Command_Serde/apply/remove📈 view plot
🚷 view threshold
177,180.00 ns
(-34.78%)Baseline: 271,649.41 ns
1,352,235.71 ns
(13.10%)
Command_Serde/apply/set📈 view plot
🚷 view threshold
181,780.00 ns
(-58.77%)Baseline: 440,879.09 ns
3,265,227.85 ns
(5.57%)
Command_Serde/pack/delete📈 view plot
🚷 view threshold
108.75 ns
(-7.79%)Baseline: 117.93 ns
155.56 ns
(69.91%)
Command_Serde/pack/delete_index📈 view plot
🚷 view threshold
95.79 ns
(-8.98%)Baseline: 105.24 ns
138.79 ns
(69.02%)
Command_Serde/pack/set📈 view plot
🚷 view threshold
194.84 ns
(+2.88%)Baseline: 189.39 ns
244.34 ns
(79.74%)
Command_Serde/pack/set_index📈 view plot
🚷 view threshold
96.64 ns
(-8.07%)Baseline: 105.13 ns
139.18 ns
(69.44%)
Command_Serde/unpack/delete📈 view plot
🚷 view threshold
142.06 ns
(-23.86%)Baseline: 186.57 ns
268.44 ns
(52.92%)
Command_Serde/unpack/delete_index📈 view plot
🚷 view threshold
126.44 ns
(-18.67%)Baseline: 155.47 ns
217.62 ns
(58.10%)
Command_Serde/unpack/set📈 view plot
🚷 view threshold
236.21 ns
(-9.15%)Baseline: 259.99 ns
354.12 ns
(66.70%)
Command_Serde/unpack/set_index📈 view plot
🚷 view threshold
125.92 ns
(-17.66%)Baseline: 152.93 ns
211.64 ns
(59.50%)
Payload_encryption/pack/remove_cmd📈 view plot
🚷 view threshold
100.77 ns
(-10.53%)Baseline: 112.63 ns
150.08 ns
(67.14%)
Payload_encryption/pack/set_cmd📈 view plot
🚷 view threshold
189.91 ns
(-3.64%)Baseline: 197.08 ns
262.85 ns
(72.25%)
Payload_encryption/unpack/remove_cmd📈 view plot
🚷 view threshold
144.12 ns
(-26.28%)Baseline: 195.49 ns
283.62 ns
(50.82%)
Payload_encryption/unpack/set_cmd📈 view plot
🚷 view threshold
247.42 ns
(-8.42%)Baseline: 270.18 ns
373.90 ns
(66.17%)
Raft_1Node_Latency/prefix/1node📈 view plot
🚷 view threshold
2,450,000.00 ns
(-19.22%)Baseline: 3,033,010.30 ns
7,933,607.76 ns
(30.88%)
Raft_1Node_Latency/read/1node📈 view plot
🚷 view threshold
34,007.00 ns
(-8.64%)Baseline: 37,223.14 ns
51,541.26 ns
(65.98%)
Raft_1Node_Latency/remove/1node📈 view plot
🚷 view threshold
543,330.00 ns
(-55.53%)Baseline: 1,221,918.44 ns
11,600,946.69 ns
(4.68%)
Raft_1Node_Latency/write/1node📈 view plot
🚷 view threshold
516,090.00 ns
(-48.26%)Baseline: 997,425.62 ns
7,588,253.69 ns
(6.80%)
build_snapshot/default📈 view plot
🚷 view threshold
86,493.00 ns
(-31.04%)Baseline: 125,420.52 ns
277,176.62 ns
(31.21%)
fernet token/project📈 view plot
🚷 view threshold
1,341.80 ns
(-5.84%)Baseline: 1,425.05 ns
1,798.82 ns
(74.59%)
get_data_keyspace📈 view plot
🚷 view threshold
0.33 ns
(+4.48%)Baseline: 0.32 ns
0.39 ns
(85.01%)
get_db📈 view plot
🚷 view threshold
0.33 ns
(+5.16%)Baseline: 0.32 ns
0.39 ns
(85.70%)
get_fernet_token_timestamp/project📈 view plot
🚷 view threshold
147.30 ns
(+5.56%)Baseline: 139.54 ns
179.15 ns
(82.22%)
get_keyspace📈 view plot
🚷 view threshold
🚨 view alert (🔔)
14.13 ns
(+139.50%)Baseline: 5.90 ns
13.79 ns
(102.44%)

🐰 View full continuous benchmarking report in Bencher

@gtema
gtema force-pushed the feat/domain-config-identity-dispatch branch from 797db07 to e9144b0 Compare September 4, 2026 10:37
@gtema gtema linked an issue Sep 4, 2026 that may be closed by this pull request
Comment thread crates/core/src/identity/service.rs Fixed
Wire the domain configuration resolution layer into identity backend
selection (issue #960). Until now `DomainConfigResolver` was built on
the provider but nothing consulted it: `IdentityService` held one global
`backend_driver` chosen from `[identity] driver` and used it for every
domain.

`IdentityService` now keeps the full registered-backend map plus an
optional `DomainConfigResolver` (present only when
`domain_specific_drivers_enabled`). Every backend call site routes
through `driver_for` / `driver_for_user` / `driver_for_group`, which
resolve the domain's stored `identity/driver` and dispatch to the named
backend, falling back to the global driver when per-domain drivers are
off, the domain is unknown, no `identity/driver` is stored, or
resolution fails. Resolution is cached per domain with no invalidation
(a follow-up); a change to a domain's driver through the config API is
picked up on restart. `from_driver` keeps a `None` resolver, so existing
single-mock unit tests are unaffected.

`driver_for_user` / `driver_for_group` mirror python-keystone: while
per-domain drivers are on, the public id is looked up in the id mapping
and a hit dispatches to that domain's driver, a miss falls back to the
global driver.

The LDAP identity backend now registers whenever per-domain drivers are
enabled, not only when it is the global driver, so a domain config may
select `ldap`. Writes to an LDAP-backed domain surface the existing
read-only error.

Port two python-keystone guards that per-domain dispatch reintroduces:

- `_select_identity_driver`: `IdentityBackend::is_domain_aware` (LDAP is
  not) gates a foreign domain on a non-domain-aware global driver with
  `DomainNotFound`, keyed on `[identity] default_domain_id`.
- `_assert_user_and_group_in_same_backend`: group membership across two
  identity backends is rejected with a new
  `IdentityProviderError::CrossBackendNotAllowed` (403), wired into
  add/remove user-to-group and their expiring variants.

Also lands the single-domain SQL identity-driver registration lock
deferred from #959: while `domain_configurations_from_database` is on,
`DomainConfigService` claims the `SQL` registration before storing an
`identity/driver = sql` configuration and releases it when the driver
changes away or the configuration is deleted; a second domain claiming
it gets HTTP 409.

Tests: a `per_domain_dispatch` unit suite covering resolution,
per-domain caching, the id-mapping lookup, both ported guards, and
password-auth dispatch; a SQL-only `test_integration` suite exercising
the resolver and the registration lock end to end through the provider
stack.

Assisted-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Artem Goncharov <artem.goncharov@gmail.com>
@gtema
gtema force-pushed the feat/domain-config-identity-dispatch branch from e9144b0 to 4e14873 Compare September 4, 2026 11:14
Comment thread crates/core/src/identity/service/tests/per_domain_dispatch.rs Dismissed
@gtema
gtema added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit df0d9d2 Sep 4, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 5: Wire domain config into identity driver selection

2 participants