Finish WP-0025 SSO cutover except group allowlist residual.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 29s

Mark T03–T05 and T07–T08 done after live Authelia/TLS verification,
prefer SSO principal in ops UI copy and audits, and document break-glass
port-forward. Leave T06 waiting on net-kingdom LLDAP/Authelia group rules.
This commit is contained in:
tegwick 2026-07-22 10:23:36 +02:00
parent 27c087bcb4
commit 91353df7d0
8 changed files with 151 additions and 67 deletions

View file

@ -67,6 +67,53 @@ async def test_trigger_requires_token(ops_app: FastAPI, monkeypatch: pytest.Monk
assert res.status_code == 401
@pytest.mark.asyncio
async def test_trigger_with_sso_principal(
ops_app: FastAPI, monkeypatch: pytest.MonkeyPatch
) -> None:
"""SSO Remote-User is preferred; audit must record sso:<user>, never the token."""
def_id = uuid.uuid4()
row = MagicMock()
row.name = "Weekly SBOM"
row.context_sources = []
row.task_templates = []
row.trigger_config = {"trigger_type": "cron", "cron_expression": "0 9 * * 1"}
session = AsyncMock()
session.get = AsyncMock(return_value=row)
session.__aenter__ = AsyncMock(return_value=session)
session.__aexit__ = AsyncMock(return_value=None)
ops_app.state.session_factory.return_value = session
handle = MagicMock()
handle.id = f"activity-{def_id}:manual-sso"
ops_app.state.temporal.start_workflow = AsyncMock(return_value=handle)
captured: dict[str, Any] = {}
async def capture_audit(**kwargs: Any) -> dict[str, Any]:
captured.update(kwargs)
return {
"action": kwargs["action"],
"audit_id": "sso-a1",
"principal": kwargs["principal"],
}
monkeypatch.setattr("activity_core.ops_api.record_ops_audit", capture_audit)
transport = ASGITransport(app=ops_app)
async with AsyncClient(transport=transport, base_url="http://test") as client:
res = await client.post(
f"/ops/automations/{def_id}/trigger",
headers={"Remote-User": "alice.operator"},
json={},
)
assert res.status_code == 200
assert res.json()["audit"]["principal"] == "sso:alice.operator"
assert captured["principal"] == "sso:alice.operator"
assert "token" not in str(res.json()).lower() or "test-token" not in str(res.json())
@pytest.mark.asyncio
async def test_trigger_with_token(ops_app: FastAPI, monkeypatch: pytest.MonkeyPatch) -> None:
def_id = uuid.uuid4()
@ -216,7 +263,7 @@ async def test_ui_index_renders(ops_app: FastAPI, monkeypatch: pytest.MonkeyPatc
res = await client.get("/ops/ui/")
assert res.status_code == 200
assert "Daily Triage" in res.text
assert "Operator token" in res.text
assert "SSO" in res.text or "Break-glass" in res.text
assert "Temporal UI" in res.text
assert "temporal.coulomb.social" in res.text