Skip to content

feat(scim): ADR 0024 - Phase 3 (Groups + membership isolation)#928

Merged
gtema merged 1 commit into
mainfrom
claude/scim-v2-support-adr-u1f488
Jul 6, 2026
Merged

feat(scim): ADR 0024 - Phase 3 (Groups + membership isolation)#928
gtema merged 1 commit into
mainfrom
claude/scim-v2-support-adr-u1f488

Conversation

@gtema

@gtema gtema commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Add /SCIM/v2/{domain_id}/Groups create/list/show/update/delete,
mirroring the Users vertical slice: ownership fencing, domain-wide
displayName uniqueness, and the same ScimApiError/ScimRealmAuth
scaffolding. Group.id stays server-assigned and externalId optional -
unlike Users, nothing federates in as a Group, so there's no
convergence hazard a deterministic id would solve.

New backend surface Groups didn't have at all before this: update_group
(+ GroupUpdate type), list_users_of_group (member hydration, mirrors
list_groups_of_user), and find_group_by_name_ci (mirrors
find_user_by_name_ci). PUT membership resync diffs the desired set
against list_users_of_group and applies it via the existing
add_users_to_groups/remove_user_from_group primitives.

Cross-realm membership fencing (ADR 0024 §7) and the 1000-member cap
(§11) are enforced before any mutation, on both create and update.
Delete neutralizes the group's role assignments and tombstones the
ScimResourceIndex without touching membership rows, matching the
forensic-retention rationale in §6.B.

Add policy/identity/scim/group/{create,list,show,update,delete}.rego
mirroring the Users policies, and 20 handler unit tests plus 8 new
identity-driver-sql tests for the new backend methods.

Assisted-by: Claude noreply@anthropic.com
Signed-off-by: Artem Goncharov artem.goncharov@gmail.com

Add /SCIM/v2/{domain_id}/Groups create/list/show/update/delete,
mirroring the Users vertical slice: ownership fencing, domain-wide
displayName uniqueness, and the same ScimApiError/ScimRealmAuth
scaffolding. Group.id stays server-assigned and externalId optional -
unlike Users, nothing federates in as a Group, so there's no
convergence hazard a deterministic id would solve.

New backend surface Groups didn't have at all before this: update_group
(+ GroupUpdate type), list_users_of_group (member hydration, mirrors
list_groups_of_user), and find_group_by_name_ci (mirrors
find_user_by_name_ci). PUT membership resync diffs the desired set
against list_users_of_group and applies it via the existing
add_users_to_groups/remove_user_from_group primitives.

Cross-realm membership fencing (ADR 0024 §7) and the 1000-member cap
(§11) are enforced before any mutation, on both create and update.
Delete neutralizes the group's role assignments and tombstones the
ScimResourceIndex without touching membership rows, matching the
forensic-retention rationale in §6.B.

Add policy/identity/scim/group/{create,list,show,update,delete}.rego
mirroring the Users policies, and 20 handler unit tests plus 8 new
identity-driver-sql tests for the new backend methods.

Assisted-by: Claude <noreply@anthropic.com>
Signed-off-by: Artem Goncharov <artem.goncharov@gmail.com>
@gtema gtema force-pushed the claude/scim-v2-support-adr-u1f488 branch from ad69b8f to 1e6214a Compare July 6, 2026 17:10
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🦢 Load Test Results

Goose Attack Report

Plan Overview

Action Started Stopped Elapsed Users
Increasing 26-07-06 17:29:37 26-07-06 17:29:54 00:00:17 0 → 30
Maintaining 26-07-06 17:29:54 26-07-06 17:30:24 00:00:30 30
Decreasing 26-07-06 17:30:24 26-07-06 17:30:24 00:00:00 0 ← 30

Request Metrics

Method Name # Requests # Fails Average (ms) Min (ms) Max (ms) RPS Failures/s
DELETE DELETE /v3/auth/tokens 530 0 103.36 23 136 17.67 0.00
DELETE DELETE /v3/projects/:id (teardown) 2 0 67.50 65 70 0.07 0.00
DELETE DELETE /v3/users/:id (teardown) 3 0 47.67 44 54 0.10 0.00
GET 4844 0 97.16 65 186 161.47 0.00
GET GET /v3/auth/tokens (validate new) 527 0 103.25 86 143 17.57 0.00
GET GET /v3/projects/:id 754 0 79.39 65 112 25.13 0.00
GET GET /v3/projects/:id (catalog) 748 0 79.60 67 119 24.93 0.00
GET GET /v3/users/:id 1055 0 85.28 72 117 35.17 0.00
GET GET /v3/users/:id (catalog) 830 0 84.14 72 122 27.67 0.00
POST POST /v3/auth/tokens 526 0 77.78 66 110 17.53 0.00
Aggregated 9819 0 91.68 23 186 327.30 0.00

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 100 100 110 110 110 120 130 136
DELETE DELETE /v3/projects/:id (teardown) 65 65 65 70 70 70 70 70
DELETE DELETE /v3/users/:id (teardown) 45 45 45 45 54 54 54 54
GET 89 93 98 100 150 150 160 186
GET GET /v3/auth/tokens (validate new) 100 100 110 110 110 110 120 140
GET GET /v3/projects/:id 79 80 82 84 87 89 96 110
GET GET /v3/projects/:id (catalog) 79 80 82 84 87 90 100 119
GET GET /v3/users/:id 85 86 88 90 93 96 110 117
GET GET /v3/users/:id (catalog) 84 85 86 88 91 94 100 120
POST POST /v3/auth/tokens 77 78 80 82 85 88 94 110
Aggregated 85 89 95 100 110 150 160 186

Status Code Metrics

Method Name Status Codes
DELETE DELETE /v3/auth/tokens 530 [204]
DELETE DELETE /v3/projects/:id (teardown) 2 [204]
DELETE DELETE /v3/users/:id (teardown) 3 [204]
GET 4,844 [200]
GET GET /v3/auth/tokens (validate new) 527 [200]
GET GET /v3/projects/:id 754 [200]
GET GET /v3/projects/:id (catalog) 748 [200]
GET GET /v3/users/:id 1,055 [200]
GET GET /v3/users/:id (catalog) 830 [200]
POST POST /v3/auth/tokens 526 [200]
Aggregated 535 [204], 9,284 [200]

Transaction Metrics

Transaction # Times Run # Fails Average (ms) Min (ms) Max (ms) RPS Failures/s
ReadHeavy
0.0 1 0 24.00 24 24 0.03 0.00
0.1 823 0 96.42 82 133 27.43 0.00
0.2 821 0 79.61 65 110 27.37 0.00
0.3 821 0 79.50 67 113 27.37 0.00
TokenLifecycle
1.0 0 0 0.00 0 0 0.00 0.00
1.1 530 0 285.48 188 334 17.67 0.00
ValidateToken
2.0 0 0 0.00 0 0 0.00 0.00
2.1 800 0 150.61 102 186 26.67 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 1055 0 85.33 72 117 35.17 0.00
3.3 3 0 47.67 44 54 0.10 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 754 0 79.44 65 112 25.13 0.00
4.3 2 0 67.50 65 70 0.07 0.00
UserRead
5.0 0 0 0.00 0 0 0.00 0.00
5.1 829 0 96.60 82 131 27.63 0.00
5.2 830 0 84.19 72 122 27.67 0.00
ProjectRead
6.0 0 0 0.00 0 0 0.00 0.00
6.1 750 0 80.40 67 110 25.00 0.00
6.2 748 0 79.66 67 119 24.93 0.00
Aggregated 8767 0 102.68 24 334 292.23 0.00

Scenario Metrics

Transaction # Users # Times Run Average (ms) Min (ms) Max (ms) Scenarios/s Iterations
ReadHeavy 7 819 256.54 230 306 27.30 117.00
TokenLifecycle 5 525 285.95 258 334 17.50 105.00
ValidateToken 4 796 150.74 131 186 26.53 199.00
UserCRUD 3 1052 85.36 72 117 35.07 350.67
ProjectCRUD 2 752 79.46 65 112 25.07 376.00
UserRead 5 825 181.33 161 221 27.50 165.00
ProjectRead 4 747 160.59 140 211 24.90 186.75
Aggregated 30 5516 163.04 65 334 183.87 1499.42

