tenant-engine/tests/test_pip_claims.py
tegwick 672cf4da6e
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 37s
Implement TEN-WP-0011 security layer conformance
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
2026-08-29 13:02:51 +02:00

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)