"""Provider-agnostic user data model. This is the value object the :class:`AuthProvider` Protocol returns from :meth:`AuthProvider.authenticate` and :meth:`AuthProvider.get_user_by_id`. It is intentionally NOT the SQLAlchemy ``UserModel`` ORM class so that future IdP adapters (OIDC / SAML / LDAP) can construct one without touching a DB. The route layer converts ``User`` → ``UserResponse`` (the public API payload) at the boundary; ``User`` itself stays an internal type. """ from __future__ import annotations from pydantic import BaseModel, ConfigDict, EmailStr class User(BaseModel): """A user as known to the auth subsystem. Distinct from the SQLAlchemy ``UserModel`` ORM class because: - The auth provider may not have a local row at all (e.g. an OIDC user logged in via SSO who hasn't been provisioned yet — that's a future concern but the data model must allow it). - IdP-sourced attributes (department / title) are not in the SQLAlchemy model. """ model_config = ConfigDict(extra="forbid") id: str username: str email: EmailStr role: str = "member" is_active: bool = True is_terminal_authorized: bool = False is_server_terminal_authorized: bool = False created_at: str updated_at: str last_login_at: str | None = None created_by: str | None = None