"""Authentication schemas for login, token, and user management.""" from __future__ import annotations from datetime import datetime from typing import Optional from pydantic import BaseModel, EmailStr, Field, field_validator class LoginRequest(BaseModel): """Request schema for user login.""" username: str = Field(..., min_length=3, max_length=50, description="Username") password: str = Field(..., min_length=6, description="Password") class Token(BaseModel): """JWT token response.""" access_token: str token_type: str = "bearer" expires_in: int = Field(description="Token expiration time in seconds") class TokenData(BaseModel): """Data extracted from JWT token.""" username: Optional[str] = None user_id: Optional[int] = None role: Optional[str] = None # User schemas class UserBase(BaseModel): """Base user schema with common fields.""" username: str = Field(..., min_length=3, max_length=50) email: Optional[EmailStr] = None display_name: Optional[str] = Field(None, max_length=100) role: str = Field(default="admin", description="User role: admin, operator, viewer") is_active: bool = True class UserCreate(UserBase): """Schema for creating a new user.""" password: str = Field(..., min_length=6, max_length=128, description="Password (min 6 chars)") @field_validator('password') @classmethod def password_strength(cls, v: str) -> str: """Validate password has minimum complexity.""" if len(v) < 6: raise ValueError('Password must be at least 6 characters') return v class UserUpdate(BaseModel): """Schema for updating user (all fields optional).""" email: Optional[EmailStr] = None display_name: Optional[str] = Field(None, max_length=100) role: Optional[str] = None is_active: Optional[bool] = None class PasswordChange(BaseModel): """Schema for changing password.""" current_password: str = Field(..., description="Current password") new_password: str = Field(..., min_length=6, max_length=128, description="New password") @field_validator('new_password') @classmethod def password_strength(cls, v: str) -> str: if len(v) < 6: raise ValueError('Password must be at least 6 characters') return v class UserOut(BaseModel): """Schema for user output (without password).""" id: int username: str email: Optional[str] = None display_name: Optional[str] = None role: str is_active: bool is_superuser: bool created_at: datetime last_login: Optional[datetime] = None class Config: from_attributes = True class UserSetup(BaseModel): """Schema for initial admin setup (first user creation).""" username: str = Field(..., min_length=3, max_length=50) password: str = Field(..., min_length=6, max_length=128) email: Optional[EmailStr] = None display_name: Optional[str] = None @field_validator('password') @classmethod def password_strength(cls, v: str) -> str: if len(v) < 6: raise ValueError('Password must be at least 6 characters') return v class AuthStatus(BaseModel): """Response for auth status check.""" authenticated: bool user: Optional[UserOut] = None setup_required: bool = Field( default=False, description="True if no users exist and setup is needed" )