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"
)