"""Phase 14: SSO / SAML 2.0 Pydantic schemas.""" from __future__ import annotations from datetime import datetime from typing import Optional from uuid import UUID from pydantic import BaseModel, Field class SsoConfigCreate(BaseModel): is_enabled: bool = False provider_name: Optional[str] = None # SP settings (auto-derived if not provided) sp_entity_id: Optional[str] = None sp_acs_url: Optional[str] = None sp_slo_url: Optional[str] = None sp_certificate: Optional[str] = None sp_private_key: Optional[str] = None # IdP settings idp_entity_id: Optional[str] = None idp_sso_url: Optional[str] = None idp_slo_url: Optional[str] = None idp_certificate: Optional[str] = None # Attribute mapping attr_email: Optional[str] = "email" attr_username: Optional[str] = "username" attr_role: Optional[str] = "role" default_role: Optional[str] = "viewer" auto_provision: bool = True class SsoConfigUpdate(SsoConfigCreate): """All fields optional for partial updates.""" pass class SsoConfigOut(BaseModel): id: UUID is_enabled: bool provider_name: Optional[str] = None sp_entity_id: Optional[str] = None sp_acs_url: Optional[str] = None sp_slo_url: Optional[str] = None sp_certificate: Optional[str] = None # sp_private_key is intentionally OMITTED from responses idp_entity_id: Optional[str] = None idp_sso_url: Optional[str] = None idp_slo_url: Optional[str] = None idp_certificate: Optional[str] = None attr_email: Optional[str] = None attr_username: Optional[str] = None attr_role: Optional[str] = None default_role: Optional[str] = None auto_provision: bool = True created_at: Optional[datetime] = None updated_at: Optional[datetime] = None class Config: from_attributes = True class SsoLoginInitResponse(BaseModel): redirect_url: str = Field(..., description="URL to redirect the browser to for IdP login") request_id: str = Field(..., description="SAML AuthnRequest ID for validation") class SsoStatusResponse(BaseModel): enabled: bool provider_name: Optional[str] = None configured: bool = Field(..., description="True if IdP settings are present") login_url: Optional[str] = None # /sso/login URL