from datetime import datetime from typing import Any from uuid import UUID from pydantic import BaseModel, Field class UserOut(BaseModel): id: UUID username: str display_name: str | None = None email: str | None = None created_at: datetime model_config = {"from_attributes": True} class DeviceOut(BaseModel): id: UUID user_id: UUID name: str platform: str public_key_alg: str is_revoked: bool revoked_at: datetime | None = None last_seen_at: datetime | None = None created_at: datetime model_config = {"from_attributes": True} class EnrollmentStartRequest(BaseModel): username: str = Field(min_length=1, max_length=255) display_name: str | None = None email: str | None = None class EnrollmentStartResponse(BaseModel): enrollment_id: UUID qr_payload: dict[str, Any] qr_png_base64: str expires_at: datetime class EnrollmentFinishRequest(BaseModel): enrollment_token: str device_name: str = Field(min_length=1, max_length=255) public_key_pem: str public_key_alg: str = "ES256" fcm_token: str | None = None app_version: str | None = None attestation: dict[str, Any] | None = None class EnrollmentFinishResponse(BaseModel): device_id: UUID user_id: UUID class ChallengeCreateRequest(BaseModel): username: str relying_party: str requester_ip: str location: str | None = None ttl_seconds: int | None = Field(default=None, ge=10, le=300) oidc_state: str | None = None class ChallengeOut(BaseModel): id: UUID user_id: UUID device_id: UUID | None status: str relying_party: str requester_ip: str location: str | None payload: dict[str, Any] issued_at: datetime expires_at: datetime responded_at: datetime | None model_config = {"from_attributes": True} class ChallengeApprovalRequest(BaseModel): device_id: UUID signature: str payload: dict[str, Any] class DenyRequest(BaseModel): device_id: UUID | None = None reason: str | None = Field(default=None, max_length=255) class AuditOut(BaseModel): id: UUID actor: str | None action: str target_type: str | None target_id: str | None ip_address: str | None metadata_json: dict[str, Any] | None created_at: datetime model_config = {"from_attributes": True}