feat(scim): ADR 0024 - Phase 1+2#925
Merged
Merged
Conversation
|
| Branch | claude/scim-v2-support-adr-u1f488 |
| Testbed | ubuntu-latest |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result nanoseconds (ns) (Result Δ%) | Upper Boundary nanoseconds (ns) (Limit %) |
|---|---|---|---|
| Command_Serde/apply/remove | 📈 view plot 🚷 view threshold | 128,410.00 ns(-56.41%)Baseline: 294,601.03 ns | 1,683,072.80 ns (7.63%) |
| Command_Serde/apply/set | 📈 view plot 🚷 view threshold | 143,970.00 ns(-43.18%)Baseline: 253,369.39 ns | 1,009,153.12 ns (14.27%) |
| Command_Serde/pack/delete | 📈 view plot 🚷 view threshold | 128.31 ns(+5.49%)Baseline: 121.63 ns | 146.10 ns (87.82%) |
| Command_Serde/pack/delete_index | 📈 view plot 🚷 view threshold | 111.61 ns(+1.51%)Baseline: 109.95 ns | 131.57 ns (84.83%) |
| Command_Serde/pack/set | 📈 view plot 🚷 view threshold | 208.39 ns(+6.56%)Baseline: 195.57 ns | 238.99 ns (87.20%) |
| Command_Serde/pack/set_index | 📈 view plot 🚷 view threshold | 112.11 ns(+2.03%)Baseline: 109.88 ns | 131.23 ns (85.43%) |
| Command_Serde/unpack/delete | 📈 view plot 🚷 view threshold | 195.09 ns(+1.85%)Baseline: 191.55 ns | 235.35 ns (82.89%) |
| Command_Serde/unpack/delete_index | 📈 view plot 🚷 view threshold | 163.43 ns(+2.44%)Baseline: 159.54 ns | 199.20 ns (82.04%) |
| Command_Serde/unpack/set | 📈 view plot 🚷 view threshold | 290.61 ns(+7.87%)Baseline: 269.41 ns | 333.13 ns (87.24%) |
| Command_Serde/unpack/set_index | 📈 view plot 🚷 view threshold | 162.30 ns(+2.40%)Baseline: 158.50 ns | 196.78 ns (82.48%) |
| Payload_encryption/pack/remove_cmd | 📈 view plot 🚷 view threshold | 118.28 ns(+1.89%)Baseline: 116.09 ns | 141.81 ns (83.41%) |
| Payload_encryption/pack/set_cmd | 📈 view plot 🚷 view threshold | 200.10 ns(-2.19%)Baseline: 204.58 ns | 268.76 ns (74.45%) |
| Payload_encryption/unpack/remove_cmd | 📈 view plot 🚷 view threshold | 209.69 ns(+2.65%)Baseline: 204.27 ns | 256.72 ns (81.68%) |
| Payload_encryption/unpack/set_cmd | 📈 view plot 🚷 view threshold | 294.82 ns(+4.67%)Baseline: 281.67 ns | 354.16 ns (83.24%) |
| Raft_1Node_Latency/prefix/1node | 📈 view plot 🚷 view threshold | 2,614,200.00 ns(-3.61%)Baseline: 2,712,049.06 ns | 6,199,198.72 ns (42.17%) |
| Raft_1Node_Latency/read/1node | 📈 view plot 🚷 view threshold | 43,858.00 ns(+98.33%)Baseline: 22,113.90 ns | 70,355.99 ns (62.34%) |
| Raft_1Node_Latency/remove/1node | 📈 view plot 🚷 view threshold | 384,860.00 ns(-33.80%)Baseline: 581,316.25 ns | 2,334,726.38 ns (16.48%) |
| Raft_1Node_Latency/write/1node | 📈 view plot 🚷 view threshold | 400,890.00 ns(-33.84%)Baseline: 605,946.88 ns | 2,174,528.37 ns (18.44%) |
| build_snapshot/default | 📈 view plot 🚷 view threshold | 108,530.00 ns(-1.13%)Baseline: 109,772.86 ns | 145,861.14 ns (74.41%) |
| fernet token/project | 📈 view plot 🚷 view threshold | 1,479.30 ns(+7.03%)Baseline: 1,382.20 ns | 1,633.62 ns (90.55%) |
| get_data_keyspace | 📈 view plot 🚷 view threshold | 0.31 ns(-0.12%)Baseline: 0.31 ns | 0.36 ns (85.86%) |
| get_db | 📈 view plot 🚷 view threshold | 0.31 ns(-0.11%)Baseline: 0.31 ns | 0.36 ns (85.90%) |
| get_fernet_token_timestamp/project | 📈 view plot 🚷 view threshold | 133.19 ns(-7.06%)Baseline: 143.31 ns | 181.97 ns (73.19%) |
| get_keyspace | 📈 view plot 🚷 view threshold | 4.40 ns(-11.72%)Baseline: 4.99 ns | 10.66 ns (41.29%) |
bb3d35d to
3569f8f
Compare
|
🦢 Load Test Results Goose Attack ReportPlan Overview
Request Metrics
Response Time Metrics
Status Code Metrics
Transaction Metrics
Scenario Metrics
Error Metrics
|
3569f8f to
e1f1c16
Compare
Adds the realm-registration layer for SCIM v2 resource provisioning,
the first of six planned phases, plus the first fully working SCIM
resource: POST/GET/PUT/DELETE /SCIM/v2/{domain_id}/Users, backed by
real Identity users.
- New core type `ScimRealmResource` (+ Create/Update/ListParameters)
and a `ScimRealmProviderError` enum, following the ApiClientResource
pattern. `idp_id` is mandatory and validated against a real
`IdentityProvider` at realm create/update.
- New `openstack-keystone-scim-driver-raft` crate storing realms at
`scim_realm:v1:<domain_id>:<provider_id>`.
- New `ScimRealmApi`/`ScimRealmService` provider
(ExecutionContext-based, so realm CRUD gets CADF audit trail parity
with other v4 admin resources) wired through
`PluginManagerApi`/`Provider`/`Config`.
- New `/v4/scim-realms` CRUD API with OPA policies under
`identity/scim_realm/*`, requiring a `manager` (or `admin`) role.
- `ScimRealmAuth` extractor (ADR 0024 §2.C amendment to ADR 0021 §3
Step 4): wraps the API-Key ingress pipeline, enforcing Domain-only
scope and the Realm Activation Gate (403 if missing/disabled) before
any SCIM resource handler runs.
- Mapping Engine write-time constraint (ADR 0024 §2.C): rejects
`Authorization::Project` rules in a ruleset whose `provider_id` has
an active SCIM realm, with 422 Unprocessable Entity, mirroring the
existing `is_system` defense-in-depth check.
- `ScimResourceIndex` core type + scim-driver-raft storage, with an
atomic realm-scoped externalId claim (create_if_absent) closing the
TOCTOU race for ADR 0024 §3.D.
- Domain-wide case-insensitive userName collision check
(`IdentityApi::find_user_by_name_ci`) against local_user/nonlocal_user.
- ScimRealmAuth-gated Users handlers implementing the Ownership
Fencing Algorithm (§3.C), soft-disable delete with token revocation
+ CADF disable event (§6.A), and a SCIM-shaped 409 uniqueness error
envelope.
- Fixed a latent bug in `resolve_verified_api_client`: it extracted
`Path<String>` for domain_id, which only works when the route has
exactly one dynamic segment; the new /Users/{id} routes have two and
would have failed every request with 401.
- OPA policies under `identity/scim/user/{create,list,show,update,delete}`
plus their `_test.rego` pairs.
SCIM provisioning and federation JIT login used to create separate
`user` rows for the same real person: SCIM always wrote a `local_user`
row while federation kept its own `federated_user` link, with nothing
correlating the two. Fixed by matching python-keystone's own shadow-user
scheme instead of inventing a new one:
- `generate_public_id(domain_id, local_id, entity_type)`
(core::identity) is bit-compatible with python-keystone's
`id_generators.sha256.Generator.generate_public_ID`: sha256 over
domain_id+entity_type+local_id in that order, no `idp_id` mixed in.
This deliberately inherits upstream's own accepted collision risk
when multiple IdPs share a domain and reuse the same subject value,
rather than diverging from the reference formula.
- `UserCreate.user_type` (Local/NonLocal) drives `identity-driver-sql`
to create a `nonlocal_user` row instead of `local_user` for
externally-managed identities; setting a password on a NonLocal user
is a hard `Conflict`.
- SCIM user create requires `externalId`, derives the user id from it
deterministically, and provisions as NonLocal.
- Federation JIT (`find_or_create_federated_user`) checks for a user
at that same deterministic id before its legacy `federated_user`
lookup/create fallback, and now assigns that id itself when creating
a brand-new federated user too — so convergence holds regardless of
which side (SCIM or federation) provisions the person first.
- SCIM create pre-checks for an existing user at the derived id and
returns a clean 409 (`scimType: uniqueness`) instead of letting a
primary-key collision surface as an opaque driver error.
Assisted-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Artem Goncharov <artem.goncharov@gmail.com>
e1f1c16 to
6bbcec4
Compare
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the realm-registration layer for SCIM v2 resource provisioning, the
first of six planned phases. No SCIM Users/Groups endpoints yet - this
phase is pure plumbing to gate future resource CRUD behind an explicit
realm-activation step.
ScimRealmResource(+ Create/Update/ListParameters) anda
ScimRealmProviderErrorenum, following the ApiClientResourcepattern.
openstack-keystone-scim-driver-raftcrate storing realms atscim_realm:v1:<domain_id>:<provider_id>.ScimRealmApi/ScimRealmServiceprovider(ExecutionContext-based, so realm CRUD gets CADF audit trail parity
with other v4 admin resources) wired through
PluginManagerApi/Provider/Configalongside the existingproviders.
/v4/scim-realmsCRUD API (create/list/show/update) with OPApolicies under
identity/scim_realm/*, requiring amanager(or
admin) role.ScimRealmAuthextractor (ADR 0024 §2.C amendment to ADR 0021 §3 Step4): wraps the existing API-Key ingress pipeline, additionally
enforcing Domain-only scope and the Realm Activation Gate (403 if the
realm is missing/disabled) before any future SCIM resource handler
runs.
Authorization::Projectrules in a ruleset whoseprovider_idhas anactive SCIM realm, with 422 Unprocessable Entity (new KeystoneApiError
variant), mirroring the existing
is_systemdefense-in-depth check.Adds the first fully working SCIM resource: POST/GET/PUT/DELETE
/SCIM/v2/{domain_id}/Users, backed by real Identity users.
realm-scoped externalId claim (create_if_absent) closing the TOCTOU
race for that dimension of ADR 0024 Section 3.D.
(IdentityApi::find_user_by_name_ci) against local_user/nonlocal_user.
Algorithm (Section 3.C), soft-disable delete with token revocation +
CADF disable event (Section 6.A), and a SCIM-shaped 409 uniqueness
error envelope.
Path for domain_id, which only works when the whole route has
exactly one dynamic segment; the new /Users/{id} routes have two and
would have failed every request with 401.
plus their _test.rego pairs.
Assisted-By: Claude Sonnet 5 noreply@anthropic.com
Signed-off-by: Artem Goncharov artem.goncharov@gmail.com