"""User schemas.""" from __future__ import annotations import re from pydantic import BaseModel, EmailStr, Field, field_validator def _validate_password_complexity(password: str) -> str: """Validate password meets complexity requirements. Requires: min 8 chars, at least 1 uppercase, 1 lowercase, 1 digit. """ if len(password) < 8: raise ValueError("Password must be at least 8 characters") if not re.search(r"[A-Z]", password): raise ValueError("Password must contain at least one uppercase letter") if not re.search(r"[a-z]", password): raise ValueError("Password must contain at least one lowercase letter") if not re.search(r"\d", password): raise ValueError("Password must contain at least one digit") return password class UserCreate(BaseModel): email: EmailStr = Field(..., examples=["user@leocrm.local"]) name: str = Field(..., min_length=1, max_length=200, examples=["John Doe"]) password: str = Field(..., min_length=8, examples=["secure-password"]) role: str = Field(default="viewer", examples=["viewer"]) role_id: str | None = Field(default=None, description="UUID of a custom Role", examples=["550e8400-e29b-41d4-a716-446655440000"]) is_active: bool = True @field_validator("password") @classmethod def validate_password(cls, v: str) -> str: return _validate_password_complexity(v) class UserUpdate(BaseModel): name: str | None = Field(None, min_length=1, max_length=200) first_name: str | None = Field(None, max_length=100) last_name: str | None = Field(None, max_length=100) email: EmailStr | None = None avatar_url: str | None = None role: str | None = None role_id: str | None = Field(default=None, description="UUID of a custom Role; send null to clear") is_active: bool | None = None is_system_admin: bool | None = Field(None, description="System admin flag — requires system admin to set") current_password: str | None = Field(None, description="Required when changing password") new_password: str | None = Field(None, min_length=8, description="New password") class UserResponse(BaseModel): id: str email: str name: str first_name: str | None = None last_name: str | None = None avatar_url: str | None = None role: str role_id: str | None = None is_active: bool is_system_admin: bool = False tenant_id: str class PaginatedUsers(BaseModel): items: list[UserResponse] total: int page: int page_size: int