FLEX-WP-0008 T03-T04: tenant-engine live-lookup context adapter, close
internal/adapters/tenantengine: HTTPClient.LiveRoles() calls tenant-engine's
GET /tenants/{id}/roles/live. LiveRolesResult.Available is the load-bearing
field -- false on any transport error, non-200, or malformed body, never
inferred as zero roles. AttachToContext() writes both tenant_roles and
tenant_roles_available into a CheckRequest.Context map; a consuming Rego
policy must check tenant_roles_available before trusting tenant_roles.
Architectural finding recorded in the workplan: engine.go's Check() has no
context-adapter hook, and none of the existing topaz/relationship/rule
adapters are wired into cmd/flex-auth either -- they're standalone packages
for downstream composition. This adapter is a request-preparation helper a
protected system's own request-building code calls before POST /v1/check,
not an engine-internal hook, matching that precedent exactly.
9 Go tests; gofmt/vet/build clean; go test ./... green repo-wide.
Verified as a real three-service chain: live flex-auth serve + live
tenant-engine, created a tenant and granted it a CUS role through the real
flex-auth-gated write path, then read it back through this adapter (via a
throwaway harness, not committed) -- known tenant: roles=[CUS]
available=true; unknown tenant: roles=[] available=false err="status 404".
FLEX-WP-0008 closed: T01-T04 all done. tenant-engine's write path is now
real end-to-end; any other protected system can pull live tenant capability
role context, fail-closed. Remaining open item unchanged from TEN-WP-0003:
KEY-WP-0005 (key-cape's IAM Profile core-claims gap).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
2ea84d240b
commit
d2dc39a8b8
5 changed files with 372 additions and 3 deletions
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "tenant-engine Consumer Integration"
|
||||
domain: infotech
|
||||
repo: flex-auth
|
||||
status: ready
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: flex-auth
|
||||
planning_priority: P1
|
||||
|
|
@ -138,7 +138,7 @@ HTTP, real Rego evaluation. `go test ./...` still green across the whole
|
|||
|
||||
```task
|
||||
id: FLEX-WP-0008-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "20c005a4-48b7-4ac4-a345-aeaa0de06d80"
|
||||
```
|
||||
|
|
@ -157,11 +157,49 @@ 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.
|
||||
|
||||
**Done 2026-07-23, one architectural finding recorded first:** confirmed by
|
||||
reading `internal/decision/engine.go` that `Check()` has no context-adapter
|
||||
hook at all, and confirmed via `cmd/flex-auth/main.go` that none of the
|
||||
existing `topaz`/`relationship`/`rule` adapters are wired into the shipped
|
||||
binary either — they're standalone Go packages for downstream composition,
|
||||
not auto-invoked plugins. This adapter follows the same shape: Rego
|
||||
evaluation is stateless and can't make an HTTP call mid-evaluation, so
|
||||
`internal/adapters/tenantengine` is a **request-preparation helper** a
|
||||
protected system's own request-building code calls before submitting to
|
||||
`POST /v1/check` — not an engine-internal hook.
|
||||
|
||||
`HTTPClient.LiveRoles(ctx, tenantID) (LiveRolesResult, error)` calls
|
||||
`GET {base}/tenants/{id}/roles/live`. `LiveRolesResult.Available` is the
|
||||
load-bearing field: `false` on *any* failure (transport error, non-200,
|
||||
malformed body) — never inferred as zero roles.
|
||||
`AttachToContext(context, result)` writes `tenant_roles` **and**
|
||||
`tenant_roles_available` into a `CheckRequest.Context` map; the
|
||||
`Available: false` / `tenant_roles_available: false` pairing is what a
|
||||
consuming policy must check before trusting `tenant_roles` at all —
|
||||
documented in the package doc comment as the required Rego precondition.
|
||||
|
||||
9 Go tests (`http_client_test.go`, `context_test.go`): success, non-200,
|
||||
malformed body, connection failure, context-timeout, empty base URL
|
||||
rejected, context attachment (including nil-context and
|
||||
non-clobbering-existing-fields cases). `gofmt`/`go vet`/`go build ./...`
|
||||
clean; `go test ./...` still green across the whole repo.
|
||||
|
||||
**Verified as a real three-service chain, not a mock:** ran a live
|
||||
`flex-auth serve` (T02's registry+policy) and a live `tenant-engine`
|
||||
pointed at it (`TENANT_ENGINE_FLEX_AUTH_URL`), created a tenant and granted
|
||||
it a `CUS` role through the real, `flex-auth`-gated write path, then called
|
||||
this new Go adapter (via a throwaway `cmd/` harness, removed after use, not
|
||||
committed) against the running `tenant-engine`: `roles=[CUS]
|
||||
available=true` for the known tenant, `roles=[] available=false
|
||||
err="status 404"` for an unknown one — the exact fail-closed distinction
|
||||
this task exists to guarantee, proven end-to-end across Go → Python → Go
|
||||
(via HTTP), not asserted in isolation.
|
||||
|
||||
## Task: Closure review
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0008-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "0c59ada6-c61b-41a3-8baa-a98e936c5690"
|
||||
```
|
||||
|
|
@ -172,3 +210,25 @@ 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`.
|
||||
|
||||
**Closed 2026-07-23.** T01–T03 done. `go test ./...` green across the whole
|
||||
repo (including the new `internal/adapters/tenantengine` package);
|
||||
`gofmt`/`go vet` clean.
|
||||
|
||||
The re-verification this task asked for happened twice, for real: in T02's
|
||||
closure (a live `flex-auth serve` + `tenant-engine`'s unmodified
|
||||
`FlexAuthWriteAuthorizer`, `allow` → `201`) and again in T03's closure (the
|
||||
same live pair, plus a write through it, plus the new Go adapter reading
|
||||
the result back) — `FlexAuthWriteAuthorizer` was never re-tested against a
|
||||
mock standing in for `allow`; every `allow` assertion in this workplan came
|
||||
from the real Rego engine.
|
||||
|
||||
**Full picture after this workplan:** `tenant-engine`'s write path is real
|
||||
end-to-end (`tenant-engine` → real `flex-auth` → real `allow`/`deny`), and
|
||||
any *other* protected system can now pull live tenant capability-role
|
||||
context via `internal/adapters/tenantengine`, fail-closed. What's still
|
||||
open, unchanged from `TEN-WP-0003`'s own closure: `KEY-WP-0005` (`key-cape`
|
||||
doesn't emit IAM Profile core claims yet — the cache-read/`tenant_roles`
|
||||
direction depends on it, not this workplan). This workplan's own T02 policy
|
||||
(operator/service identity, not `aal2`+role) should be revisited once
|
||||
`KEY-WP-0005` gives real caller assurance to check.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue