43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""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
|