Build and Push / build (release) Successful in 1m16s
- Add PostgreSQL database with async SQLAlchemy - Add Alembic migrations for schema management - Add User, Role, UserPreferences, ApiKey models - Add auth endpoints: /auth/me, /auth/users, /auth/users/sync-from-authentik - Add token validation via Authentik userinfo endpoint - Add bulk user sync from Authentik admin API - Add database health check to diagnostics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
111 lines
4.1 KiB
Python
111 lines
4.1 KiB
Python
"""
|
|
Authentication Schemas
|
|
|
|
Pydantic models for auth request/response payloads.
|
|
"""
|
|
import uuid
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from pydantic import Field
|
|
|
|
from src.base_schema import BaseSchema
|
|
|
|
|
|
class AuthSyncRequest(BaseSchema):
|
|
"""
|
|
Request payload for POST /auth/sync
|
|
|
|
The client sends this after obtaining an OIDC token from Authentik.
|
|
The access_token is validated against Authentik's userinfo endpoint.
|
|
"""
|
|
|
|
access_token: str = Field(
|
|
...,
|
|
description="OIDC access token from Authentik",
|
|
)
|
|
|
|
|
|
class RoleSchema(BaseSchema):
|
|
"""Role information in domain:action format"""
|
|
|
|
name: str = Field(..., description="Role name (e.g., 'control-room:admin')")
|
|
domain: str = Field(..., description="Permission domain (e.g., 'control-room')")
|
|
action: str = Field(..., description="Permission action (e.g., 'admin')")
|
|
|
|
|
|
class UserPreferencesSchema(BaseSchema):
|
|
"""User preferences"""
|
|
|
|
theme: str = Field(default="system", description="Theme preference: system, light, dark")
|
|
default_room: str = Field(default="front-hall", description="Default room for housekeeping")
|
|
preferences_json: dict = Field(default_factory=dict, description="Extended preferences")
|
|
|
|
|
|
class UserSchema(BaseSchema):
|
|
"""User information returned from sync"""
|
|
|
|
id: uuid.UUID = Field(..., description="Internal user ID")
|
|
authentik_id: uuid.UUID = Field(..., description="Authentik user ID")
|
|
email: str = Field(..., description="User email")
|
|
name: str = Field(..., description="Display name")
|
|
avatar_url: Optional[str] = Field(None, description="Profile picture URL")
|
|
created_at: datetime = Field(..., description="Account creation timestamp")
|
|
last_login: Optional[datetime] = Field(None, description="Last login timestamp")
|
|
|
|
|
|
class AuthSyncResponse(BaseSchema):
|
|
"""
|
|
Response from POST /auth/sync
|
|
|
|
Contains the synced user profile, roles, and preferences.
|
|
"""
|
|
|
|
user: UserSchema = Field(..., description="User profile")
|
|
roles: list[RoleSchema] = Field(..., description="User's permission roles")
|
|
preferences: UserPreferencesSchema = Field(..., description="User preferences")
|
|
is_new_user: bool = Field(..., description="True if user was just created")
|
|
|
|
|
|
class TokenInfoSchema(BaseSchema):
|
|
"""
|
|
Token information from Authentik userinfo endpoint
|
|
|
|
This is what Authentik returns when validating an access token.
|
|
"""
|
|
|
|
sub: str = Field(..., description="Subject (Authentik user ID)")
|
|
email: str = Field(..., description="User email")
|
|
name: Optional[str] = Field(None, description="Display name")
|
|
preferred_username: Optional[str] = Field(None, description="Username")
|
|
groups: list[str] = Field(default_factory=list, description="Group memberships")
|
|
picture: Optional[str] = Field(None, description="Profile picture URL")
|
|
|
|
|
|
class UserListItemSchema(BaseSchema):
|
|
"""User item for list display"""
|
|
|
|
id: uuid.UUID = Field(..., description="Internal user ID")
|
|
email: str = Field(..., description="User email")
|
|
name: str = Field(..., description="Display name")
|
|
avatar_url: Optional[str] = Field(None, description="Profile picture URL")
|
|
created_at: datetime = Field(..., description="Account creation timestamp")
|
|
last_login: Optional[datetime] = Field(None, description="Last login timestamp")
|
|
roles: list[str] = Field(default_factory=list, description="Role names")
|
|
|
|
|
|
class UsersListResponse(BaseSchema):
|
|
"""Response from GET /auth/users"""
|
|
|
|
items: list[UserListItemSchema] = Field(..., description="List of users")
|
|
total: int = Field(..., description="Total count of users")
|
|
|
|
|
|
class BulkSyncResultSchema(BaseSchema):
|
|
"""Result from bulk sync operation"""
|
|
|
|
created: int = Field(..., description="Number of users created")
|
|
updated: int = Field(..., description="Number of users updated")
|
|
failed: int = Field(..., description="Number of users that failed to sync")
|
|
total_in_authentik: int = Field(..., description="Total users in Authentik")
|
|
errors: list[str] = Field(default_factory=list, description="Error messages for failed syncs")
|