TEN-WP-0003: FlexAuthWriteAuthorizer -- gate writes through flex-auth
flex_auth.py: CheckRequest + FlexAuthCheckClient against flex-auth's real POST /v1/check contract (schemas/check_request.schema.json, decision_envelope.schema.json, read directly from the flex-auth repo, not guessed). Fail-closed by construction: only effect=="allow" authorizes; every other effect, non-200, malformed body, or transport failure resolves to deny, nothing raises past is_allowed(). authz.FlexAuthWriteAuthorizer implements the existing WriteAuthorizer Protocol. Action -> resource-type mapping coordinated with FLEX-WP-0008's planned vocabulary (both repos reference the same table). DefaultDenyWriteAuthorizer stays the fallback when no flex-auth URL is configured. config.py: Settings.from_env(), mirroring qonto-assistant's pattern. docs/flex-auth-integration.md documents the contract, fail-closed rule, and current real state (denies everything until FLEX-WP-0008 lands). 60 tests passing. Verified live twice over real HTTP between separate processes (not just MockTransport): a deny-returning flex-auth double produces 403 from POST /tenants, an allow-returning one produces 201. Also registered (not implemented) the two workplans this depends on for a complete picture: flex-auth/FLEX-WP-0008 (protected-system registration -- what makes allow reachable) and key-cape/KEY-WP-0005 (discovered key-cape emits none of iam-profile_v0.3.md's core claims yet, not just missing tenant_roles -- a bigger, security-sensitive gap flagged rather than quietly worked around). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
0b37f792a1
commit
5d57c7d488
13 changed files with 648 additions and 4 deletions
68
docs/flex-auth-integration.md
Normal file
68
docs/flex-auth-integration.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# flex-auth Integration (TEN-WP-0003)
|
||||
|
||||
`tenant-engine` gates every write through flex-auth's `POST /v1/check`
|
||||
(`flex_auth.FlexAuthCheckClient`, wired in as `authz.FlexAuthWriteAuthorizer`).
|
||||
|
||||
## What tenant-engine sends
|
||||
|
||||
A `CheckRequest` per `flex-auth/schemas/check_request.schema.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "check:<uuid>",
|
||||
"tenant": "tenant:friendly:binky",
|
||||
"subject": {"id": "<actor>", "type": "service"},
|
||||
"action": "tenant.create",
|
||||
"resource": {"id": "<tenant_id>", "type": "tenant", "system": "tenant-engine"}
|
||||
}
|
||||
```
|
||||
|
||||
Action → resource-type mapping (must match `flex-auth`'s
|
||||
`FLEX-WP-0008-T01` vocabulary exactly — coordinate values, don't diverge):
|
||||
|
||||
| Action | Resource type |
|
||||
| --- | --- |
|
||||
| `tenant.create` | `tenant` |
|
||||
| `tenant.role.grant` | `role-grant` |
|
||||
| `tenant.role.revoke` | `role-grant` |
|
||||
| `tenant.plan.assign` | `plan-assignment` |
|
||||
|
||||
## What tenant-engine expects back
|
||||
|
||||
A `DecisionEnvelope` per `flex-auth/schemas/decision_envelope.schema.json`.
|
||||
Only `effect: "allow"` authorizes the write. Every other `effect`
|
||||
(`deny`/`redact`/`audit_only`/`not_applicable`), a non-200 response, a
|
||||
malformed body, or a transport failure/timeout all resolve to **deny** —
|
||||
`FlexAuthCheckClient.is_allowed()` never raises past its own boundary; it
|
||||
always returns a plain `bool`.
|
||||
|
||||
## The current real state
|
||||
|
||||
Until `flex-auth/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md`
|
||||
lands (resource/action vocabulary + an authored policy package), **every
|
||||
check resolves to deny or not_applicable** — verified against real
|
||||
`flex-auth` behavior, not assumed. This is correct fail-closed behavior,
|
||||
not a bug: tenant-engine cannot perform any write against a real `flex-auth`
|
||||
deployment until that policy package exists. Verified locally with a fake
|
||||
HTTP double standing in for `flex-auth` (deny → `403`, allow → `201`,
|
||||
both over real HTTP between two processes) — see `TEN-WP-0003`'s closure
|
||||
notes for the exact commands.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Env var | Default | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `TENANT_ENGINE_FLEX_AUTH_URL` | unset | Base URL of a reachable `flex-auth` deployment. When unset, `create_app()` falls back to `authz.DefaultDenyWriteAuthorizer` — no writes ever succeed, which is the correct posture for local/test runs that have no `flex-auth` to call. |
|
||||
| `TENANT_ENGINE_FLEX_AUTH_TIMEOUT_SECONDS` | `3` | Bounded timeout on the synchronous write path — no retries, so a slow deny doesn't become a hang. |
|
||||
|
||||
## Related
|
||||
|
||||
- `flex-auth/workplans/FLEX-WP-0008-tenant-engine-consumer-integration.md`
|
||||
— the flex-auth-side work this client depends on for real `allow`
|
||||
decisions, and (separately) flex-auth's own live-lookup consumption of
|
||||
`tenant-engine`'s `/roles/live` endpoint for other protected systems.
|
||||
- `key-cape/workplans/KEY-WP-0005-iam-profile-core-claims.md` — the
|
||||
cache-read/`tenant_roles` direction (`key-cape` → `tenant-engine`), not
|
||||
covered by this doc.
|
||||
- `net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md` —
|
||||
the Authorization Contract section this client implements.
|
||||
Loading…
Add table
Add a link
Reference in a new issue