QONTO-WP-0004: mark T03/T04 done
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
1f0f979e36
commit
691ec85ad5
1 changed files with 39 additions and 29 deletions
|
|
@ -80,51 +80,61 @@ proven not to affect ordinary policy-deny traffic.
|
|||
|
||||
```task
|
||||
id: QONTO-WP-0004-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "f02b1cc0-4afc-44e5-b71d-545a13a3e12b"
|
||||
```
|
||||
|
||||
Verify `key-cape`-issued IAM Profile tokens at the request boundary (facade
|
||||
or assistant, depending on where T05 lands the auth check) in place of
|
||||
`QONTO_ASSISTANT_MCP_TOKEN`. Derive `X-Actor-*`-equivalent claims from the
|
||||
verified token instead of trusting self-asserted headers — closing the gap
|
||||
`docs/mcp-integration.md`'s auth-model section calls out explicitly.
|
||||
**2026-07-24:** No `key-cape`-side coordination was actually needed — its
|
||||
`/jwks` is a standard RS256 endpoint. `KeyCapeTokenVerifier`
|
||||
(`src/qonto_assistant/key_cape_auth.py`) fetches/caches signing keys over
|
||||
httpx, validates `iss`/`aud`/`exp` and the IAM Profile v0.3 required claims,
|
||||
and derives `ActorClaims` from the token. Wired into
|
||||
`auth.actor_claims_from_headers` (the single seam both REST and MCP use); a
|
||||
verified bearer token now takes precedence over self-asserted `X-Actor-*`
|
||||
headers, and can be made mandatory via `QONTO_KEY_CAPE_REQUIRED` once real
|
||||
tokens are issued to callers. Off by default (no
|
||||
`QONTO_KEY_CAPE_JWKS_URL` set). `QONTO_ASSISTANT_MCP_TOKEN` remains a
|
||||
documented local-dev/legacy fallback per `docs/mcp-integration.md`.
|
||||
|
||||
**Depends on:** `key-cape` exposing a client-verifiable token/JWKS surface
|
||||
this service can validate against — needs coordination with `key-cape`, not
|
||||
something this repo can complete alone.
|
||||
|
||||
Done when: a request bearing a valid key-cape token is accepted with
|
||||
claims derived from the token; the shared-secret bearer path is
|
||||
demoted to fixture/local-dev-only, matching what `docs/mcp-integration.md`
|
||||
already documents as the intended boundary.
|
||||
Verified: 13 new tests using a real generated RSA keypair + JWKS served
|
||||
over `httpx.MockTransport` (valid/expired/wrong-audience/wrong-issuer/
|
||||
missing-claim/unknown-key/rotated-key tokens, plus the auth.py precedence
|
||||
and required-vs-optional paths).
|
||||
|
||||
## Task: Register and enforce `finance.qonto.read` in flex-auth
|
||||
|
||||
```task
|
||||
id: QONTO-WP-0004-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "fce7bf0e-674a-461f-8f2f-42d7dde3a722"
|
||||
```
|
||||
|
||||
Register `finance.qonto.read` as a `flex-auth` resource. Call `flex-auth`
|
||||
for a live decision on this resource rather than relying on
|
||||
`QONTO_ASSISTANT_ENFORCE_SCOPE`'s current cached-claim check — mirroring
|
||||
`tenant-engine`'s own reasoning that a stale grant is not an acceptable risk
|
||||
for money-adjacent actions, applied here to read access. Replace the
|
||||
hardcoded `default_tenant_id="binky"` assumption in `config.py` with a live
|
||||
`tenant-engine` capability-role + plan lookup (`VEN`/`CUS`) so a lapsed
|
||||
tenant loses read access on the next request, not whenever a cache expires.
|
||||
**2026-07-24:** Registered `qonto-assistant` in `flex-auth`
|
||||
(`flex-auth/examples/qonto-assistant/`, modeled directly on
|
||||
`examples/tenant-engine/`): one resource type (`finance-snapshot`), one
|
||||
action (`finance.qonto.read`), a Rego policy gating on
|
||||
`resource.system` + `action` + `subject.type` + tenant match. Tenant
|
||||
capability-role/plan liveness (`VEN`/`CUS`) is deliberately *not* encoded in
|
||||
that policy — kept as a separate `tenant-engine` live-lookup check, per
|
||||
both repos' policy-scope notes.
|
||||
|
||||
**Depends on:** `flex-auth` resource registration and a `tenant-engine`
|
||||
lookup/cache API this repo can call — both external, not something this
|
||||
repo can complete alone.
|
||||
`LiveAuthorizationGate` (`src/qonto_assistant/live_authorization.py`)
|
||||
combines `FlexAuthCheckClient.is_allowed()` (live `POST /v1/check`) and
|
||||
`TenantEngineClient.active_roles()` (live `GET
|
||||
/tenants/{id}/roles/live`, gated on `QONTO_TENANT_ENGINE_REQUIRED_ROLES`,
|
||||
default `VEN,CUS`) and is wired into `CapabilityService._execute` ahead of
|
||||
the internal policy kernel. Both clients fail closed by construction.
|
||||
Replaces `QONTO_ASSISTANT_ENFORCE_SCOPE`'s cached-claim check as the live
|
||||
authorization control point; off by default (no `QONTO_FLEX_AUTH_URL` set).
|
||||
|
||||
Done when: an unauthorized tenant/role is denied by a live `flex-auth`
|
||||
decision, not a locally cached scope check; policy tests cover both the
|
||||
allow and the newly-live-checked deny path.
|
||||
Verified beyond mocked unit tests: ran a real `flex-auth serve` loaded with
|
||||
the registered policy (`flex-auth test-policy`/`load-registry`/`check` all
|
||||
pass) and a real `tenant-engine` instance seeded with a `VEN` grant for
|
||||
`tenant:friendly:binky`, and exercised this repo's actual clients against
|
||||
both live processes over real HTTP — `allow` for the correct tenant,
|
||||
`live_authz_denied` for a mismatched one. 28 new unit tests.
|
||||
|
||||
## Task: Facade / scale-to-zero activator — design and reference implementation
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue