diff --git a/scripts/mas_evidence_publisher_gate.py b/scripts/mas_evidence_publisher_gate.py index 97c9945..62e9a37 100755 --- a/scripts/mas_evidence_publisher_gate.py +++ b/scripts/mas_evidence_publisher_gate.py @@ -671,11 +671,16 @@ def write_pair(pair): return summary -def _app_keys(api): - value = api( - "GET", - "/iam/v1alpha1/api-keys?application_id=" + APPLICATION_ID + "&page_size=100", - ) +def _app_keys(api, organization): + """List the application's keys. A Console session needs organization_id or gets HTTP 400.""" + from urllib.parse import urlencode + + query = urlencode({ + "organization_id": organization, + "application_id": APPLICATION_ID, + "page_size": 100, + }) + value = api("GET", "/iam/v1alpha1/api-keys?" + query) keys = value.get("api_keys") if isinstance(value, dict) else None if not isinstance(keys, list) or value.get("total_count") != len(keys): raise GateError("key_inventory_rejected") @@ -699,9 +704,10 @@ def mint_workload_pair(api, prove, write, sleep=time.sleep): """ receipt = {"application_id": APPLICATION_ID, "values_recorded": False} app = api("GET", "/iam/v1alpha1/applications/" + APPLICATION_ID) - if app.get("name") != POLICY_NAME: + organization = app.get("organization_id") + if app.get("name") != POLICY_NAME or not isinstance(organization, str) or not organization: raise GateError("application_binding_refused") - before = _app_keys(api) + before = _app_keys(api, organization) receipt["keys_before"] = _key_view(before) if len(before) != 1 or before[0].get("default_project_id") != PROJECT_ID: raise GateError("supplier_key_state_requires_review", receipt["keys_before"]) @@ -745,7 +751,7 @@ def mint_workload_pair(api, prove, write, sleep=time.sleep): detail = dict(exc.detail or {}) if isinstance(exc, GateError) and isinstance(exc.detail, dict) else {} detail["minted_key_deleted"] = deleted raise GateError(code, detail) from None - receipt["keys_after"] = _key_view(_app_keys(api)) + receipt["keys_after"] = _key_view(_app_keys(api, organization)) receipt["status"] = "passed" return receipt diff --git a/tests/test_mas_evidence_publisher_gate.py b/tests/test_mas_evidence_publisher_gate.py index 6ee23d1..60a7565 100644 --- a/tests/test_mas_evidence_publisher_gate.py +++ b/tests/test_mas_evidence_publisher_gate.py @@ -269,8 +269,9 @@ class _FakeConsole: def __call__(self, method, path, data=None): self.calls.append((method, path)) if method == "GET" and path.startswith("/iam/v1alpha1/applications/"): - return {"name": self.name} + return {"name": self.name, "organization_id": "org-1"} if method == "GET" and path.startswith("/iam/v1alpha1/api-keys?"): + assert "organization_id=org-1" in path and "application_id=" + gate.APPLICATION_ID in path return {"api_keys": list(self.keys), "total_count": len(self.keys)} if method == "POST": assert data["application_id"] == gate.APPLICATION_ID diff --git a/workplans/RPF-WP-0070-mas-evidence-recovery.md b/workplans/RPF-WP-0070-mas-evidence-recovery.md index 25cf380..9b7a208 100644 --- a/workplans/RPF-WP-0070-mas-evidence-recovery.md +++ b/workplans/RPF-WP-0070-mas-evidence-recovery.md @@ -104,6 +104,14 @@ The supplier key stays founder custody and can be revoked separately. Tests cover the mint, retry, deletion and refusal paths. The attended run is still pending. +2026-11-03 05:36 CET: the first attended `--mint-workload` run exited with +`provider_http_error`, a GET returning HTTP 400, before any key was minted. +The denials receipt was not rewritten and the pair stayed empty. Cause: the +API-key listing omitted `organization_id`, which a Console session requires. +`mas_evidence_iam_grant.py` already sends it. Fix: the gate takes +`organization_id` from the application read and sends it on both key +listings. The next attended run is the same command. + ## Admit delivery and place the node file ```task