113 lines
3.4 KiB
Python
113 lines
3.4 KiB
Python
"""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"
|
|
)
|