View full report

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchclaude/scim-v2-support-adr-u1f488
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
nanoseconds (ns)
(Result Δ%)
Upper Boundary
nanoseconds (ns)
(Limit %)
Command_Serde/apply/remove📈 view plot
🚷 view threshold
144,060.00 ns
(-50.50%)Baseline: 291,039.92 ns
1,680,724.39 ns
(8.57%)
Command_Serde/apply/set📈 view plot
🚷 view threshold
149,090.00 ns
(-39.92%)Baseline: 248,170.33 ns
1,003,315.24 ns
(14.86%)
Command_Serde/pack/delete📈 view plot
🚷 view threshold
125.38 ns
(+2.70%)Baseline: 122.09 ns
145.50 ns
(86.17%)
Command_Serde/pack/delete_index📈 view plot
🚷 view threshold
111.68 ns
(+1.16%)Baseline: 110.40 ns
131.14 ns
(85.16%)
Command_Serde/pack/set📈 view plot
🚷 view threshold
206.72 ns
(+5.30%)Baseline: 196.31 ns
237.79 ns
(86.94%)
Command_Serde/pack/set_index📈 view plot
🚷 view threshold
111.84 ns
(+1.38%)Baseline: 110.32 ns
130.75 ns
(85.54%)
Command_Serde/unpack/delete📈 view plot
🚷 view threshold
203.69 ns
(+5.88%)Baseline: 192.38 ns
236.31 ns
(86.19%)
Command_Serde/unpack/delete_index📈 view plot
🚷 view threshold
165.38 ns
(+3.11%)Baseline: 160.40 ns
200.32 ns
(82.56%)
Command_Serde/unpack/set📈 view plot
🚷 view threshold
296.13 ns
(+9.39%)Baseline: 270.72 ns
331.36 ns
(89.37%)
Command_Serde/unpack/set_index📈 view plot
🚷 view threshold
161.32 ns
(+1.32%)Baseline: 159.21 ns
197.56 ns
(81.66%)
Payload_encryption/pack/remove_cmd📈 view plot
🚷 view threshold
118.54 ns
(+1.79%)Baseline: 116.45 ns
141.46 ns
(83.80%)
Payload_encryption/pack/set_cmd📈 view plot
🚷 view threshold
253.91 ns
(+23.68%)Baseline: 205.29 ns
267.54 ns
(94.90%)
Payload_encryption/unpack/remove_cmd📈 view plot
🚷 view threshold
225.72 ns
(+9.87%)Baseline: 205.45 ns
258.26 ns
(87.40%)
Payload_encryption/unpack/set_cmd📈 view plot
🚷 view threshold
307.53 ns
(+8.74%)Baseline: 282.81 ns
351.79 ns
(87.42%)
Raft_1Node_Latency/prefix/1node📈 view plot
🚷 view threshold
2,600,400.00 ns
(-5.98%)Baseline: 2,765,780.31 ns
6,275,473.83 ns
(41.44%)
Raft_1Node_Latency/read/1node📈 view plot
🚷 view threshold
43,296.00 ns
(+91.14%)Baseline: 22,651.12 ns
70,580.01 ns
(61.34%)
Raft_1Node_Latency/remove/1node📈 view plot
🚷 view threshold
389,620.00 ns
(-21.96%)Baseline: 499,261.56 ns
1,386,266.44 ns
(28.11%)
Raft_1Node_Latency/write/1node📈 view plot
🚷 view threshold
419,750.00 ns
(-30.12%)Baseline: 600,666.41 ns
2,172,417.46 ns
(19.32%)
build_snapshot/default📈 view plot
🚷 view threshold
118,300.00 ns
(+7.63%)Baseline: 109,916.30 ns
145,973.15 ns
(81.04%)
fernet token/project📈 view plot
🚷 view threshold
1,451.50 ns
(+4.62%)Baseline: 1,387.41 ns
1,633.12 ns
(88.88%)
get_data_keyspace📈 view plot
🚷 view threshold
0.32 ns
(+2.00%)Baseline: 0.31 ns
0.37 ns
(87.75%)
get_db📈 view plot
🚷 view threshold
0.31 ns
(-0.52%)Baseline: 0.31 ns
0.37 ns
(85.61%)
get_fernet_token_timestamp/project📈 view plot
🚷 view threshold
142.11 ns
(-1.06%)Baseline: 143.63 ns
181.34 ns
(78.37%)
get_keyspace📈 view plot
🚷 view threshold
4.36 ns
(-12.76%)Baseline: 5.00 ns
10.66 ns
(40.91%)
🐰 View full continuous benchmarking report in Bencher

@gtema gtema merged commit d2f07a5 into main Jul 6, 2026
34 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.

1 participant