feat: add secure agent enrollment

This commit is contained in:
lussierj committed 2026-08-26 21:31:09 +00:00
1 parent 82309db79a
commit d9f0da694b
26 files changed
+2942 -76

No files matched your search

+136
View File
@@ -0,0 +1,136 @@
"""Focused tests for per-Agent authentication and legacy compatibility."""
import unittest
from datetime import datetime, timezone
from app.agent_auth.exceptions import (
AgentAuthenticationFailed,
AgentAuthenticationUnavailable,
AgentCredentialRevoked,
AgentIdentityUnauthorized,
)
from app.agent_auth.service import AgentAuthService, hash_agent_secret
from app.config import settings
NOW = datetime(2026, 8, 26, tzinfo=timezone.utc)
class FakeAgentAuthRepository:
def __init__(self, rows=None):
self.rows = rows or {}
def get_credential(self, agent_id):
return self.rows.get(agent_id)
class AgentAuthServiceTests(unittest.TestCase):
def setUp(self):
self.old_id = settings.minerops_agent_id
self.old_secret = settings.minerops_agent_shared_secret
settings.minerops_agent_id = "legacy-agent"
settings.minerops_agent_shared_secret = "legacy-secret"
def tearDown(self):
settings.minerops_agent_id = self.old_id
settings.minerops_agent_shared_secret = self.old_secret
def service(self, rows=None):
return AgentAuthService(FakeAgentAuthRepository(rows))
def test_high_entropy_secret_hash_is_sha256_hex(self):
digest = hash_agent_secret("example-secret")
self.assertEqual(len(digest), 64)
self.assertRegex(digest, r"^[0-9a-f]{64}$")
def test_legacy_agent_without_registry_row_still_authenticates(self):
self.assertEqual(
self.service().authenticate("legacy-agent", "legacy-secret"),
"legacy-agent",
)
def test_legacy_agent_with_null_credential_still_authenticates(self):
rows = {
"legacy-agent": {
"agent_id": "legacy-agent",
"agent_secret_hash": None,
"credential_created_at": None,
"credential_revoked_at": None,
}
}
self.assertEqual(
self.service(rows).authenticate("legacy-agent", "legacy-secret"),
"legacy-agent",
)
def test_per_agent_credential_authenticates_independently(self):
rows = {
"agent-new": {
"agent_id": "agent-new",
"agent_secret_hash": hash_agent_secret("unique-secret"),
"credential_created_at": NOW,
"credential_revoked_at": None,
}
}
self.assertEqual(
self.service(rows).authenticate("agent-new", "unique-secret"),
"agent-new",
)
def test_legacy_secret_never_authenticates_per_agent_identity(self):
settings.minerops_agent_id = "agent-new"
rows = {
"agent-new": {
"agent_id": "agent-new",
"agent_secret_hash": hash_agent_secret("unique-secret"),
"credential_created_at": NOW,
"credential_revoked_at": None,
}
}
with self.assertRaises(AgentAuthenticationFailed):
self.service(rows).authenticate("agent-new", "legacy-secret")
def test_wrong_per_agent_secret_is_rejected(self):
rows = {
"agent-new": {
"agent_id": "agent-new",
"agent_secret_hash": hash_agent_secret("unique-secret"),
"credential_created_at": NOW,
"credential_revoked_at": None,
}
}
with self.assertRaises(AgentAuthenticationFailed):
self.service(rows).authenticate("agent-new", "wrong-secret")
def test_revoked_per_agent_credential_never_falls_back(self):
settings.minerops_agent_id = "agent-new"
rows = {
"agent-new": {
"agent_id": "agent-new",
"agent_secret_hash": hash_agent_secret("unique-secret"),
"credential_created_at": NOW,
"credential_revoked_at": NOW,
}
}
for secret in ("unique-secret", "legacy-secret"):
with self.subTest(secret=secret), self.assertRaises(AgentCredentialRevoked):
self.service(rows).authenticate("agent-new", secret)
def test_unknown_identity_does_not_use_shared_secret(self):
with self.assertRaises(AgentIdentityUnauthorized):
self.service().authenticate("unknown-agent", "legacy-secret")
def test_missing_identity_or_secret_is_rejected(self):
with self.assertRaises(AgentAuthenticationFailed):
self.service().authenticate(None, "legacy-secret")
with self.assertRaises(AgentAuthenticationFailed):
self.service().authenticate("legacy-agent", None)
def test_legacy_path_reports_unavailable_if_not_configured(self):
settings.minerops_agent_shared_secret = ""
with self.assertRaises(AgentAuthenticationUnavailable):
self.service().authenticate("legacy-agent", "anything")
if __name__ == "__main__":
unittest.main()