Implement AUDIT-WP-0006 honest operational custody.
Postgres now reports custody_class=operational with a cited 30-day recoverable window. Join ITC-CAP operations.audit at D4, publish the interface card, and overlay user-engine tenants [*] from Git so an ExternalSecret refresh cannot shrink it.
This commit is contained in:
parent
0a3d05ff1c
commit
ded432a63f
25 changed files with 832 additions and 94 deletions
97
tests/test_senders.py
Normal file
97
tests/test_senders.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from audit_core.senders import SenderRegistry, WILDCARD
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCOPE_FILE = ROOT / "deploy" / "senders-scope.json"
|
||||
|
||||
|
||||
def test_declared_scope_keeps_user_engine_tenants_wildcard():
|
||||
scope = json.loads(SCOPE_FILE.read_text())
|
||||
user_engine = next(entry for entry in scope if entry["name"] == "user-engine")
|
||||
assert user_engine["tenants"] == ["*"]
|
||||
assert user_engine["sources"] == ["user-engine"]
|
||||
assert user_engine["may_write"] is True
|
||||
assert user_engine["may_read"] is False
|
||||
assert "tokens" not in user_engine
|
||||
assert "token" not in user_engine
|
||||
|
||||
|
||||
def test_scope_overlay_widens_narrow_secret_tenants(tmp_path):
|
||||
secret = json.dumps(
|
||||
[
|
||||
{
|
||||
"name": "user-engine",
|
||||
"tokens": ["live-token"],
|
||||
"sources": ["user-engine"],
|
||||
"tenants": ["tenant:friendly:binky"],
|
||||
"may_write": True,
|
||||
"may_read": False,
|
||||
}
|
||||
]
|
||||
)
|
||||
env = {
|
||||
"AUDIT_CORE_SENDERS": secret,
|
||||
"AUDIT_CORE_SENDERS_SCOPE_PATH": str(SCOPE_FILE),
|
||||
}
|
||||
registry = SenderRegistry.from_env(env)
|
||||
identity = registry.authenticate("Bearer live-token")
|
||||
assert identity is not None
|
||||
assert WILDCARD in identity.tenants
|
||||
assert identity.permits_tenant("tenant:other:x")
|
||||
assert identity.tokens == ("live-token",)
|
||||
|
||||
|
||||
def test_scope_overlay_does_not_take_tokens_from_git():
|
||||
overlay = json.dumps(
|
||||
[
|
||||
{
|
||||
"name": "user-engine",
|
||||
"tokens": ["must-not-be-used"],
|
||||
"tenants": ["*"],
|
||||
}
|
||||
]
|
||||
)
|
||||
secret = json.dumps(
|
||||
[
|
||||
{
|
||||
"name": "user-engine",
|
||||
"tokens": ["live-token"],
|
||||
"sources": ["user-engine"],
|
||||
"tenants": ["tenant:friendly:binky"],
|
||||
}
|
||||
]
|
||||
)
|
||||
registry = SenderRegistry.from_env(
|
||||
{"AUDIT_CORE_SENDERS": secret, "AUDIT_CORE_SENDERS_SCOPE": overlay}
|
||||
)
|
||||
assert registry.authenticate("Bearer must-not-be-used") is None
|
||||
assert registry.authenticate("Bearer live-token") is not None
|
||||
|
||||
|
||||
def test_missing_overlay_leaves_secret_as_is():
|
||||
secret = json.dumps(
|
||||
[
|
||||
{
|
||||
"name": "user-engine",
|
||||
"tokens": ["live-token"],
|
||||
"sources": ["user-engine"],
|
||||
"tenants": ["tenant:friendly:binky"],
|
||||
}
|
||||
]
|
||||
)
|
||||
identity = SenderRegistry.from_env({"AUDIT_CORE_SENDERS": secret}).identities[0]
|
||||
assert identity.tenants == frozenset({"tenant:friendly:binky"})
|
||||
|
||||
|
||||
def test_invalid_scope_overlay_is_a_startup_error():
|
||||
secret = json.dumps(
|
||||
[{"name": "user-engine", "tokens": ["t"], "sources": ["user-engine"]}]
|
||||
)
|
||||
with pytest.raises(ValueError, match="JSON list"):
|
||||
SenderRegistry.from_env(
|
||||
{"AUDIT_CORE_SENDERS": secret, "AUDIT_CORE_SENDERS_SCOPE": "{}"}
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue