Adapt USER-WP-0021 and USER-WP-0023 to published policy-nexus contracts
IAM Profile v0.3 and Tenancy Posture v0.1 are now live on policy.coulomb.social. Close the portal expansion workplan against those documents, keep the flex-auth live A2 probe waiting, and forward optional tenant_roles to flex-auth without authorizing from them locally.
This commit is contained in:
parent
166788228d
commit
f762161d84
13 changed files with 109 additions and 10 deletions
|
|
@ -4,7 +4,7 @@ description: >
|
|||
tenant, membership, profile, lifecycle, and evidence-facing context.
|
||||
status: candidate
|
||||
owner: codex
|
||||
updated: "2026-06-05"
|
||||
updated: "2026-08-19"
|
||||
|
||||
implements:
|
||||
- identity-canon conceptual model as an implementation-facing domain facade
|
||||
|
|
@ -25,9 +25,10 @@ produces:
|
|||
- Access Grant or grant-like membership fact
|
||||
|
||||
consumes:
|
||||
- NetKingdom IAM Profile claims
|
||||
- NetKingdom IAM Profile v0.3 claims
|
||||
- verified issuer and subject identifiers
|
||||
- assurance and principal type claims
|
||||
- optional tenant_roles forwarded to flex-auth only
|
||||
- authorization decisions and obligations
|
||||
- policy, control, review, exception, and evidence references
|
||||
- lifecycle task references from downstream task systems
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
|
||||
Status: caller side implemented and deployed; live proof pending flex-auth A2 promotion (FLEX-WP-0015-T02).
|
||||
|
||||
Governing published policy (2026-08-19):
|
||||
|
||||
- IAM Profile v0.3 Service Account Flow — audience-scoped, rotating
|
||||
workload identity:
|
||||
`https://policy.coulomb.social/standards/iam-profile/v0.3/`
|
||||
- Tenancy Posture v0.1 A2 — inbound caller choke point:
|
||||
`https://policy.coulomb.social/standards/tenancy-posture/v0.1/`
|
||||
|
||||
user-engine calls `flex-auth-user-engine` with a projected Kubernetes
|
||||
ServiceAccount token whose audience is exactly `flex-auth`. The adapter reads
|
||||
the token file for every decision so hourly projection rotation requires no
|
||||
|
|
@ -77,7 +85,9 @@ BODY='{"actor":{"issuer":"https://kc.coulomb.social","subject":"probe",
|
|||
3. **user-engine cannot represent another protected system.** Re-run step 1
|
||||
with `resource.system` set to `tenant-engine`. Expect a refusal from the
|
||||
binding, not an allow: the token binds `user-engine` to
|
||||
`system:serviceaccount:user-engine:user-engine` only.
|
||||
`system:serviceaccount:user-engine:user-engine` only. That binding is the
|
||||
A2 choke point named by Tenancy Posture v0.1; publication does not change
|
||||
the remaining operator sequence.
|
||||
|
||||
Record all three results, the flex-auth digest they ran against, and the
|
||||
user-engine digest, in USER-WP-0023-T03 before marking it done. A pass on
|
||||
|
|
|
|||
|
|
@ -13,6 +13,16 @@ See also the cross-repo assessment in the net-kingdom repo:
|
|||
`/home/worsch/net-kingdom/docs/user-engine-netkingdom-integration-assessment.md`
|
||||
(for current intent/scope fit, gaps, and recommendations as of 2026-06-03).
|
||||
|
||||
Published governing contracts (policy-nexus, 2026-08-19):
|
||||
|
||||
- IAM Profile v0.3: `https://policy.coulomb.social/standards/iam-profile/v0.3/`
|
||||
- Tenancy Posture v0.1: `https://policy.coulomb.social/standards/tenancy-posture/v0.1/`
|
||||
- NetKingdom architecture: `https://policy.coulomb.social/architecture/net-kingdom/v0.1/`
|
||||
|
||||
The user-engine boundary contract remains accepted in net-kingdom
|
||||
(`canon/standards/user-engine-boundary-contract_v0.1.md`) and is marked
|
||||
`publish` in the policy-nexus ledger, but it is not yet on the site.
|
||||
|
||||
## Required Ports
|
||||
|
||||
| Port | Direction | Purpose |
|
||||
|
|
@ -55,6 +65,7 @@ actor:
|
|||
tenant: string
|
||||
principal_type: human | service | agent
|
||||
roles: [string]
|
||||
tenant_roles: [string]
|
||||
groups: [string]
|
||||
scopes: [string]
|
||||
assurance: object
|
||||
|
|
|
|||
|
|
@ -46,4 +46,7 @@ as externally complete. Compensation is idempotent and operator-visible.
|
|||
Enterprise SAML/OIDC federation, SCIM and corporate directory synchronization
|
||||
remain provider adapters. Their external ownership metadata and identity links
|
||||
must fit the existing neutral boundaries rather than becoming user-engine
|
||||
domain dependencies.
|
||||
domain dependencies. The published target is IAM Profile v0.3 Keycloak
|
||||
expanded mode
|
||||
(`https://policy.coulomb.social/standards/iam-profile/v0.3/`); a dedicated
|
||||
workplan is created only when a tenant demands those integrations.
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class VerifiedIdentityClaimsAdapter:
|
|||
principal_type=PrincipalType(str(claims["principal_type"])),
|
||||
audience=audience,
|
||||
roles=_strings(claims.get("roles", ())),
|
||||
tenant_roles=_strings(claims.get("tenant_roles", ())),
|
||||
groups=_strings(claims.get("groups", ())),
|
||||
scopes=_strings(scopes),
|
||||
assurance=dict(claims.get("assurance", {})),
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class FlexAuthHTTPAdapter:
|
|||
"attributes": {
|
||||
"issuer": request.actor.issuer,
|
||||
"roles": list(request.actor.roles),
|
||||
"tenant_roles": list(request.actor.tenant_roles),
|
||||
"groups": list(request.actor.groups),
|
||||
"scopes": list(request.actor.scopes),
|
||||
"assurance": dict(request.actor.assurance),
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ class Actor:
|
|||
principal_type: PrincipalType
|
||||
audience: tuple[str, ...]
|
||||
roles: tuple[str, ...] = ()
|
||||
tenant_roles: tuple[str, ...] = ()
|
||||
groups: tuple[str, ...] = ()
|
||||
scopes: tuple[str, ...] = ()
|
||||
assurance: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class FixtureIdentityClaimsAdapter:
|
|||
principal_type=PrincipalType(str(claims["principal_type"])),
|
||||
audience=tuple(_as_tuple(claims.get("aud", ()))),
|
||||
roles=tuple(_as_tuple(claims.get("roles", ()))),
|
||||
tenant_roles=tuple(_as_tuple(claims.get("tenant_roles", ()))),
|
||||
groups=tuple(_as_tuple(claims.get("groups", ()))),
|
||||
scopes=tuple(_as_tuple(scopes)),
|
||||
assurance=dict(claims.get("assurance", {})),
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class PlatformAdapterTests(unittest.TestCase):
|
|||
request = json.loads(call.call_args.args[0].data)
|
||||
self.assertEqual(request["resource"]["system"], "user-engine")
|
||||
self.assertEqual(request["context"]["self"], True)
|
||||
self.assertEqual(request["subject"]["attributes"]["tenant_roles"], ["CUS"])
|
||||
|
||||
def test_flex_auth_fails_closed_when_unavailable(self):
|
||||
with patch("user_engine.adapters.flex_auth.urlopen", side_effect=URLError("down")):
|
||||
|
|
@ -155,7 +156,7 @@ def _request():
|
|||
actor = Actor(
|
||||
issuer="https://issuer", subject="subject-1", tenant="tenant-a",
|
||||
principal_type=PrincipalType.HUMAN, audience=("user-engine",),
|
||||
roles=("tenant-admin",),
|
||||
roles=("tenant-admin",), tenant_roles=("CUS",),
|
||||
)
|
||||
return AuthorizationRequest(
|
||||
actor=actor, resource_type="user-engine:user", resource_id="user-1",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ class VerifiedIdentityClaimsAdapterTests(unittest.TestCase):
|
|||
actor = self.adapter.normalize(self.claims)
|
||||
self.assertEqual("person-1", actor.subject)
|
||||
self.assertEqual(("tenant-admin",), actor.roles)
|
||||
self.assertEqual((), actor.tenant_roles)
|
||||
|
||||
def test_optional_tenant_roles_are_preserved_without_local_authorization(self):
|
||||
actor = self.adapter.normalize({**self.claims, "tenant_roles": ["CUS", "VEN"]})
|
||||
self.assertEqual(("CUS", "VEN"), actor.tenant_roles)
|
||||
self.assertEqual(("tenant-admin",), actor.roles)
|
||||
|
||||
def test_rejects_wrong_issuer_and_audience(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ Date: 2026-05-22
|
|||
Related product docs: `../INTENT.md`, `ProductRequirementsDocument.md`
|
||||
Primary platform references:
|
||||
|
||||
- `/home/worsch/net-kingdom/canon/standards/iam-profile_v0.2.md`
|
||||
- `https://policy.coulomb.social/standards/iam-profile/v0.3/` (source: `/home/worsch/net-kingdom/canon/standards/iam-profile_v0.3.md`; v0.2 is superseded)
|
||||
- `https://policy.coulomb.social/standards/tenancy-posture/v0.1/`
|
||||
- `https://policy.coulomb.social/architecture/net-kingdom/v0.1/`
|
||||
- `/home/worsch/net-kingdom/docs/platform-identity-security-architecture.md`
|
||||
- `/home/worsch/net-kingdom/docs/responsibility-map.md`
|
||||
- `/home/worsch/key-cape/SCOPE.md`
|
||||
|
|
@ -63,10 +65,17 @@ by flex-auth:
|
|||
- `iss`, `sub`, `aud`, `tenant`, `principal_type`
|
||||
- `groups`, `roles`, `scope` or `scp`
|
||||
- `assurance`
|
||||
- optional `tenant_roles` (IAM Profile v0.3 cached tenant capability roles;
|
||||
forwarded to flex-auth, never used as a local authorization decision;
|
||||
privileged or `aal2`-class actions must re-query `tenant-engine`)
|
||||
- optional human display/contact claims such as `preferred_username`, `email`,
|
||||
and `name`
|
||||
- optional agent/delegation claims for agentic use cases
|
||||
|
||||
A tenant's onboarding grouping (ADR-0013) and its capability roles (ADR-0014)
|
||||
are independent. The identifier's grouping segment is historical after a
|
||||
reclassification; `tenant_roles` is not encoded in the identifier.
|
||||
|
||||
`user-engine` must treat `iss + sub` as the stable identity link key. Email,
|
||||
username, or display name must never be primary keys.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ type: workplan
|
|||
title: "Expand user-engine portal beyond the proven Binky MVP"
|
||||
domain: communication
|
||||
repo: user-engine
|
||||
status: active
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-07-30"
|
||||
updated: "2026-08-16"
|
||||
updated: "2026-08-19"
|
||||
depends_on:
|
||||
- USER-WP-0020
|
||||
- TEN-WP-0005
|
||||
|
|
@ -190,7 +190,7 @@ deployed and wants its own permission.
|
|||
|
||||
```task
|
||||
id: USER-WP-0021-T05
|
||||
status: wait
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "05046780-9625-47c2-8caf-f57e9239c603"
|
||||
```
|
||||
|
|
@ -199,6 +199,24 @@ When a tenant requires SAML/OIDC federation, SCIM, directory synchronization,
|
|||
just-in-time provisioning, or customer-owned lifecycle, create a dedicated
|
||||
cross-repository plan against the provider-neutral contracts.
|
||||
|
||||
2026-08-19 policy-nexus adaptation: no tenant has demanded those
|
||||
integrations, so this workplan does not spawn one. The trigger now points
|
||||
at published contracts rather than unpublished drafts:
|
||||
|
||||
- IAM Profile v0.3 at
|
||||
`https://policy.coulomb.social/standards/iam-profile/v0.3/` — Keycloak
|
||||
expanded mode remains the interchangeable enterprise-federation
|
||||
implementation; grouping and `tenant_roles` are independent axes
|
||||
(ADR-0013 / ADR-0014). Existing v0.2 claim handling stays conformant.
|
||||
- Tenancy Posture v0.1 at
|
||||
`https://policy.coulomb.social/standards/tenancy-posture/v0.1/`.
|
||||
- NetKingdom architecture draft-2 at
|
||||
`https://policy.coulomb.social/architecture/net-kingdom/v0.1/`.
|
||||
|
||||
user-engine still does not own federation, SCIM, or directory
|
||||
synchronization. A new workplan is the right vehicle if a tenant later
|
||||
requires them. This workplan's product-expansion scope is complete.
|
||||
|
||||
## Production rollout (2026-08-08)
|
||||
|
||||
Commit `8229c6d` passed 115 tests with three external-provider skips, was
|
||||
|
|
@ -283,3 +301,17 @@ instead of receiving the documented bounded automatic retry. The query now
|
|||
selects every undelivered, non-dead-letter event; regression coverage checks
|
||||
that failed rows are not filtered out. The full suite passes 133 tests with
|
||||
three external-provider skips.
|
||||
|
||||
## Residuals after close (2026-08-19)
|
||||
|
||||
- Live tenant-lifecycle probe from a user-engine pod (GET / PATCH / retire /
|
||||
reactivate on a disposable tenant) remains owed with USER-WP-0023-T03.
|
||||
Contract tests cannot see a digest pin rollback.
|
||||
- Outbox transport activation still waits on governed OpenBao event/mail
|
||||
tokens and transactional SMTP credentials.
|
||||
- Optional `tenant_roles` is now forwarded to flex-auth. Privileged
|
||||
live re-query against tenant-engine is required only if a future policy
|
||||
treats those roles as `aal2`-class input; flex-auth currently has no
|
||||
such policy.
|
||||
- A new workplan is required if a tenant demands federation, SCIM, or
|
||||
directory synchronization.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ status: active
|
|||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-18"
|
||||
updated: "2026-08-18"
|
||||
updated: "2026-08-19"
|
||||
state_hub_workstream_id: "014d0886-b690-4860-8337-c718e440f678"
|
||||
---
|
||||
|
||||
|
|
@ -80,4 +80,26 @@ This task stays `wait` on FLEX-WP-0015-T02 promotion through FLEX-WP-0011,
|
|||
and on an operator shell with cluster credentials, which agent sessions in
|
||||
this repo do not hold.
|
||||
|
||||
2026-08-19 policy-nexus adaptation: the published contracts now name the
|
||||
same split this workplan already implemented.
|
||||
|
||||
- IAM Profile v0.3 Service Account Flow
|
||||
(`https://policy.coulomb.social/standards/iam-profile/v0.3/`) requires
|
||||
audience-scoped, rotating workload credentials delivered through the
|
||||
credential-management standard — that is the projected ServiceAccount
|
||||
token we already send. The optional `tenant_roles` claim is forwarded
|
||||
to flex-auth as a subject attribute and is never used as a local
|
||||
authorization decision.
|
||||
- Tenancy Posture v0.1
|
||||
(`https://policy.coulomb.social/standards/tenancy-posture/v0.1/`) is the
|
||||
public A2 target T03 is waiting to prove: a single inbound choke point
|
||||
that refuses an unbound caller. Our half of that vector is deployed;
|
||||
flex-auth's running digest is still A0.
|
||||
- NetKingdom architecture draft-2
|
||||
(`https://policy.coulomb.social/architecture/net-kingdom/v0.1/`) lists
|
||||
both documents in chapter 9.
|
||||
|
||||
The workplan stays `active` because T03's live proof is still owed. That
|
||||
proof is not unblocked by publication.
|
||||
|
||||
Contract: `docs/flex-auth-caller-identity.md`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue