generated from coulomb/repo-seed
feat: implement T01-T04 — Go module, canonical model, LDAP validator, error taxonomy
- T01: Go module (keycape), full directory skeleton, Makefile, CI workflow - T02: spec/canonical-model.yaml with 6 entities + Go domain types - T03: spec/ldap-schema.yaml + validator binary with structural/semantic rules - T04: Error taxonomy — 4 stable error types, JSON format, HTTP helpers 28 tests pass, go vet clean, go build clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
174
spec/canonical-model.yaml
Normal file
174
spec/canonical-model.yaml
Normal file
@@ -0,0 +1,174 @@
|
||||
version: "0.1"
|
||||
description: >
|
||||
Canonical Identity Model for KeyCape / NetKingdom IAM Profile.
|
||||
This file is the source of truth for all identity entities.
|
||||
All provisioning, tests, and migrations derive from these definitions.
|
||||
|
||||
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."
|
||||
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
|
||||
required: true
|
||||
minItems: 1
|
||||
description: "Allowed redirect URIs. Wildcards are NEVER permitted."
|
||||
allowedScopes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required: true
|
||||
description: "Scopes this client may request."
|
||||
grantTypes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [authorization_code]
|
||||
required: true
|
||||
description: "Allowed OAuth2 grant types. Only authorization_code in v0.1."
|
||||
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)."
|
||||
tokenProfile:
|
||||
type: string
|
||||
description: "Optional: token configuration profile name."
|
||||
environments:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: "Environments this client is registered for (e.g. prod, staging)."
|
||||
|
||||
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."
|
||||
91
spec/ldap-schema.yaml
Normal file
91
spec/ldap-schema.yaml
Normal file
@@ -0,0 +1,91 @@
|
||||
version: "0.1"
|
||||
description: >
|
||||
Canonical LDAP Schema for KeyCape / NetKingdom IAM Profile.
|
||||
Expresses the canonical identity model in LDAP terms.
|
||||
Portable across LLDAP, OpenLDAP, 389DS, and Active Directory.
|
||||
|
||||
base_dn: "dc=netkingdom,dc=local"
|
||||
|
||||
organization_units:
|
||||
users:
|
||||
dn: "ou=users,dc=netkingdom,dc=local"
|
||||
description: "User accounts"
|
||||
object_classes:
|
||||
required:
|
||||
- inetOrgPerson
|
||||
- organizationalPerson
|
||||
- person
|
||||
- top
|
||||
attributes:
|
||||
required:
|
||||
- uid # canonical: username
|
||||
- cn # canonical: displayName
|
||||
- sn # canonical: surname (may be set to displayName if absent)
|
||||
optional:
|
||||
- mail # canonical: email
|
||||
- memberOf # back-reference to group membership
|
||||
forbidden: []
|
||||
naming_attr: uid
|
||||
examples:
|
||||
- dn: "uid=alice,ou=users,dc=netkingdom,dc=local"
|
||||
uid: alice
|
||||
cn: "Alice Example"
|
||||
sn: Example
|
||||
mail: alice@example.com
|
||||
|
||||
groups:
|
||||
dn: "ou=groups,dc=netkingdom,dc=local"
|
||||
description: "User groups"
|
||||
object_classes:
|
||||
required:
|
||||
- groupOfNames
|
||||
- top
|
||||
attributes:
|
||||
required:
|
||||
- cn # canonical: name
|
||||
- member # list of member DNs
|
||||
optional:
|
||||
- description
|
||||
forbidden: []
|
||||
naming_attr: cn
|
||||
examples:
|
||||
- dn: "cn=admins,ou=groups,dc=netkingdom,dc=local"
|
||||
cn: admins
|
||||
member:
|
||||
- "uid=alice,ou=users,dc=netkingdom,dc=local"
|
||||
|
||||
clients:
|
||||
dn: "ou=clients,dc=netkingdom,dc=local"
|
||||
description: "OIDC client registrations"
|
||||
object_classes:
|
||||
required:
|
||||
- inetOrgPerson
|
||||
- top
|
||||
attributes:
|
||||
required:
|
||||
- uid # canonical: clientId
|
||||
- cn # canonical: displayName
|
||||
optional:
|
||||
- description
|
||||
forbidden: []
|
||||
naming_attr: uid
|
||||
|
||||
validation_rules:
|
||||
structural:
|
||||
- name: valid_dn_structure
|
||||
description: "All DNs must conform to the base_dn and OU layout above."
|
||||
- name: required_attributes_present
|
||||
description: "Every entry must carry all required attributes for its OU."
|
||||
- name: no_unknown_attributes
|
||||
description: "No attributes outside the allowed set may appear."
|
||||
- name: valid_group_memberships
|
||||
description: "All member values must be non-empty valid DNs."
|
||||
semantic:
|
||||
- name: referenced_users_exist
|
||||
description: "Every user ID referenced in group members must exist."
|
||||
- name: no_cyclic_groups
|
||||
description: "Groups may not contain other group IDs as members."
|
||||
- name: usernames_unique
|
||||
description: "The uid attribute must be unique across ou=users."
|
||||
- name: email_format_valid
|
||||
description: "mail, when present, must be a valid RFC 5322 address."
|
||||
Reference in New Issue
Block a user