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>
2026-03-13 01:27:54 +01:00
|
|
|
// Package domain contains the canonical identity model for KeyCape.
|
Reconcile the canonical model and discovery with the runtime
Closes gap G02 of the scope assessment for the client-registration and discovery
surface. spec/canonical-model.yaml and domain/model.go both claimed to be the
source of truth and disagreed: the spec restricted grants to authorization_code,
required redirect URIs of every client, and omitted the audience, service
subject, tenant, role, MFA and handoff fields the runtime reads.
The durable part is the link, not the edit. A two-way conformance test compares
the spec against the Go model by reflection and fails when a runtime field has
no spec entry or a spec entry is not read by the runtime, the latter unless
marked runtime: false. It found drift beyond the assessment's list on its first
run -- User.tenant was undeclared -- which is the argument for the check over a
one-time reconciliation.
Discovery now advertises the core profile claims that appear on every token and
derives scopes_supported from the registered clients rather than a fixed list.
The Go model is stated as the runtime authority and the YAML as the reviewed
contract, in both files. This covers client registration and discovery, not
schema enforcement in general, which remains G06.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-07 00:22:49 +02:00
|
|
|
//
|
|
|
|
|
// These types are the runtime authority for all user, group, client and MFA
|
|
|
|
|
// data: the server reads them, not spec/canonical-model.yaml. That YAML is the
|
|
|
|
|
// reviewed contract, and conformance_test.go holds the two together — adding a
|
|
|
|
|
// serialised field here without describing it there fails the build, and so
|
|
|
|
|
// does describing a field there that nothing here reads (KEY-WP-0017).
|
|
|
|
|
//
|
|
|
|
|
// Fields tagged `yaml:"-"` are outside the canonical model by construction:
|
|
|
|
|
// they are runtime policy or secret material, not identity data.
|
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>
2026-03-13 01:27:54 +01:00
|
|
|
package domain
|
|
|
|
|
|
|
|
|
|
import "time"
|
|
|
|
|
|
|
|
|
|
// User is the canonical identity entity — source of truth for all user data.
|
|
|
|
|
type User struct {
|
KEY-WP-0005-T01: IAM Profile core claims for the human PKCE flow
Verified first: grant_types_supported advertises client_credentials in
discovery.go, but token.go only ever accepted authorization_code -- no
service-token issuance path exists at all. Building one from scratch is
materially bigger than extending the existing flow; explicitly not
attempted here, left open in the workplan rather than declared done.
What shipped for the human Authorization Code + PKCE flow:
- domain.User.Tenant (new, omitempty) + token.go's effectiveTenant():
falls back to tenant:coulomb (this workstation's actual tenant, ADR-0006)
when unset -- never an empty tenant claim, never a silent reassignment.
- principal_type: "human", unconditional.
- groups/roles promoted from scope-gated to unconditional core claims,
always [] not null when empty. One pre-existing test asserted the old
scope-gated groups behavior -- updated to match the new intentional
behavior, not left failing or reverted.
- assurance built from PKCESession.MFAVerified (new field, threaded
through completeAuthorization's two call sites in authorize.go) --
whether MFA was actually verified in this session, not static enrollment
state. aal2 only when required-and-passed this time, aal1 otherwise.
go build/vet clean, go test ./... green repo-wide. Two new authorize_test.go
cases assert MFAVerified on both paths. tests/profile/profile_test.go's
TestCompleteTokenFlow (the repo's own full HTTP integration test) extended
with real value assertions for all five claims, not just presence checks.
Python conformance tool not run against a live instance (needs the full
Authelia+LLDAP+privacyIDEA stack); TestCompleteTokenFlow's real HTTP round
trip covers the equivalent claim checks instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 00:03:31 +02:00
|
|
|
ID string `yaml:"id" json:"id"`
|
|
|
|
|
Username string `yaml:"username" json:"username"`
|
|
|
|
|
DisplayName string `yaml:"displayName" json:"displayName"`
|
|
|
|
|
Email string `yaml:"email" json:"email"`
|
|
|
|
|
Enabled bool `yaml:"enabled" json:"enabled"`
|
|
|
|
|
Groups []string `yaml:"groups" json:"groups"`
|
|
|
|
|
Roles []string `yaml:"roles" json:"roles"`
|
|
|
|
|
// Tenant is the NetKingdom IAM Profile tenant claim value
|
|
|
|
|
// (e.g. "tenant:friendly:binky"), per net-kingdom/canon/standards/
|
|
|
|
|
// iam-profile_v0.3.md. Empty means "not yet assigned" -- token
|
|
|
|
|
// issuance falls back to the platform default (KEY-WP-0005-T01) rather
|
|
|
|
|
// than emitting an empty tenant claim, since the profile requires
|
|
|
|
|
// tenant on every token.
|
|
|
|
|
Tenant string `yaml:"tenant,omitempty" json:"tenant,omitempty"`
|
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>
2026-03-13 01:27:54 +01:00
|
|
|
MFAEnrollment *MFAEnrollment `yaml:"mfaEnrollment,omitempty" json:"mfaEnrollment,omitempty"`
|
|
|
|
|
LDAPAttributes map[string]string `yaml:"ldapAttributes,omitempty" json:"ldapAttributes,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Group is a named collection of users.
|
|
|
|
|
type Group struct {
|
|
|
|
|
ID string `yaml:"id" json:"id"`
|
|
|
|
|
Name string `yaml:"name" json:"name"`
|
|
|
|
|
Description string `yaml:"description" json:"description"`
|
|
|
|
|
Members []string `yaml:"members" json:"members"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Role is a named permission set.
|
|
|
|
|
type Role struct {
|
|
|
|
|
ID string `yaml:"id" json:"id"`
|
|
|
|
|
Name string `yaml:"name" json:"name"`
|
|
|
|
|
Description string `yaml:"description" json:"description"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Client is a registered OIDC client (static in v0.1 — no dynamic registration).
|
|
|
|
|
type Client struct {
|
2026-07-27 20:03:07 +02:00
|
|
|
ClientID string `yaml:"clientId" json:"clientId"`
|
|
|
|
|
DisplayName string `yaml:"displayName" json:"displayName"`
|
|
|
|
|
RedirectURIs []string `yaml:"redirectUris" json:"redirectUris"`
|
|
|
|
|
AllowedScopes []string `yaml:"allowedScopes" json:"allowedScopes"`
|
|
|
|
|
GrantTypes []string `yaml:"grantTypes" json:"grantTypes"`
|
|
|
|
|
ClientType string `yaml:"clientType" json:"clientType"` // "confidential" | "public"
|
|
|
|
|
SecretRef string `yaml:"secretRef,omitempty" json:"secretRef,omitempty"`
|
|
|
|
|
ClientSecret string `yaml:"-" json:"-"`
|
2026-09-05 00:41:17 +02:00
|
|
|
Audience string `yaml:"audience,omitempty" json:"audience,omitempty"`
|
2026-07-27 20:03:07 +02:00
|
|
|
ServiceSubject string `yaml:"serviceSubject,omitempty" json:"serviceSubject,omitempty"`
|
|
|
|
|
Tenant string `yaml:"tenant,omitempty" json:"tenant,omitempty"`
|
2026-08-23 13:10:13 +02:00
|
|
|
Roles []string `yaml:"roles,omitempty" json:"roles,omitempty"`
|
|
|
|
|
// TokenLifetime overrides the server default for this confidential client.
|
|
|
|
|
// It is internal runtime policy, not identity data serialized into tokens.
|
|
|
|
|
TokenLifetime time.Duration `yaml:"-" json:"-"`
|
|
|
|
|
MFARequired *bool `yaml:"mfaRequired,omitempty" json:"mfaRequired,omitempty"`
|
|
|
|
|
RegistrationURL string `yaml:"registrationUrl,omitempty" json:"registrationUrl,omitempty"`
|
|
|
|
|
EnrollmentURL string `yaml:"enrollmentUrl,omitempty" json:"enrollmentUrl,omitempty"`
|
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>
2026-03-13 01:27:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Membership links a user to a group.
|
|
|
|
|
type Membership struct {
|
|
|
|
|
UserID string `yaml:"userId" json:"userId"`
|
|
|
|
|
GroupID string `yaml:"groupId" json:"groupId"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MFAEnrollment records that a user has enrolled MFA via privacyIDEA.
|
|
|
|
|
type MFAEnrollment struct {
|
|
|
|
|
UserID string `yaml:"userId" json:"userId"`
|
|
|
|
|
Provider string `yaml:"provider" json:"provider"` // "privacyidea"
|
|
|
|
|
State string `yaml:"state" json:"state"` // "enabled" | "disabled" | "pending"
|
|
|
|
|
EnrolledAt time.Time `yaml:"enrolledAt,omitempty" json:"enrolledAt,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Directory is the full canonical identity directory snapshot.
|
|
|
|
|
// Used for provisioning, validation, and migration operations.
|
|
|
|
|
type Directory struct {
|
|
|
|
|
Users []User `yaml:"users" json:"users"`
|
|
|
|
|
Groups []Group `yaml:"groups" json:"groups"`
|
|
|
|
|
Roles []Role `yaml:"roles" json:"roles"`
|
|
|
|
|
Clients []Client `yaml:"clients" json:"clients"`
|
|
|
|
|
}
|