feat: add secure agent enrollment
This commit is contained in:
1 parent
82309db79a
commit
d9f0da694b
26 files changed
+2942
-76
No files matched your search
@@ -0,0 +1,98 @@
|
||||
"""Application service for secure one-time Agent enrollment."""
|
||||
|
||||
import secrets
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from app.agent_auth.service import hash_agent_secret
|
||||
from app.agent_enrollment.exceptions import (
|
||||
AgentEnrollmentConflict,
|
||||
AgentEnrollmentCredentialCollision,
|
||||
)
|
||||
from app.agent_enrollment.models import (
|
||||
AgentEnrollmentCreateRequest,
|
||||
AgentEnrollRequest,
|
||||
)
|
||||
|
||||
|
||||
MAX_GENERATION_ATTEMPTS = 3
|
||||
|
||||
|
||||
def generate_enrollment_token() -> str:
|
||||
return secrets.token_urlsafe(32)
|
||||
|
||||
|
||||
def generate_agent_secret() -> str:
|
||||
return secrets.token_urlsafe(32)
|
||||
|
||||
|
||||
def generate_agent_id() -> str:
|
||||
return f"agent-{uuid4().hex}"
|
||||
|
||||
|
||||
class AgentEnrollmentService:
|
||||
def __init__(self, repository):
|
||||
self.repository = repository
|
||||
|
||||
def create_enrollment(
|
||||
self,
|
||||
request: AgentEnrollmentCreateRequest,
|
||||
*,
|
||||
actor_user_id: UUID,
|
||||
) -> dict:
|
||||
for _ in range(MAX_GENERATION_ATTEMPTS):
|
||||
token = generate_enrollment_token()
|
||||
try:
|
||||
enrollment = self.repository.create_enrollment(
|
||||
token_hash=hash_agent_secret(token),
|
||||
site_id=request.site_id,
|
||||
agent_zone_id=request.agent_zone_id,
|
||||
display_name=request.display_name,
|
||||
actor_user_id=actor_user_id,
|
||||
)
|
||||
return {
|
||||
**enrollment,
|
||||
"enrollment_token": token,
|
||||
}
|
||||
except AgentEnrollmentCredentialCollision:
|
||||
continue
|
||||
|
||||
raise AgentEnrollmentConflict(
|
||||
"Unable to generate a unique enrollment token"
|
||||
)
|
||||
|
||||
def list_enrollments(
|
||||
self,
|
||||
*,
|
||||
site_id: UUID | None = None,
|
||||
agent_zone_id: UUID | None = None,
|
||||
limit: int = 100,
|
||||
) -> list[dict]:
|
||||
return self.repository.list_enrollments(
|
||||
site_id=site_id,
|
||||
agent_zone_id=agent_zone_id,
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
def redeem(self, request: AgentEnrollRequest) -> dict:
|
||||
token_hash = hash_agent_secret(request.token)
|
||||
|
||||
for _ in range(MAX_GENERATION_ATTEMPTS):
|
||||
agent_id = generate_agent_id()
|
||||
agent_secret = generate_agent_secret()
|
||||
|
||||
try:
|
||||
result = self.repository.redeem_enrollment(
|
||||
token_hash=token_hash,
|
||||
agent_id=agent_id,
|
||||
agent_secret_hash=hash_agent_secret(agent_secret),
|
||||
)
|
||||
return {
|
||||
**result,
|
||||
"agent_secret": agent_secret,
|
||||
}
|
||||
except AgentEnrollmentCredentialCollision:
|
||||
continue
|
||||
|
||||
raise AgentEnrollmentConflict(
|
||||
"Unable to generate unique Agent credentials"
|
||||
)
|
||||
Reference in new issue
Block a user