160 lines
4.6 KiB
Python
160 lines
4.6 KiB
Python
"""HTTP routes for Agent enrollment and bootstrap."""
|
|
|
|
from typing import Annotated
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status
|
|
|
|
from app.agent_enrollment.dependencies import get_agent_enrollment_service
|
|
from app.agent_enrollment.exceptions import (
|
|
AgentEnrollmentConflict,
|
|
AgentEnrollmentNotFound,
|
|
AgentEnrollmentTokenInvalid,
|
|
)
|
|
from app.agent_enrollment.models import (
|
|
AgentEnrollmentCreateRequest,
|
|
AgentEnrollmentCreateResponse,
|
|
AgentEnrollmentListResponse,
|
|
AgentEnrollRequest,
|
|
AgentEnrollResponse,
|
|
AgentWindowsPackageRequest,
|
|
)
|
|
from app.agent_enrollment.service import AgentEnrollmentService
|
|
from app.agent_packages.windows import (
|
|
AgentWindowsPackageError,
|
|
AgentWindowsPackageSourceError,
|
|
build_windows_agent_package,
|
|
normalize_hub_url,
|
|
)
|
|
from app.identity import require_permission
|
|
|
|
|
|
router = APIRouter(tags=["agent-enrollment"])
|
|
|
|
EnrollmentService = Annotated[
|
|
AgentEnrollmentService,
|
|
Depends(get_agent_enrollment_service),
|
|
]
|
|
EnrollmentViewer = Annotated[
|
|
dict,
|
|
Depends(require_permission("agents.view")),
|
|
]
|
|
EnrollmentManager = Annotated[
|
|
dict,
|
|
Depends(require_permission("agents.manage")),
|
|
]
|
|
|
|
|
|
@router.post(
|
|
"/api/agent-enrollments",
|
|
response_model=AgentEnrollmentCreateResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
)
|
|
def create_agent_enrollment(
|
|
request: AgentEnrollmentCreateRequest,
|
|
response: Response,
|
|
current_user: EnrollmentManager,
|
|
service: EnrollmentService,
|
|
) -> AgentEnrollmentCreateResponse:
|
|
response.headers["Cache-Control"] = "no-store"
|
|
|
|
try:
|
|
enrollment = service.create_enrollment(
|
|
request,
|
|
actor_user_id=UUID(str(current_user["id"])),
|
|
)
|
|
return AgentEnrollmentCreateResponse(data=enrollment)
|
|
except AgentEnrollmentNotFound as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except AgentEnrollmentConflict as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get(
|
|
"/api/agent-enrollments",
|
|
response_model=AgentEnrollmentListResponse,
|
|
)
|
|
def list_agent_enrollments(
|
|
current_user: EnrollmentViewer,
|
|
service: EnrollmentService,
|
|
site_id: UUID | None = Query(default=None),
|
|
agent_zone_id: UUID | None = Query(default=None),
|
|
limit: int = Query(default=100, ge=1, le=500),
|
|
) -> AgentEnrollmentListResponse:
|
|
del current_user
|
|
|
|
return AgentEnrollmentListResponse(
|
|
data=service.list_enrollments(
|
|
site_id=site_id,
|
|
agent_zone_id=agent_zone_id,
|
|
limit=limit,
|
|
)
|
|
)
|
|
|
|
|
|
|
|
@router.post("/api/agent-enrollments/windows-package")
|
|
def download_windows_agent_package(
|
|
package_request: AgentWindowsPackageRequest,
|
|
http_request: Request,
|
|
current_user: EnrollmentManager,
|
|
) -> Response:
|
|
del current_user
|
|
|
|
browser_origin = http_request.headers.get("origin", "").strip()
|
|
|
|
try:
|
|
hub_url = normalize_hub_url(browser_origin)
|
|
except AgentWindowsPackageError as exc:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=(
|
|
"Windows Agent package downloads require "
|
|
"a valid browser Origin"
|
|
),
|
|
) from exc
|
|
|
|
try:
|
|
package = build_windows_agent_package(
|
|
enrollment_token=package_request.token,
|
|
hub_url=hub_url,
|
|
)
|
|
except AgentWindowsPackageSourceError as exc:
|
|
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
except AgentWindowsPackageError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
return Response(
|
|
content=package,
|
|
media_type="application/zip",
|
|
headers={
|
|
"Cache-Control": "no-store",
|
|
"Pragma": "no-cache",
|
|
"Content-Disposition": (
|
|
'attachment; filename="MinerOpsAgent-Windows.zip"'
|
|
),
|
|
},
|
|
)
|
|
|
|
@router.post(
|
|
"/api/agent/enroll",
|
|
response_model=AgentEnrollResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
)
|
|
def enroll_agent(
|
|
request: AgentEnrollRequest,
|
|
response: Response,
|
|
service: EnrollmentService,
|
|
) -> AgentEnrollResponse:
|
|
response.headers["Cache-Control"] = "no-store"
|
|
|
|
try:
|
|
return AgentEnrollResponse(data=service.redeem(request))
|
|
except AgentEnrollmentTokenInvalid as exc:
|
|
raise HTTPException(
|
|
status_code=401,
|
|
detail="Invalid or expired enrollment token",
|
|
) from exc
|
|
except AgentEnrollmentConflict as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc)) from exc
|