All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 37s
Engine/PIP declaration is now checkable (layer.yaml plus a Tooling-client scan). Writes persist a decision record or the published fail-closed stance, live-lookup freshness is published, events_for is tenant-scoped, and mutation evidence drains to audit-core from a local outbox without blocking the mutation. Sender registration is requested as AUDIT-IN-0002. Boundary-contract amendment is requested as NET-IN-0002. Assistant: grok Assistant-Session: 01a04cea-e5e8-7081-a0fc-808ebbc35fa9
91 lines
3.3 KiB
Python
91 lines
3.3 KiB
Python
"""TEN-WP-0011-T03: PIP claim freshness and live-lookup non-reentry."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
|
|
import httpx
|
|
import yaml
|
|
from fastapi.testclient import TestClient
|
|
|
|
from tenant_engine.app import create_app
|
|
from tenant_engine.authz import FlexAuthWriteAuthorizer
|
|
from tenant_engine.domain import CapabilityRole, Tenant, create_role_grant
|
|
from tenant_engine.flex_auth import FlexAuthCheckClient
|
|
from tenant_engine.store import InMemoryTenantStore
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def test_pip_claims_contract_is_published():
|
|
data = yaml.safe_load((ROOT / "pip-claims.yaml").read_text())
|
|
assert data["role"] == "pip"
|
|
classes = data["input_classes"]
|
|
assert classes["tenant_roles_live"]["cross_request_cache_by_consumer"] is False
|
|
assert classes["tenant_roles_live"]["request_scoped_memoization"] is True
|
|
assert data["degradation"]["store_unavailable"]["http_status"] == 503
|
|
assert data["live_lookup_authorization"]["reenters_tenant_engine"] is False
|
|
assert data["live_lookup_authorization"]["check_consumes_tenant_roles"] is False
|
|
|
|
|
|
def test_live_lookup_hits_the_store_every_request():
|
|
store = InMemoryTenantStore()
|
|
tenant = Tenant.create(tenant_id="t-1", identifier="tenant:friendly:binky")
|
|
store.create_tenant(tenant)
|
|
store.grant_role(
|
|
create_role_grant(
|
|
tenant=tenant,
|
|
grant_id="g-1",
|
|
role=CapabilityRole.CUS,
|
|
grant_reason="manual_grant",
|
|
plan_id=None,
|
|
granted_by="ops",
|
|
correlation_id="c-1",
|
|
granted_at=datetime.now(UTC),
|
|
)
|
|
)
|
|
calls = {"n": 0}
|
|
original = store.active_roles
|
|
|
|
def counting(tenant_id: str):
|
|
calls["n"] += 1
|
|
return original(tenant_id)
|
|
|
|
store.active_roles = counting # type: ignore[method-assign]
|
|
from helpers import AllowAllAuthorizer
|
|
|
|
client = TestClient(create_app(store=store, authorizer=AllowAllAuthorizer()))
|
|
assert client.get("/tenants/t-1/roles/live", params={"actor": "flex-auth"}).status_code == 200
|
|
assert client.get("/tenants/t-1/roles/live", params={"actor": "flex-auth"}).status_code == 200
|
|
assert calls["n"] == 2
|
|
|
|
|
|
def test_live_lookup_check_does_not_reenter_tenant_engine():
|
|
"""The authorize call for /roles/live POSTs /v1/check and never GETs us."""
|
|
seen: list[str] = []
|
|
|
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
seen.append(f"{request.method} {request.url.path}")
|
|
return httpx.Response(
|
|
200,
|
|
json={
|
|
"id": "d-live",
|
|
"effect": "allow",
|
|
"resource": {},
|
|
"subject": {},
|
|
"provenance": {},
|
|
},
|
|
)
|
|
|
|
store = InMemoryTenantStore()
|
|
store.create_tenant(Tenant.create(tenant_id="t-1", identifier="tenant:friendly:binky"))
|
|
client = FlexAuthCheckClient(
|
|
base_url="https://flex-auth.example.test", transport=httpx.MockTransport(handler)
|
|
)
|
|
app = create_app(store=store, authorizer=FlexAuthWriteAuthorizer(client=client))
|
|
response = TestClient(app).get("/tenants/t-1/roles/live", params={"actor": "flex-auth"})
|
|
assert response.status_code == 200
|
|
assert seen == ["POST /v1/check"]
|
|
assert not any("/roles/live" in item for item in seen)
|
|
assert not any("/tenants/" in item for item in seen)
|