Commit graph

31 commits

Author SHA1 Message Date
7a6666d1f9 Align approval registrations to the tenant:platform decision
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 33s
Operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 accepts tenant:platform
as the platform management tenant for the Glas approval chain, requiring exact
spelling across the approval store, the service-client JWT claim and the
lifecycle CheckRequest.

Changes the tenant field on secrets-engine-approval and approval-engine-operator
only, in the registration fixture and the provisioning packet. Unrelated clients
and the human directory default keep tenant:coulomb, and no audience, scope,
subject, role, lifetime or MFA grant changes.

Adds issuance evidence that the approval shape emits tenant:platform exactly and
never an alias the caller requests, that the OpenBao client gains no
cross-tenant reach, and a fixture guard pinning every reviewed client's tenant.

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-06 22:30:32 +02:00
da7bc31d45 Document tenant claim ownership and prove registration binding
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 32s
Answers the GLAS-WP-0015 tenant-alignment request. The JWT tenant is
tenant:coulomb for all four reviewed service registrations; the approval store
tenant and the policy tenant belong to other owners and KeyCape does no
normalization between them. No mapping is invented and no live registration
changes.

Adds regression tests proving the tenant claim is bound at registration and is
not influenced by request parameters, that distinct registrations never carry
each other's tenant, and that human tokens fall back to the platform tenant
rather than an empty claim.

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-06 20:33:06 +02:00
b989de4e90 Add native verified login and service-token commands
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 41s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06e87-e039-7ed2-b85c-20ad37f8a21b
2026-09-05 01:08:58 +02:00
403904b901 Add bounded resource audiences and enforce browser scope grants
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 36s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06e87-e039-7ed2-b85c-20ad37f8a21b
2026-09-05 00:41:17 +02:00
153258b9d3 Fix UserInfo canonical subject resolution
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 23s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
2026-08-31 23:41:29 +02:00
f5cf77fa0c Admit operator-tunneled OpenBao callback
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 23s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02e3f-7301-7622-9be1-12e5f352881c
2026-08-23 13:16:21 +02:00
efce3e9331 Implement KeyCape provider and service identity contracts
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02e3f-7301-7622-9be1-12e5f352881c
2026-08-23 13:10:13 +02:00
b6af6c5268 Finish KEY-WP-0008: registration handoff and client MFA isolation
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 34s
Add signed registration/enrollment handoffs, per-request assurance
policy with login-session isolation, and /logout. coulomb-social
stays AAL1 unless acr_values or another client raises the bar.
2026-08-16 01:05:27 +02:00
3bef507cb8 KEY-WP-0008: honor per-client mfaRequired and acr_values step-up
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 30s
Allow coulomb-social ordinary login at AAL1 via mfaRequired: false while
keeping provider requireForAll for clients without an override. Preserve
explicit acr_values=aal2 for step-up.
2026-08-09 22:42:51 +02:00
90a20783e5 Map platform-root group to platform operator
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 26s
2026-07-29 21:30:56 +02:00
909bb327fc Deny suspended directory identities
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 24s
2026-07-28 01:23:22 +02:00
e8b4eded88 Map explicit tenant groups into OIDC claims
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 26s
2026-07-28 00:34:19 +02:00
881fffc079 Complete KeyCape service-token rollout
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s
2026-07-27 20:17:55 +02:00
e877d2752d Implement KeyCape service-token issuance
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 47s
2026-07-27 20:03:07 +02:00
44da5f5f99 KEY-WP-0005-T02-T03: cached tenant_roles claim, close workplan
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 1m21s
New internal/adapters/tenantengine package, mirroring
internal/adapters/{lldap,privacyidea,authelia}'s shape: Client.Roles()
calls tenant-engine's cache-read endpoint. Fails open by construction --
unreachable, non-200, malformed body, or a nil *Client all return
(nil, false), never an error to specially handle. Wired into
TokenHandler.TenantEngine (nil by default, existing tests unaffected);
token.go stamps tenant_roles only when ok.

7 adapter tests plus 3 TokenHandler-level tests proving the actual
required behavior end-to-end: present when reachable, token issuance still
200 with every other core claim intact when unreachable (tenant_roles
simply absent -- the literal done-criteria), absent when not configured.

Real bug found and fixed at the source, not worked around: the first live
cross-process check (real flex-auth, real tenant-engine, this adapter)
returned tenant_not_found for a tenant that existed -- tenant-engine's read
endpoint was keyed by its internal tenant_id, but key-cape only ever has
the tenant's profile identifier. Fixed in tenant-engine
(ADHOC-2026-07-24), re-verified with the same live three-process chain --
roles=[IAM] ok=true.

Workplan closed: T01-T03 done. Explicitly still open: client_credentials /
service-token issuance -- no such flow exists in token.go at all, a
materially larger separate piece of work than either task here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 00:18:17 +02:00
f1f7fa9dd7 KEY-WP-0005-T01: IAM Profile core claims for the human PKCE flow
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 1m50s
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
d6d41dd84f Fix OpenBao OIDC token exchange compatibility 2026-06-01 21:20:54 +02:00
06d20c3379 Load LLDAP organizational unit config 2026-05-25 00:28:33 +02:00
937cb39de6 Require MFA during bootstrap mode 2026-05-25 00:09:40 +02:00
56d279a8e6 Use basic auth for Authelia token exchange 2026-05-24 18:04:28 +02:00
7e22fcf3c7 bootrapping support 2026-05-24 17:03:01 +02:00
a6af43b332 fix(authelia): use adapter's own client_id/redirect_uri in AuthorizeURL
The adapter was forwarding the downstream client's client_id and
redirect_uri to Authelia, which would always be rejected — Authelia
only recognises client_id=keycape and its registered callback URI.
Also removed downstream PKCE forwarding: KeyCape is a confidential
OIDC client to Authelia and authenticates via client_secret instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 03:15:36 +00:00
847abcba73 feat: implement T19, T20 — Scenario B/C replacement tests; complete workplan
- T19: Scenario B tests — IAM swap correctness (7 tests: profile safety, client mapping, user/group preservation)
- T20: Scenario C tests — full expansion correctness (6 tests: LDIF round-trip, target differences, MFA orthogonality)
- CI scripts: test-scenario-b.sh, test-scenario-c.sh
- README: complete documentation with quick start, endpoints, migration guide
- Workplan: all acceptance criteria checked off

All 23 tasks done. 15 test packages, all green. go vet clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 02:36:29 +01:00
c18adb6441 feat: implement T22, T18, T23 — dev stack, profile tests, server binary
- T22: docker-compose.dev.yml dev stack, Dockerfile, root Makefile
- T18: Profile test suite (Scenario A) — 8 integration tests with real handlers
- T23: Server binary wiring all components, config validation, /healthz
- Config: ValidateConfig with startup validation

14 test packages pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 02:18:36 +01:00
fa27adbc77 feat: implement T16, T17 — Keycloak realm import transformer, LDIF generator
- T16: canonical → Keycloak realm JSON (profile-safe: no identity brokering, implicit flow always false)
- T17: canonical → LDIF for openldap/389ds/ad targets with pre-validation

27 migration tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 02:13:04 +01:00
3ee8090a98 feat: implement T09, T15, T21 — userinfo endpoint, LLDAP export, negative tests
- T09: /userinfo with RS256 JWT validation, scope-filtered claims
- T15: LLDAP→canonical export tool with validation, migration_event telemetry
- T21: Negative test suite (Scenario D) — all 7 unsupported features verified

All go tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 02:08:03 +01:00
4097a7de8b feat: implement T06, T07 — authorization endpoint, token endpoint
- T06: /authorize with full PKCE validation, Authelia delegation, MFA check
- T07: /token with RS256 JWT issuance (stdlib only), PKCE verification, scope-filtered claims

50 OIDC tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:56:57 +01:00
d05c73dc19 feat: implement T11, T12 — Authelia adapter, privacyIDEA adapter
- T11: AutheliaAdapter delegating login UI and session; Authelia tokens never leak to profile layer
- T12: PrivacyIDEAAdapter delegating MFA 100% — no MFA logic in KeyCape

21 adapter tests pass, vet clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:50:31 +01:00
b0adbc5daa feat: implement T14, T10 — enforcement middleware, LLDAP adapter
- T14: Unsupported feature registry with 7 pre-registered profile boundaries
- T10: LLDAP adapter implementing UserRepository; validator-gated reads

24 tests pass, go vet clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:45:21 +01:00
22f7a7dc50 feat: implement T05, T08, T13 — OIDC discovery, JWKS, telemetry pipeline
- T05: /.well-known/openid-configuration — profile-only features advertised
- T08: /jwks — RS256 JWK Set, stdlib crypto only, key rotation support
- T13: Structured telemetry — Event types, LogEmitter/NoopEmitter/MultiEmitter, context helpers

38 server tests pass, go vet clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 01:35:34 +01:00
329e996619 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