Plan tenant-engine lifecycle policy actions
TEN-WP-0005 added tenant.update / tenant.retire / tenant.reactivate to tenant-engine's write API. FLEX-WP-0008's policy package predates them, so every lifecycle mutation resolves to deny unknown_action -- correct fail-closed, but it blocks TEN-WP-0005-T05 rollout and USER-WP-0021. Requested by tenant-engine; action strings taken verbatim from the live _RESOURCE_TYPES mapping. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
8fc5a1a582
commit
8ba1755718
1 changed files with 165 additions and 0 deletions
165
workplans/FLEX-WP-0010-tenant-lifecycle-policy-actions.md
Normal file
165
workplans/FLEX-WP-0010-tenant-lifecycle-policy-actions.md
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
---
|
||||
id: FLEX-WP-0010
|
||||
type: workplan
|
||||
title: "Authorize tenant-engine lifecycle actions"
|
||||
domain: infotech
|
||||
repo: flex-auth
|
||||
status: ready
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
planning_priority: P1
|
||||
planning_order: 100
|
||||
depends_on_workplans:
|
||||
- FLEX-WP-0008
|
||||
related_workplans:
|
||||
- TEN-WP-0005
|
||||
- USER-WP-0021
|
||||
created: "2026-08-10"
|
||||
updated: "2026-08-10"
|
||||
---
|
||||
|
||||
# FLEX-WP-0010 - Authorize tenant-engine lifecycle actions
|
||||
|
||||
Extend the existing `tenant-engine` policy package (FLEX-WP-0008-T02) with the
|
||||
three tenant lifecycle actions `TEN-WP-0005` introduced. Until they exist here,
|
||||
every lifecycle mutation resolves to `deny` with `unknown_action` — correct
|
||||
fail-closed behaviour, not a defect, but it means tenant-engine's new update
|
||||
and retirement endpoints cannot perform a single write in production.
|
||||
|
||||
**Requested by:** `tenant-engine` (TEN-WP-0005, T01–T04 complete and merged).
|
||||
**Blocks:** `TEN-WP-0005-T05` (production rollout), and through it
|
||||
`USER-WP-0021` (user-engine platform operator UI/API).
|
||||
|
||||
## Requirements from tenant-engine
|
||||
|
||||
Three new actions, all on the existing `tenant` resource type, all against
|
||||
`system: "tenant-engine"`:
|
||||
|
||||
| Action | Resource type | HTTP surface |
|
||||
|---|---|---|
|
||||
| `tenant.update` | `tenant` | `PATCH /tenants/{tenant_id}` |
|
||||
| `tenant.retire` | `tenant` | `POST /tenants/{tenant_id}/retire` |
|
||||
| `tenant.reactivate` | `tenant` | `POST /tenants/{tenant_id}/reactivate` |
|
||||
|
||||
These strings are already live in
|
||||
`tenant-engine/src/tenant_engine/authz.py`'s `_RESOURCE_TYPES` mapping —
|
||||
they are the exact values `FlexAuthWriteAuthorizer` sends. Match them
|
||||
verbatim; do not let the two repos invent different strings for the same
|
||||
action (the coordination rule FLEX-WP-0008-T01 already established).
|
||||
|
||||
The `CheckRequest` shape is unchanged from FLEX-WP-0008: same
|
||||
`FlexAuthCheckClient`, same `subject_type: "service"`, `resource_id` =
|
||||
`tenant_id`. No new request fields, no schema change.
|
||||
|
||||
**Why three actions rather than one `tenant.write`:** tenant-engine
|
||||
deliberately separated them so policy *can* permit an operator to rename a
|
||||
tenant without thereby permitting them to retire it. Consumer contract:
|
||||
`tenant-engine/docs/tenant-lifecycle-api.md`.
|
||||
|
||||
**Honest caveat on what this buys today:** under FLEX-WP-0008-T02's current
|
||||
model there is exactly one known operator (the `tenant-engine` service
|
||||
identity) and no `assurance` claim, so all three actions will resolve
|
||||
identically once added. The separation is a seam for later, not
|
||||
differentiated authorization now. Adding the actions is what unblocks
|
||||
production; differentiating them is T02 below, and may well close as "not
|
||||
yet, and here is why".
|
||||
|
||||
## T01 - Extend the policy package with the lifecycle actions
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0010-T01
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Add `tenant.update`, `tenant.retire`, and `tenant.reactivate` to
|
||||
`examples/tenant-engine/policy_package.md`'s `valid_actions` set and to the
|
||||
frontmatter `actions:` list. Extend the `caring.capabilities` list if the
|
||||
existing vocabulary (`Create`/`Grant`/`Revoke`/`Bind`/`Audit`) does not
|
||||
already cover an update/retire/reactivate shape — a judgement call for this
|
||||
repo, not pre-specified here.
|
||||
|
||||
Update `examples/tenant-engine/subject_manifest.yaml`'s subject metadata
|
||||
description, which currently enumerates the original four actions.
|
||||
|
||||
Update `docs/tenant-engine-action-vocabulary.md` with the three actions, their
|
||||
resource type, and the HTTP surface each gates. Keep the existing note that
|
||||
this policy governs *who may call tenant-engine's admin API*, not a tenant's
|
||||
own `PLTF`/`IAM`/`VEN`/`CUS` capability roles — the lifecycle actions do not
|
||||
change that boundary and should not be read as doing so.
|
||||
|
||||
Rebuild `examples/tenant-engine/registry_snapshot.json`.
|
||||
|
||||
Done when `valid_actions` carries all seven actions and the vocabulary doc
|
||||
and subject manifest no longer describe only four.
|
||||
|
||||
## T02 - Decide whether retirement warrants stricter authorization
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0010-T02
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
`tenant.retire` is the highest-consequence action in the set: it suspends a
|
||||
tenant's ability to receive new capability grants and plan changes across the
|
||||
whole platform. Decide whether it should require anything `tenant.update` does
|
||||
not — a distinct subject, group, or (once available) assurance level.
|
||||
|
||||
Record the decision with its reasoning, in the style of FLEX-WP-0008-T02's
|
||||
closure note. "No, not until `KEY-WP-0005` gives callers a real
|
||||
`assurance`-bearing identity to check" is a perfectly good outcome — the point
|
||||
is that the decision is explicit and recorded rather than defaulted into by
|
||||
treating all seven actions as interchangeable.
|
||||
|
||||
Note that retirement is **reversible** by design (`tenant.reactivate`), and
|
||||
that tenant-engine hard-deletes nothing — so the blast radius of a wrongly
|
||||
allowed retirement is recoverable, which is legitimate input to this decision.
|
||||
|
||||
Done when the decision and its reasoning are recorded in
|
||||
`policy_package.md`, and implemented if the answer is "yes, stricter".
|
||||
|
||||
## T03 - Fixtures and verification
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0010-T03
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Add `allow`/`deny` fixture pairs to
|
||||
`examples/tenant-engine/policy_fixtures.yaml` for each of the three actions,
|
||||
mirroring the existing eight pairs' shape. Cover at minimum: authorized
|
||||
service subject → `allow`; unknown subject → `deny unknown_subject`; and a
|
||||
misspelled lifecycle action → `deny unknown_action` (the case that guards
|
||||
against the two repos drifting apart on action strings).
|
||||
|
||||
Verify the way FLEX-WP-0008-T02 did, against the real binary rather than in
|
||||
isolation: `go build ./cmd/flex-auth`, then `test-policy` (all Rego tests and
|
||||
fixtures), `load-registry`, and `check` against standalone request files.
|
||||
|
||||
Then close the loop end-to-end, which is what `TEN-WP-0005-T05` actually needs
|
||||
evidence of: run a live `flex-auth serve` with this registry and point a real
|
||||
`tenant-engine` at it (`TENANT_ENGINE_FLEX_AUTH_URL`), then exercise
|
||||
`PATCH /tenants/{id}`, `POST .../retire`, and `POST .../reactivate` through
|
||||
the unmodified `FlexAuthWriteAuthorizer`. tenant-engine's lifecycle endpoints
|
||||
require `Idempotency-Key` and `If-Match` headers — read the record first and
|
||||
echo its `ETag` back; `tenant-engine/docs/tenant-lifecycle-api.md` has the
|
||||
call shapes.
|
||||
|
||||
Done when all three lifecycle mutations return a real `allow` from the real
|
||||
Rego engine over real HTTP, `go test ./...` is green across the repo, and
|
||||
`gofmt`/`go vet` are clean.
|
||||
|
||||
## T04 - Closure and handoff
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0010-T04
|
||||
status: todo
|
||||
priority: low
|
||||
```
|
||||
|
||||
Confirm T01–T03. Run `statehub fix-consistency`. Notify `tenant-engine` that
|
||||
`TEN-WP-0005-T05` is unblocked, naming the policy revision — T05's own
|
||||
done-criteria require the handoff to name the authorization policy revision
|
||||
alongside the immutable image and API version.
|
||||
Loading…
Add table
Add a link
Reference in a new issue