key-cape/spec/canonical-model.yaml

244 lines
7.4 KiB
YAML
Raw Normal View History

version: "0.1"
description: >
Canonical Identity Model for KeyCape / NetKingdom IAM Profile.
2026-09-07 00:22:49 +02:00
This file is the reviewed contract for identity entities. The runtime
authority is src/internal/domain/model.go: the server reads Go structs, not
this YAML. The two are held together by an executable conformance check
(src/internal/domain/conformance_test.go, KEY-WP-0017), which fails the build
when a runtime field has no entry here or an entry here is not read by the
runtime.
So: change the Go model and this file together. A field declared here but
deliberately not implemented must carry `runtime: false`, which is how the
check tells a reserved field apart from a forgotten one.
entities:
User:
description: "A person or service account in the identity directory."
fields:
id:
type: string
required: true
description: "Stable internal identifier. Immutable after creation."
username:
type: string
required: true
description: "Unique login name. Maps to LDAP uid."
displayName:
type: string
required: true
description: "Human-readable full name. Maps to LDAP cn."
email:
type: string
required: false
format: email
description: "Primary email address. Maps to LDAP mail."
enabled:
type: boolean
required: true
description: "Whether the account is active."
2026-09-07 00:22:49 +02:00
tenant:
type: string
required: false
description: >
NetKingdom IAM Profile tenant claim for this user (e.g.
tenant:friendly:binky). Absent falls back to the platform tenant
rather than emitting an empty claim, since the profile requires a
tenant on every token.
groups:
type: array
items:
type: string
ref: Group.id
description: "Group memberships by group ID."
roles:
type: array
items:
type: string
ref: Role.id
description: "Role assignments by role ID."
mfaEnrollment:
type: object
ref: MFAEnrollment
nullable: true
description: "MFA enrollment record if the user has enrolled."
ldapAttributes:
type: object
additionalProperties: true
description: "Raw LDAP attributes not covered by the canonical model."
Group:
description: "A named collection of users."
fields:
id:
type: string
required: true
description: "Stable internal identifier."
name:
type: string
required: true
description: "Unique group name. Maps to LDAP cn."
description:
type: string
required: false
description: "Human-readable description."
members:
type: array
items:
type: string
ref: User.id
description: "User IDs belonging to this group."
Role:
description: "A named permission set assigned to users."
fields:
id:
type: string
required: true
description: "Stable internal identifier."
name:
type: string
required: true
description: "Unique role name."
description:
type: string
required: false
description: "Human-readable description."
Client:
description: "A registered OIDC client. Registration is static in v0.1."
fields:
clientId:
type: string
required: true
description: "OAuth2 client_id."
displayName:
type: string
required: true
description: "Human-readable client name."
redirectUris:
type: array
items:
type: string
format: uri
2026-09-07 00:22:49 +02:00
required: false
minItems: 1
2026-09-07 00:22:49 +02:00
description: >
Allowed redirect URIs. Wildcards are NEVER permitted. Required for any
client using the authorization_code grant; a client_credentials-only
client registers none.
allowedScopes:
type: array
items:
type: string
required: true
description: "Scopes this client may request."
grantTypes:
type: array
items:
type: string
2026-09-07 00:22:49 +02:00
enum: [authorization_code, client_credentials]
required: true
2026-09-07 00:22:49 +02:00
description: >
Allowed OAuth2 grant types. An empty or absent value means
authorization_code, which is what config validation assumes.
clientType:
type: string
enum: [confidential, public]
required: true
description: "confidential = server-side app; public = SPA or native."
secretRef:
type: string
nullable: true
description: "Reference to the client secret (confidential clients only)."
2026-09-07 00:22:49 +02:00
audience:
type: string
nullable: true
description: >
Static resource audience for access tokens. ID tokens keep the client
audience. Absent means the access-token audience is the client ID.
serviceSubject:
type: string
nullable: true
description: >
Subject claim for client_credentials tokens (e.g. service:secrets-engine).
Required together with tenant for that grant.
tenant:
type: string
nullable: true
description: >
Tenant claim emitted for this client's service tokens. Bound at
registration and never influenced by request parameters; see
docs/tenant-claim-contract.md.
roles:
type: array
items:
type: string
description: "Role claims emitted for this client's service tokens."
mfaRequired:
type: boolean
nullable: true
description: >
Per-client MFA requirement. Absent means the provider default
(mandatory MFA); false lowers ordinary login to AAL1 while acr_values
can still force step-up.
registrationUrl:
type: string
format: uri
nullable: true
description: "Registration handoff target for unknown subjects."
enrollmentUrl:
type: string
format: uri
nullable: true
description: "Factor-enrollment handoff target for unenrolled subjects."
tokenProfile:
type: string
2026-09-07 00:22:49 +02:00
runtime: false
description: >
Reserved. Declared for future token configuration profiles; the runtime
does not read it.
environments:
type: array
items:
type: string
2026-09-07 00:22:49 +02:00
runtime: false
description: >
Reserved. Environments this client is registered for (e.g. prod,
staging); the runtime does not read it.
Membership:
description: "Explicit link between a user and a group."
fields:
userId:
type: string
required: true
ref: User.id
groupId:
type: string
required: true
ref: Group.id
MFAEnrollment:
description: "Records MFA enrollment state for a user via privacyIDEA."
fields:
userId:
type: string
required: true
ref: User.id
provider:
type: string
required: true
enum: [privacyidea]
description: "MFA provider. Only privacyidea is supported in v0.1."
state:
type: string
required: true
enum: [enabled, disabled, pending]
description: "Current enrollment state."
enrolledAt:
type: string
format: datetime
description: "ISO 8601 timestamp of enrollment."