diff --git a/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md b/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md new file mode 100644 index 0000000..eb06b92 --- /dev/null +++ b/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md @@ -0,0 +1,125 @@ +--- +id: FLEX-WP-0008 +type: workplan +title: "tenant-engine Consumer Integration" +domain: infotech +repo: flex-auth +status: ready +owner: codex +topic_slug: flex-auth +planning_priority: P1 +planning_order: 80 +depends_on_workplans: + - FLEX-WP-0007 +related_workplans: + - TEN-WP-0003 + - KEY-WP-0005 +created: "2026-07-23" +updated: "2026-07-23" +--- + +# FLEX-WP-0008: tenant-engine Consumer Integration + +## Purpose + +Make `tenant-engine` a registered protected-system consumer of flex-auth, +in both directions its boundary contract requires +(`net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md`): + +1. **tenant-engine's writes need authorization.** `tenant-engine`'s + `FlexAuthWriteAuthorizer` (`TEN-WP-0003`) calls `POST /v1/check` for + every mutation (`tenant.create`, `tenant.role.grant`, `tenant.role.revoke`, + `tenant.plan.assign`). Until a policy package exists here, every check + resolves to `deny`/`not_applicable` — correct fail-closed behavior, but + it means `tenant-engine` cannot actually perform any write yet. This + workplan is what makes real `allow` decisions reachable. +2. **flex-auth's own decisions need tenant capability roles as input**, for + any protected system whose policy depends on a tenant's `PLTF`/`IAM`/ + `VEN`/`CUS` roles (ADR-0014) at `aal2`-class decision time. flex-auth + must call `tenant-engine`'s live-lookup endpoint + (`GET /tenants/{id}/roles/live`) to enrich decision context, the same + role a `context` adapter plays for any other external fact source. + +Closer in shape to `FLEX-WP-0006`/`FLEX-WP-0007` (ops-warden's small, +security-lane SSH-certificate integration) than `FLEX-WP-0003` (Markitect's +document-heavy, CARING-benchmark integration) — `tenant-engine`'s resource +and action vocabulary is small and doesn't need CARING descriptor mapping. + +## Task: Define tenant-engine resource and action vocabulary + +```task +id: FLEX-WP-0008-T01 +status: todo +priority: high +``` + +Resource types: `tenant`, `role-grant`, `plan-assignment`. Actions: +`tenant.create`, `tenant.role.grant`, `tenant.role.revoke`, +`tenant.plan.assign` — matching `TEN-WP-0003-T02`'s action-name mapping +exactly (coordinate values, don't let the two repos invent different +strings for the same action). Model on `examples/ops-warden`'s fixture +shape (`schemas/check_request.schema.json`'s `resource{id,type,system}`, +`system: "tenant-engine"`), not Markitect's. + +Done when: resource/action vocabulary documented under `docs/` following +the `markitect-resource-namespace.md` / `markitect-action-vocabulary.md` +naming pattern (`tenant-engine-resource-namespace.md`, +`tenant-engine-action-vocabulary.md`). + +## Task: Author and register the tenant-engine policy package + +```task +id: FLEX-WP-0008-T02 +status: todo +priority: high +``` + +Policy: writes require an `aal2`+ assurance actor holding an appropriate +capability role for the action (e.g. `tenant.plan.assign` might require +`PLTF` or a platform-operator subject type; exact rules are a design +decision for this task, not pre-specified here — coordinate with ADR-0014's +role semantics, not invented fresh). Register in `registry/` per the +existing pattern; add fixtures (`allow`/`deny` request/response pairs) +mirroring `examples/ops-warden/check_request_allow_adm.json`. + +Done when: `POST /v1/check` against a request matching `TEN-WP-0003`'s +`FlexAuthCheckClient` shape returns `allow` for an authorized actor/action +and `deny` for an unauthorized one, exercised by `main_test.go`-style +integration tests. + +## Task: tenant-engine live-lookup context adapter + +```task +id: FLEX-WP-0008-T03 +status: todo +priority: medium +``` + +A context-enrichment adapter (mirrors `internal/adapters/{relationship,rule,topaz}`'s +shape) that calls `tenant-engine`'s `GET /tenants/{id}/roles/live` and +attaches the result to decision context for policies that need it — used by +*other* protected systems' policies that key off tenant capability role, +not only by tenant-engine's own policy from T02. Must fail closed: adapter +error or `tenant-engine` unavailability must not silently produce an empty +role list indistinguishable from "no roles granted" (same rule +`tenant-engine`'s own read endpoints already enforce — don't weaken it on +the consuming side). + +Done when: a policy package referencing tenant capability role context +correctly denies when the adapter call fails, not just when it succeeds +with an empty result. + +## Task: Closure review + +```task +id: FLEX-WP-0008-T04 +status: todo +priority: low +``` + +Confirm T01–T03 done; run flex-auth's existing test suite plus the new +fixtures. Note in closure: `TEN-WP-0003`'s `FlexAuthWriteAuthorizer` should +be re-verified against a real `allow` decision once this workplan's policy +package exists (it was only tested against `deny`/`not_applicable` +responses when built, since this workplan didn't exist yet). Run `statehub +fix-consistency`.