"""PostgreSQL security contract for existing-Agent credential transition.""" from concurrent.futures import ThreadPoolExecutor import os import unittest from uuid import UUID, uuid4 from pydantic import ValidationError from psycopg.rows import dict_row from app.agent_auth.exceptions import AgentAuthenticationFailed from app.agent_auth.repository import AgentAuthRepository from app.agent_auth.service import AgentAuthService, hash_agent_secret from app.agent_enrollment.exceptions import ( AgentEnrollmentConflict, AgentEnrollmentCredentialCollision, AgentEnrollmentTokenInvalid, ) from app.agent_enrollment.models import ( AgentCredentialTransitionCreateRequest, AgentCredentialTransitionRedeemRequest, AgentEnrollmentCreateRequest, AgentEnrollRequest, ) from app.agent_enrollment.repository import ( AgentEnrollmentRepository, default_connection_factory, ) from app.agent_enrollment.service import AgentEnrollmentService from app.agent_registry.repository import AgentRegistryRepository from app.config import settings PRODUCTION_AGENT_ID = "plexus-windows-agent" PRODUCTION_SITE_ID = UUID("00000000-0000-4000-8000-000000002001") PRODUCTION_ZONE_ID = UUID("00000000-0000-4000-8000-000000002002") PRODUCTION_MINER_ID = UUID("00000000-0000-4000-8000-000000002003") def connect(): return default_connection_factory() class AgentCredentialTransitionPostgresTests(unittest.TestCase): @classmethod def setUpClass(cls): if os.environ.get("MINEROPS_CREDENTIAL_TRANSITION_DISPOSABLE") != "1": raise unittest.SkipTest( "credential transition requires its disposable PostgreSQL runner" ) if settings.minerops_agent_id != PRODUCTION_AGENT_ID: raise AssertionError( "runner must bind the production-shaped legacy Agent identity" ) cls.service = AgentEnrollmentService(AgentEnrollmentRepository(connect)) cls.auth = AgentAuthService(AgentAuthRepository(connect)) def create_legacy_agent( self, agent_id, *, site_id=None, zone_id=None, enabled=True, secret_hash=None, revoked=False, ): site_id = site_id or uuid4() zone_id = zone_id or uuid4() connection = connect() try: with connection.cursor() as cur: cur.execute( "INSERT INTO sites (id, name) VALUES (%s, %s)", (site_id, f"Transition Site {site_id.hex[:8]}"), ) cur.execute( """ INSERT INTO agent_zones (id, site_id, name, is_enabled) VALUES (%s, %s, %s, %s) """, (zone_id, site_id, "Default Zone", enabled), ) cur.execute( """ INSERT INTO agent_zone_network_ranges (agent_zone_id, network) VALUES (%s, '10.220.0.0/24'::cidr) """, (zone_id,), ) cur.execute( """ INSERT INTO minerops_site_agents ( agent_id, site_id, agent_zone_id, site_name, display_name, agent_secret_hash, credential_created_at, credential_revoked_at ) VALUES ( %s, %s, %s, %s, %s, %s, CASE WHEN %s::boolean THEN now() ELSE NULL END, CASE WHEN %s::boolean THEN now() ELSE NULL END ) """, ( agent_id, site_id, zone_id, f"Transition Site {site_id.hex[:8]}", f"Agent {agent_id}"[:120], secret_hash, secret_hash is not None, revoked, ), ) connection.commit() finally: connection.close() return site_id, zone_id def create_transition(self, agent_id): return self.service.create_credential_transition( AgentCredentialTransitionCreateRequest(agent_id=agent_id), actor_user_id=None, ) def redeem_transition(self, token, secret): return AgentEnrollmentService( AgentEnrollmentRepository(connect) ).redeem_credential_transition( AgentCredentialTransitionRedeemRequest( token=token, agent_secret_hash=hash_agent_secret(secret), ) ) def agent_row(self, agent_id): connection = connect() try: with connection.cursor(row_factory=dict_row) as cur: cur.execute( """ SELECT agent_id, site_id, agent_zone_id, agent_secret_hash, credential_created_at, credential_revoked_at, legacy_auth_disabled_at FROM minerops_site_agents WHERE agent_id = %s """, (agent_id,), ) return cur.fetchone() finally: connection.close() def test_01_production_identity_transitions_without_routing_change(self): connection = connect() try: with connection.cursor() as cur: cur.execute( """ INSERT INTO sites (id, name) VALUES (%s, 'Plexus Mining Site') """, (PRODUCTION_SITE_ID,), ) cur.execute( """ INSERT INTO agent_zones (id, site_id, name, is_enabled) VALUES (%s, %s, 'Default Zone', true) """, (PRODUCTION_ZONE_ID, PRODUCTION_SITE_ID), ) cur.execute( """ INSERT INTO agent_zone_network_ranges (agent_zone_id, network) VALUES (%s, '192.0.2.0/24'::cidr) """, (PRODUCTION_ZONE_ID,), ) cur.execute( """ INSERT INTO minerops_site_agents ( agent_id, site_id, agent_zone_id, site_name, display_name ) VALUES (%s, %s, %s, 'Plexus', 'Plexus Windows Agent') """, ( PRODUCTION_AGENT_ID, PRODUCTION_SITE_ID, PRODUCTION_ZONE_ID, ), ) cur.execute( """ INSERT INTO miners ( id, site_id, agent_zone_id, asset_tag, current_ip, current_mac, status ) VALUES ( %s, %s, %s, 'TRANSITION-PRODUCTION-MINER', '192.0.2.20', '02:00:00:00:20:03', 'online' ) """, ( PRODUCTION_MINER_ID, PRODUCTION_SITE_ID, PRODUCTION_ZONE_ID, ), ) connection.commit() finally: connection.close() legacy_secret = settings.minerops_agent_shared_secret self.assertEqual( self.auth.authenticate(None, legacy_secret), PRODUCTION_AGENT_ID, ) transition = self.create_transition(PRODUCTION_AGENT_ID) token = transition["transition_token"] self.assertEqual(transition["target_agent_id"], PRODUCTION_AGENT_ID) self.assertEqual(transition["site_id"], PRODUCTION_SITE_ID) self.assertEqual(transition["agent_zone_id"], PRODUCTION_ZONE_ID) connection = connect() try: with connection.cursor(row_factory=dict_row) as cur: cur.execute( """ SELECT token_hash, target_agent_id, used_at, enrolled_agent_id, site_id, agent_zone_id FROM agent_enrollments WHERE id = %s """, (transition["id"],), ) authorization = cur.fetchone() finally: connection.close() self.assertEqual(authorization["token_hash"], hash_agent_secret(token)) self.assertNotEqual(authorization["token_hash"], token) self.assertEqual(authorization["target_agent_id"], PRODUCTION_AGENT_ID) self.assertIsNone(authorization["used_at"]) new_secret = "production-shaped-per-agent-secret" result = self.redeem_transition(token, new_secret) self.assertEqual(result["agent_id"], PRODUCTION_AGENT_ID) self.assertNotIn("agent_secret", result) self.assertEqual(result["site_id"], PRODUCTION_SITE_ID) self.assertEqual(result["agent_zone_id"], PRODUCTION_ZONE_ID) # An ambiguous response is safely retryable only with the same locally # generated credential; no second transition or audit event is created. retried = self.redeem_transition(token, new_secret) self.assertEqual(retried, result) with self.assertRaises(AgentEnrollmentTokenInvalid): self.redeem_transition(token, "different-secret-cannot-rotate") self.assertEqual( self.auth.authenticate(PRODUCTION_AGENT_ID, new_secret), PRODUCTION_AGENT_ID, ) for supplied_id in (None, PRODUCTION_AGENT_ID): with self.subTest(supplied_id=supplied_id), self.assertRaises( AgentAuthenticationFailed ): self.auth.authenticate(supplied_id, legacy_secret) connection = connect() try: with connection.cursor(row_factory=dict_row) as cur: cur.execute( """ SELECT site_id, agent_zone_id, agent_secret_hash, credential_created_at, legacy_auth_disabled_at FROM minerops_site_agents WHERE agent_id = %s """, (PRODUCTION_AGENT_ID,), ) agent = cur.fetchone() cur.execute( """ SELECT site_id, agent_zone_id, current_mac::text, host(current_ip) AS current_ip FROM miners WHERE id = %s """, (PRODUCTION_MINER_ID,), ) miner = cur.fetchone() cur.execute( """ SELECT count(*) AS retirement_count FROM agent_legacy_auth_retirements WHERE agent_id = %s """, (PRODUCTION_AGENT_ID,), ) retirement_count = cur.fetchone()["retirement_count"] cur.execute( """ SELECT count(*) AS redeemed_audits FROM audit_logs WHERE action = 'agent.credential_transition.redeemed' AND entity_id = %s """, (transition["id"],), ) redeemed_audits = cur.fetchone()["redeemed_audits"] finally: connection.close() self.assertEqual(agent["site_id"], PRODUCTION_SITE_ID) self.assertEqual(agent["agent_zone_id"], PRODUCTION_ZONE_ID) self.assertEqual(agent["agent_secret_hash"], hash_agent_secret(new_secret)) self.assertIsNotNone(agent["credential_created_at"]) self.assertIsNotNone(agent["legacy_auth_disabled_at"]) self.assertEqual(miner["site_id"], PRODUCTION_SITE_ID) self.assertEqual(miner["agent_zone_id"], PRODUCTION_ZONE_ID) self.assertEqual(miner["current_mac"], "02:00:00:00:20:03") self.assertEqual(miner["current_ip"], "192.0.2.20") self.assertEqual(retirement_count, 1) self.assertEqual(redeemed_audits, 1) # Remove deletable enrollment/current state to prove that the separate # append-only retirement identity still defeats fallback on recreation. connection = connect() try: with connection.cursor() as cur: cur.execute( "DELETE FROM agent_enrollments WHERE id = %s", (transition["id"],), ) cur.execute( "DELETE FROM minerops_site_agents WHERE agent_id = %s", (PRODUCTION_AGENT_ID,), ) cur.execute( """ INSERT INTO minerops_site_agents ( agent_id, site_id, agent_zone_id, site_name ) VALUES (%s, %s, %s, 'Recreated Plexus') """, ( PRODUCTION_AGENT_ID, PRODUCTION_SITE_ID, PRODUCTION_ZONE_ID, ), ) connection.commit() finally: connection.close() recreated = self.agent_row(PRODUCTION_AGENT_ID) self.assertIsNone(recreated["agent_secret_hash"]) self.assertIsNotNone(recreated["legacy_auth_disabled_at"]) with self.assertRaises(AgentAuthenticationFailed): self.auth.authenticate(PRODUCTION_AGENT_ID, legacy_secret) def test_02_concurrent_redemption_has_one_credential_winner(self): agent_id = "transition-concurrent-agent" self.create_legacy_agent(agent_id) token = self.create_transition(agent_id)["transition_token"] secrets = ("concurrent-secret-a", "concurrent-secret-b") def redeem(secret): try: return ("success", secret, self.redeem_transition(token, secret)) except AgentEnrollmentTokenInvalid: return ("invalid", secret, None) with ThreadPoolExecutor(max_workers=2) as executor: results = list(executor.map(redeem, secrets)) winners = [item for item in results if item[0] == "success"] losers = [item for item in results if item[0] == "invalid"] self.assertEqual(len(winners), 1) self.assertEqual(len(losers), 1) row = self.agent_row(agent_id) self.assertEqual(row["agent_secret_hash"], hash_agent_secret(winners[0][1])) def test_03_authorization_cannot_substitute_identity_or_steal_zone(self): agent_a = "transition-bound-agent-a" agent_b = "transition-bound-agent-b" site_a, zone_a = self.create_legacy_agent(agent_a) site_b, zone_b = self.create_legacy_agent(agent_b) token = self.create_transition(agent_a)["transition_token"] with self.assertRaises(ValidationError): AgentCredentialTransitionRedeemRequest.model_validate( { "token": token, "agent_secret_hash": hash_agent_secret("bound-secret"), "agent_id": agent_b, } ) result = self.redeem_transition(token, "bound-secret") self.assertEqual(result["agent_id"], agent_a) self.assertEqual((result["site_id"], result["agent_zone_id"]), (site_a, zone_a)) other = self.agent_row(agent_b) self.assertEqual((other["site_id"], other["agent_zone_id"]), (site_b, zone_b)) self.assertIsNone(other["agent_secret_hash"]) def test_04_revoked_retired_and_disabled_targets_fail_closed(self): revoked = "transition-revoked-agent" self.create_legacy_agent( revoked, secret_hash=hash_agent_secret("revoked-secret"), revoked=True, ) with self.assertRaises(AgentEnrollmentConflict): self.create_transition(revoked) retired = "transition-retired-agent" self.create_legacy_agent(retired) connection = connect() try: with connection.cursor() as cur: cur.execute( "INSERT INTO agent_legacy_auth_retirements (agent_id) VALUES (%s)", (retired,), ) connection.commit() finally: connection.close() with self.assertRaises(AgentEnrollmentConflict): self.create_transition(retired) disabled = "transition-disabled-agent" self.create_legacy_agent(disabled, enabled=False) with self.assertRaises(AgentEnrollmentConflict): self.create_transition(disabled) changed = "transition-zone-disabled-after-authorization" _, changed_zone = self.create_legacy_agent(changed) authorization = self.create_transition(changed) connection = connect() try: with connection.cursor() as cur: cur.execute( "UPDATE agent_zones SET is_enabled = false WHERE id = %s", (changed_zone,), ) connection.commit() finally: connection.close() with self.assertRaises(AgentEnrollmentConflict): self.redeem_transition( authorization["transition_token"], "disabled-zone-secret" ) self.assertIsNone(self.agent_row(changed)["agent_secret_hash"]) def test_05_credential_collision_rolls_back_retirement_and_token_use(self): collision_hash = hash_agent_secret("already-issued-secret") self.create_legacy_agent( "transition-existing-credential-holder", secret_hash=collision_hash, ) target = "transition-rollback-target" self.create_legacy_agent(target) transition = self.create_transition(target) with self.assertRaises(AgentEnrollmentCredentialCollision): self.service.redeem_credential_transition( AgentCredentialTransitionRedeemRequest( token=transition["transition_token"], agent_secret_hash=collision_hash, ) ) row = self.agent_row(target) self.assertIsNone(row["agent_secret_hash"]) self.assertIsNone(row["credential_created_at"]) self.assertIsNone(row["legacy_auth_disabled_at"]) connection = connect() try: with connection.cursor() as cur: cur.execute( """ SELECT count(*) FROM agent_legacy_auth_retirements WHERE agent_id = %s """, (target,), ) self.assertEqual(cur.fetchone()[0], 0) cur.execute( """ SELECT used_at, enrolled_agent_id FROM agent_enrollments WHERE id = %s """, (transition["id"],), ) self.assertEqual(cur.fetchone(), (None, None)) finally: connection.close() def test_06_normal_enrollment_still_works_and_occupied_zone_is_protected(self): site_id, occupied_zone = self.create_legacy_agent( "transition-ordinary-occupied" ) with self.assertRaises(AgentEnrollmentConflict): self.service.create_enrollment( AgentEnrollmentCreateRequest( site_id=site_id, agent_zone_id=occupied_zone, display_name="Must Not Steal Occupied Zone", ), actor_user_id=None, ) free_zone = uuid4() connection = connect() try: with connection.cursor() as cur: cur.execute( """ INSERT INTO agent_zones (id, site_id, name, is_enabled) VALUES (%s, %s, 'Free Enrollment Zone', true) """, (free_zone, site_id), ) connection.commit() finally: connection.close() enrollment = self.service.create_enrollment( AgentEnrollmentCreateRequest( site_id=site_id, agent_zone_id=free_zone, display_name="Ordinary New Agent", ), actor_user_id=None, ) enrolled = self.service.redeem( AgentEnrollRequest(token=enrollment["enrollment_token"]) ) self.assertRegex(enrolled["agent_id"], r"^agent-[0-9a-f]{32}$") self.assertEqual(enrolled["agent_zone_id"], free_zone) def test_07_registry_history_and_audit_never_expose_plaintext(self): agent_id = "transition-privacy-agent" self.create_legacy_agent(agent_id) transition = self.create_transition(agent_id) token = transition["transition_token"] listed = self.service.list_enrollments() item = next(row for row in listed if row["id"] == transition["id"]) self.assertEqual(item["target_agent_id"], agent_id) self.assertEqual(item["purpose"], "existing_agent_credential_transition") serialized_item = repr(item) self.assertNotIn(token, serialized_item) for forbidden in ( "token_hash", "transition_token", "agent_secret", "agent_secret_hash", ): self.assertNotIn(forbidden, item) registry = AgentRegistryRepository().list_registry() registry_agent = next( row for row in registry["agents"] if row["agent_id"] == agent_id ) for forbidden in ("agent_secret", "agent_secret_hash", "token_hash"): self.assertNotIn(forbidden, registry_agent) connection = connect() try: with connection.cursor() as cur: cur.execute( """ SELECT new_value_json::text FROM audit_logs WHERE entity_id = %s """, (transition["id"],), ) audit_text = "\n".join(row[0] for row in cur.fetchall()) finally: connection.close() self.assertNotIn(token, audit_text) if __name__ == "__main__": unittest.main()