Some checks failed
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
326 lines
16 KiB
Python
326 lines
16 KiB
Python
"""Synthetic actual provisioner/HTTP/storage proof; no live LDAP or authority."""
|
|
import concurrent.futures
|
|
from dataclasses import asdict
|
|
from http.server import ThreadingHTTPServer
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
from pathlib import Path
|
|
import tempfile
|
|
import threading
|
|
import unittest
|
|
from unittest.mock import patch
|
|
from urllib.error import HTTPError
|
|
from urllib.request import Request, urlopen
|
|
|
|
from group_provenance import GroupProvenance
|
|
from provisioner import LLDAPProvisioner, dispatch
|
|
from server import Handler, configured_group_provenance
|
|
from test_service_groups import Directory, req
|
|
|
|
|
|
def rollback(**kw):
|
|
return req(ensure_idempotency_key=req()["idempotency_key"], ensure_correlation_id=req()["correlation_id"], **kw)
|
|
|
|
|
|
class ProvenanceTests(unittest.TestCase):
|
|
def test_inventory_excludes_preexisting_membership_and_empty_group(self):
|
|
d = Directory()
|
|
d.members[3].add("tegwick")
|
|
result = d.ensure_service_groups(req())
|
|
self.assertEqual([{"name": "forge-coulomb-owners", "id": 4}, {"name": "forge-coulomb-readers", "id": 5}], list(result.created_groups))
|
|
self.assertEqual([{"name": "forge-coulomb-owners", "id": 4, "username": "tegwick"}], list(result.created_memberships))
|
|
d.revoke_service_groups(rollback(delete_if_empty=True))
|
|
self.assertEqual({"tegwick"}, d.members[3])
|
|
self.assertIn("forge-admins", d.groups)
|
|
self.assertNotIn("forge-coulomb-owners", d.groups)
|
|
|
|
def test_preexisting_empty_group_never_deleted(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
d.revoke_service_groups(rollback(delete_if_empty=True))
|
|
self.assertIn("forge-admins", d.groups)
|
|
self.assertEqual(set(), d.members[3])
|
|
|
|
def test_unknown_receipt_and_digest_mismatch_before_login(self):
|
|
d = Directory()
|
|
with self.assertRaises(ValueError):
|
|
d.revoke_service_groups(rollback())
|
|
d.ensure_service_groups(req())
|
|
d.calls.clear()
|
|
with patch.object(d, "_login", side_effect=AssertionError("must not login")):
|
|
with self.assertRaises(ValueError):
|
|
d.revoke_service_groups(rollback(members={"forge-admins": ["agent-codex"]}))
|
|
with self.assertRaises(ValueError):
|
|
d.ensure_service_groups(req(groups=["another-group"], members={}))
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_unsigned_inventory_and_legacy_revoke_cannot_mutate(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
d.calls.clear()
|
|
for payload in [req(), rollback(created_groups=[{"name": "forge-admins", "id": 3}]), rollback(created_memberships=[]), rollback(provenance={})]:
|
|
with self.assertRaises(ValueError):
|
|
d.revoke_service_groups(payload)
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_shared_membership_drift_refuses_all_destructive_calls(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
d.members[3].add("agent-codex")
|
|
d.calls.clear()
|
|
with self.assertRaisesRegex(RuntimeError, "postimage"):
|
|
d.revoke_service_groups(rollback(delete_if_empty=True))
|
|
self.assertEqual([], d.calls)
|
|
self.assertEqual({"tegwick", "agent-codex"}, d.members[3])
|
|
|
|
def test_replaced_group_identity_refuses_all_destructive_calls(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
d.groups["forge-admins"] = 10
|
|
d.members[10] = {"tegwick"}
|
|
d.calls.clear()
|
|
with self.assertRaisesRegex(RuntimeError, "postimage"):
|
|
d.revoke_service_groups(rollback())
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_successful_ensure_replay_never_recreates_later_removed_state(self):
|
|
d = Directory()
|
|
first = d.ensure_service_groups(req())
|
|
d.members[3].clear()
|
|
d.calls.clear()
|
|
replay = d.ensure_service_groups(req())
|
|
self.assertTrue(replay.resumed)
|
|
self.assertEqual(first.created_memberships, replay.created_memberships)
|
|
self.assertEqual([], d.calls)
|
|
self.assertEqual(set(), d.members[3])
|
|
|
|
def test_interrupted_add_persists_intent_and_blocks_restart_retry(self):
|
|
d = Directory()
|
|
original = d._gql
|
|
def lost_response(token, query, variables):
|
|
result = original(token, query, variables)
|
|
if "addUserToGroup" in query:
|
|
raise RuntimeError("synthetic lost response")
|
|
return result
|
|
d._gql = lost_response
|
|
with self.assertRaises(RuntimeError):
|
|
d.ensure_service_groups(req())
|
|
d.group_provenance = GroupProvenance(d.group_provenance.path)
|
|
receipt = d.group_provenance.get(req()["idempotency_key"], d._group_binding(req()["groups"], req()["members"], req()["correlation_id"]))
|
|
self.assertEqual("uncertain", receipt["state"])
|
|
self.assertEqual("add_member", receipt["intent"]["operation"])
|
|
self.assertEqual([], receipt["created_memberships"])
|
|
d.calls.clear()
|
|
for operation, payload in [(d.ensure_service_groups, req()), (d.revoke_service_groups, rollback()), (d.ensure_service_groups, req(idempotency_key="different-key-is-not-recovery"))]:
|
|
with self.assertRaises(RuntimeError):
|
|
operation(payload)
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_interrupted_rollback_retains_confirmed_inventory_and_pending_intent(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
original = d._remove_group
|
|
def lost_response(*args):
|
|
original(*args)
|
|
raise RuntimeError("lost remove response")
|
|
d._remove_group = lost_response
|
|
with self.assertRaises(RuntimeError):
|
|
d.revoke_service_groups(rollback())
|
|
d.group_provenance = GroupProvenance(d.group_provenance.path)
|
|
receipt = d.group_provenance.get(req()["idempotency_key"], d._group_binding(req()["groups"], req()["members"], req()["correlation_id"]))
|
|
self.assertEqual("uncertain", receipt["state"])
|
|
self.assertEqual("remove_member", receipt["intent"]["operation"])
|
|
self.assertEqual(2, len(receipt["created_memberships"]))
|
|
d.calls.clear()
|
|
with self.assertRaises(RuntimeError):
|
|
d.revoke_service_groups(rollback())
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_unresolved_pre_call_intent_survives_hard_process_loss(self):
|
|
d = Directory()
|
|
d._gql = lambda *_: (_ for _ in ()).throw(SystemExit("process lost before response"))
|
|
with self.assertRaises(SystemExit):
|
|
d.ensure_service_groups(req())
|
|
d.group_provenance = GroupProvenance(d.group_provenance.path)
|
|
receipt = d.group_provenance.get(req()["idempotency_key"], d._group_binding(req()["groups"], req()["members"], req()["correlation_id"]))
|
|
self.assertEqual("pending", receipt["state"])
|
|
self.assertEqual("add_member", receipt["intent"]["operation"])
|
|
with self.assertRaises(RuntimeError):
|
|
d.ensure_service_groups(req())
|
|
|
|
def test_missing_config_private_permissions_and_lost_database_fail_closed(self):
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
self.assertIsNone(configured_group_provenance())
|
|
d = Directory()
|
|
path = d.group_provenance.path
|
|
with patch.dict(os.environ, {"GROUP_PROVENANCE_DB": path}):
|
|
self.assertIsInstance(configured_group_provenance(), GroupProvenance)
|
|
os.chmod(path, 0o644)
|
|
with self.assertRaises(RuntimeError):
|
|
configured_group_provenance()
|
|
os.chmod(path, 0o600)
|
|
os.unlink(path)
|
|
with self.assertRaises(RuntimeError):
|
|
configured_group_provenance()
|
|
self.assertFalse(Path(path).exists())
|
|
provider = LLDAPProvisioner(base_url="https://invalid", admin_password="synthetic")
|
|
with patch.object(provider, "_login", side_effect=AssertionError("no directory")):
|
|
with self.assertRaises(RuntimeError):
|
|
provider.ensure_service_groups(req())
|
|
with self.assertRaises(RuntimeError):
|
|
provider.revoke_service_groups(rollback())
|
|
|
|
def test_missing_intent_row_stops_before_any_directory_effect(self):
|
|
d = Directory()
|
|
store = d.group_provenance
|
|
original_save = store.save
|
|
def lose_row(receipt):
|
|
if receipt["intent"] is not None:
|
|
with sqlite3.connect(store.path) as db:
|
|
db.execute("DELETE FROM group_receipts")
|
|
original_save(receipt)
|
|
store.save = lose_row
|
|
with self.assertRaisesRegex(RuntimeError, "group_provenance_unavailable"):
|
|
d.ensure_service_groups(req())
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_live_instance_rejects_replaced_database_before_effect(self):
|
|
d = Directory()
|
|
original = d.group_provenance
|
|
replacement = str(Path(d.storage.name) / "replacement.db")
|
|
GroupProvenance(replacement, initialize=True)
|
|
os.replace(replacement, original.path)
|
|
with self.assertRaisesRegex(RuntimeError, "group_provenance_unavailable"):
|
|
d.ensure_service_groups(req())
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_failed_before_mutation_is_retained_without_stranding_other_keys(self):
|
|
for failure in ("unknown-user", "oversized-prestate", "directory-read-failed"):
|
|
d = Directory()
|
|
old_members = d._group_members
|
|
request = req(members={"forge-admins": ["nobody"]}) if failure == "unknown-user" else req()
|
|
if failure == "oversized-prestate":
|
|
d._group_members = lambda *_: [f"synthetic{i}" for i in range(4097)]
|
|
if failure == "directory-read-failed":
|
|
d._group_members = lambda *_: (_ for _ in ()).throw(RuntimeError("read failed"))
|
|
with self.assertRaises((ValueError, RuntimeError)):
|
|
d.ensure_service_groups(request)
|
|
binding = d._group_binding(request["groups"], request["members"], request["correlation_id"])
|
|
receipt = d.group_provenance.get(request["idempotency_key"], binding)
|
|
self.assertEqual("refused", receipt["state"])
|
|
self.assertEqual("before_mutation", receipt["failure_phase"])
|
|
self.assertEqual([], d.calls)
|
|
d._group_members = old_members
|
|
with self.assertRaises(RuntimeError):
|
|
d.ensure_service_groups(request)
|
|
self.assertEqual("groups_ensured", d.ensure_service_groups(req(idempotency_key="another-valid-operation-key")).status)
|
|
|
|
def test_current_correlations_are_bound_to_original_ensure(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
d.calls.clear()
|
|
with self.assertRaises(ValueError):
|
|
d.ensure_service_groups(req(correlation_id="other-case"))
|
|
payload = rollback()
|
|
payload["ensure_correlation_id"] = "other-case"
|
|
with self.assertRaises(ValueError):
|
|
d.revoke_service_groups(payload)
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_readiness_separates_directory_health_from_group_capability(self):
|
|
provider = LLDAPProvisioner(base_url="https://invalid", admin_password="synthetic")
|
|
provider._login = lambda **_: "synthetic"
|
|
provider._gql = lambda *_args, **_kw: {"groups": []}
|
|
self.assertEqual("disabled", provider.preflight()["service_groups"])
|
|
d = Directory()
|
|
provider.group_provenance = d.group_provenance
|
|
self.assertEqual("available", provider.preflight()["service_groups"])
|
|
os.chmod(d.group_provenance.path, 0o644)
|
|
with self.assertRaisesRegex(RuntimeError, "group_provenance_unavailable"):
|
|
provider.preflight()
|
|
|
|
def test_concurrent_sqlite_reservation_allows_one_original_attempt(self):
|
|
d = Directory()
|
|
path = d.group_provenance.path
|
|
def reserve(_):
|
|
return GroupProvenance(path).reserve("synthetic-concurrent-key", "same-binding")[1]
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as pool:
|
|
fresh = list(pool.map(reserve, range(24)))
|
|
self.assertEqual(1, sum(fresh))
|
|
|
|
def test_bounded_records_never_age_or_evict_into_authority(self):
|
|
d = Directory()
|
|
store = GroupProvenance(d.group_provenance.path, capacity=1)
|
|
receipt, _ = store.reserve("one", "binding")
|
|
receipt.update(state="ensured", requested_groups=[], ensure_correlation_id="test")
|
|
store.save(receipt)
|
|
with self.assertRaisesRegex(RuntimeError, "capacity"):
|
|
store.reserve("two", "binding")
|
|
self.assertFalse(store.reserve("one", "binding")[1])
|
|
|
|
def test_rollback_replay_and_key_reuse_do_not_recreate(self):
|
|
d = Directory()
|
|
d.ensure_service_groups(req())
|
|
first = d.revoke_service_groups(rollback(delete_if_empty=True))
|
|
d.calls.clear()
|
|
result = d.revoke_service_groups(rollback(delete_if_empty=True))
|
|
self.assertEqual(first.status, result.status)
|
|
self.assertTrue(result.resumed)
|
|
with self.assertRaises(RuntimeError):
|
|
d.ensure_service_groups(req())
|
|
with self.assertRaises(ValueError):
|
|
d.revoke_service_groups(rollback(delete_if_empty=False))
|
|
self.assertEqual([], d.calls)
|
|
|
|
def test_real_http_handler_reinitialized_from_config_retains_receipt(self):
|
|
d = Directory()
|
|
path = d.group_provenance.path
|
|
def boot():
|
|
with patch.dict(os.environ, {"GROUP_PROVENANCE_DB": path}):
|
|
provider = LLDAPProvisioner(base_url="https://synthetic.invalid", admin_password="unused", group_provenance=configured_group_provenance())
|
|
# Only directory transport is synthetic; actual initialized adapter,
|
|
# dispatch, HTTP authentication, configuration and SQLite are used.
|
|
for name in ("_login", "_directory", "_group_members", "_ensure_group", "_gql", "_remove_group"):
|
|
setattr(provider, name, getattr(d, name))
|
|
class LocalHandler(Handler):
|
|
provisioner = provider
|
|
service_token = "synthetic-service-token"
|
|
server = ThreadingHTTPServer(("127.0.0.1", 0), LocalHandler)
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
thread.start()
|
|
return server, thread
|
|
def stop(server, thread):
|
|
server.shutdown(); server.server_close(); thread.join()
|
|
def post(server, endpoint, payload):
|
|
request = Request(f"http://127.0.0.1:{server.server_port}/v1/groups/{endpoint}", data=json.dumps(payload).encode(),
|
|
headers={"Authorization": "Bearer synthetic-service-token"})
|
|
with urlopen(request) as response:
|
|
return json.load(response)
|
|
server, thread = boot()
|
|
try:
|
|
created = post(server, "ensure", req())
|
|
finally:
|
|
stop(server, thread)
|
|
server, thread = boot()
|
|
try:
|
|
d.calls.clear()
|
|
replay = post(server, "ensure", req())
|
|
self.assertTrue(replay["resumed"])
|
|
self.assertEqual(created["created_memberships"], replay["created_memberships"])
|
|
self.assertEqual([], d.calls)
|
|
revoked = post(server, "revoke", rollback(delete_if_empty=True))
|
|
self.assertEqual("groups_revoked_and_deleted", revoked["status"])
|
|
self.assertIn("forge-admins", d.groups)
|
|
with self.assertRaises(HTTPError) as error:
|
|
post(server, "revoke", req())
|
|
self.assertEqual(400, error.exception.code)
|
|
d.calls.clear()
|
|
os.chmod(path, 0o644)
|
|
with self.assertRaises(HTTPError) as error:
|
|
post(server, "ensure", req(idempotency_key="different-invocation-key"))
|
|
self.assertEqual(503, error.exception.code)
|
|
self.assertEqual({"error": "dependency_unavailable", "dependency": "group_provenance"}, json.load(error.exception))
|
|
self.assertEqual([], d.calls)
|
|
finally:
|
|
stop(server, thread)
|