SCIM: users, groups, queries, PATCH, Bulk and cursors at /scim/v2, over x:Account (SCIM-1 to SCIM-57)

Every SCIM operation becomes the x:Account get, query or set JMAP makes,
as the service principal, so permissions, tenant scope and limits,
address uniqueness and account destruction are enforced in one place.
Discovery is anonymous; everything else takes an API key as a bearer
token and nothing else. Domains open to SCIM carry a flag in the domain
cache. Filters take eq and and, answered from the account indexes, with
unindexed attributes checked on at most 200 candidates. Cursors are
stateless, HMAC-sealed under the server key. PATCH applies to the
resource in memory and saves it as a PUT, so it is all or nothing.
Groups get an address from their display name on the principal's
domain; membership is written on each user.

Every write emits one of five new scim.* events (ids 637 to 641), also
added to the packaged schema. The helpers the surviving SCIM suites
import are rebuilt from the spec; scim_tests runs the new acceptance
suite and the surviving tenant isolation suite, and both pass.
This commit is contained in:
2026-09-19 09:35:23 -07:00
parent 776d18d06e
commit 0ca26070d7
28 changed files with 6141 additions and 22 deletions
+15 -1
View File
@@ -6,7 +6,8 @@
// This file is auto-generated. Do not edit directly.
pub const TOTAL_EVENT_COUNT: usize = 637;
// inbuxa: 637 to 641 are the fork's SCIM events (SCIM-54)
pub const TOTAL_EVENT_COUNT: usize = 642;
pub const TOTAL_METRIC_COUNT: usize = 369;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -42,6 +43,8 @@ pub enum EventType {
Queue(QueueEvent),
Registry(RegistryEvent),
Resource(ResourceEvent),
// inbuxa: SCIM-54
Scim(ScimEvent),
Security(SecurityEvent),
Server(ServerEvent),
Sieve(SieveEvent),
@@ -624,6 +627,17 @@ pub enum ResourceEvent {
ApplicationUnpacked = 602,
}
// inbuxa: SCIM-54: every write an identity provider makes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum ScimEvent {
ResourceCreated = 637,
ResourceUpdated = 638,
ResourceSuspended = 639,
ResourceReactivated = 640,
ResourceDeleted = 641,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum SecurityEvent {
+42
View File
@@ -36,6 +36,12 @@ impl EventType {
b"acme.error" => EventType::Acme(AcmeEvent::Error),
b"ai.llm-response" => EventType::Ai(AiEvent::LlmResponse),
b"ai.api-error" => EventType::Ai(AiEvent::ApiError),
// inbuxa: SCIM-54
b"scim.resource-created" => EventType::Scim(ScimEvent::ResourceCreated),
b"scim.resource-updated" => EventType::Scim(ScimEvent::ResourceUpdated),
b"scim.resource-suspended" => EventType::Scim(ScimEvent::ResourceSuspended),
b"scim.resource-reactivated" => EventType::Scim(ScimEvent::ResourceReactivated),
b"scim.resource-deleted" => EventType::Scim(ScimEvent::ResourceDeleted),
b"arc.chain-too-long" => EventType::Arc(ArcEvent::ChainTooLong),
b"arc.invalid-instance" => EventType::Arc(ArcEvent::InvalidInstance),
b"arc.invalid-cv" => EventType::Arc(ArcEvent::InvalidCv),
@@ -679,6 +685,12 @@ impl EventType {
EventType::Acme(AcmeEvent::Error) => "acme.error",
EventType::Ai(AiEvent::LlmResponse) => "ai.llm-response",
EventType::Ai(AiEvent::ApiError) => "ai.api-error",
// inbuxa: SCIM-54
EventType::Scim(ScimEvent::ResourceCreated) => "scim.resource-created",
EventType::Scim(ScimEvent::ResourceUpdated) => "scim.resource-updated",
EventType::Scim(ScimEvent::ResourceSuspended) => "scim.resource-suspended",
EventType::Scim(ScimEvent::ResourceReactivated) => "scim.resource-reactivated",
EventType::Scim(ScimEvent::ResourceDeleted) => "scim.resource-deleted",
EventType::Arc(ArcEvent::ChainTooLong) => "arc.chain-too-long",
EventType::Arc(ArcEvent::InvalidInstance) => "arc.invalid-instance",
EventType::Arc(ArcEvent::InvalidCv) => "arc.invalid-cv",
@@ -1457,6 +1469,12 @@ impl EventType {
EventType::Acme(AcmeEvent::Error) => 15,
EventType::Ai(AiEvent::LlmResponse) => 556,
EventType::Ai(AiEvent::ApiError) => 557,
// inbuxa: SCIM-54
EventType::Scim(ScimEvent::ResourceCreated) => 637,
EventType::Scim(ScimEvent::ResourceUpdated) => 638,
EventType::Scim(ScimEvent::ResourceSuspended) => 639,
EventType::Scim(ScimEvent::ResourceReactivated) => 640,
EventType::Scim(ScimEvent::ResourceDeleted) => 641,
EventType::Arc(ArcEvent::ChainTooLong) => 28,
EventType::Arc(ArcEvent::InvalidInstance) => 31,
EventType::Arc(ArcEvent::InvalidCv) => 30,
@@ -2099,6 +2117,12 @@ impl EventType {
15 => Some(EventType::Acme(AcmeEvent::Error)),
556 => Some(EventType::Ai(AiEvent::LlmResponse)),
557 => Some(EventType::Ai(AiEvent::ApiError)),
// inbuxa: SCIM-54
637 => Some(EventType::Scim(ScimEvent::ResourceCreated)),
638 => Some(EventType::Scim(ScimEvent::ResourceUpdated)),
639 => Some(EventType::Scim(ScimEvent::ResourceSuspended)),
640 => Some(EventType::Scim(ScimEvent::ResourceReactivated)),
641 => Some(EventType::Scim(ScimEvent::ResourceDeleted)),
28 => Some(EventType::Arc(ArcEvent::ChainTooLong)),
31 => Some(EventType::Arc(ArcEvent::InvalidInstance)),
30 => Some(EventType::Arc(ArcEvent::InvalidCv)),
@@ -3056,6 +3080,12 @@ impl EventType {
EventType::Acme(AcmeEvent::TlsAlpnError) => Level::Warn,
EventType::Acme(AcmeEvent::TokenNotFound) => Level::Warn,
EventType::Ai(AiEvent::ApiError) => Level::Warn,
// inbuxa: SCIM-54
EventType::Scim(ScimEvent::ResourceCreated) => Level::Info,
EventType::Scim(ScimEvent::ResourceUpdated) => Level::Info,
EventType::Scim(ScimEvent::ResourceSuspended) => Level::Info,
EventType::Scim(ScimEvent::ResourceReactivated) => Level::Info,
EventType::Scim(ScimEvent::ResourceDeleted) => Level::Info,
EventType::Arc(ArcEvent::SealerNotFound) => Level::Warn,
EventType::Auth(AuthEvent::TooManyAttempts) => Level::Warn,
EventType::Calendar(CalendarEvent::AlarmFailed) => Level::Warn,
@@ -3137,6 +3167,12 @@ impl EventType {
EventType::Acme(AcmeEvent::Error) => "ACME error",
EventType::Ai(AiEvent::LlmResponse) => "LLM response",
EventType::Ai(AiEvent::ApiError) => "AI API error",
// inbuxa: SCIM-54
EventType::Scim(ScimEvent::ResourceCreated) => "SCIM resource created",
EventType::Scim(ScimEvent::ResourceUpdated) => "SCIM resource updated",
EventType::Scim(ScimEvent::ResourceSuspended) => "SCIM user suspended",
EventType::Scim(ScimEvent::ResourceReactivated) => "SCIM user reactivated",
EventType::Scim(ScimEvent::ResourceDeleted) => "SCIM resource deleted",
EventType::Arc(ArcEvent::ChainTooLong) => "ARC chain too long",
EventType::Arc(ArcEvent::InvalidInstance) => "Invalid ARC instance",
EventType::Arc(ArcEvent::InvalidCv) => "Invalid ARC CV",
@@ -4203,6 +4239,12 @@ impl EventType {
EventType::Acme(AcmeEvent::Error),
EventType::Ai(AiEvent::LlmResponse),
EventType::Ai(AiEvent::ApiError),
// inbuxa: SCIM-54
EventType::Scim(ScimEvent::ResourceCreated),
EventType::Scim(ScimEvent::ResourceUpdated),
EventType::Scim(ScimEvent::ResourceSuspended),
EventType::Scim(ScimEvent::ResourceReactivated),
EventType::Scim(ScimEvent::ResourceDeleted),
EventType::Arc(ArcEvent::ChainTooLong),
EventType::Arc(ArcEvent::InvalidInstance),
EventType::Arc(ArcEvent::InvalidCv),