fix telegram
This commit is contained in:
344
deploy-package-20260223-151231/shared/auth/models.py
Normal file
344
deploy-package-20260223-151231/shared/auth/models.py
Normal file
@@ -0,0 +1,344 @@
|
||||
"""
|
||||
Authentication Pydantic Models pentru ROA2WEB
|
||||
|
||||
Acest modul definește toate modelele de date folosite în sistemul de autentificare,
|
||||
incluzând request/response models și modele pentru user data.
|
||||
|
||||
Modelele acoperă:
|
||||
- Login request și response
|
||||
- Token data și management
|
||||
- User information și permisiuni
|
||||
- Company access control
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, validator, EmailStr
|
||||
from typing import List, Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class PermissionType(str, Enum):
|
||||
"""Tipurile de permisiuni disponibile în sistem"""
|
||||
READ = "read"
|
||||
WRITE = "write"
|
||||
DELETE = "delete"
|
||||
ADMIN = "admin"
|
||||
REPORTS = "reports"
|
||||
EXPORT = "export"
|
||||
|
||||
|
||||
class TokenType(str, Enum):
|
||||
"""Tipurile de token-uri JWT"""
|
||||
ACCESS = "access"
|
||||
REFRESH = "refresh"
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
"""Model pentru request-ul de login"""
|
||||
username: str = Field(
|
||||
...,
|
||||
min_length=3,
|
||||
max_length=50,
|
||||
description="Numele utilizatorului",
|
||||
example="admin"
|
||||
)
|
||||
password: str = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
description="Parola utilizatorului"
|
||||
)
|
||||
remember_me: bool = Field(
|
||||
default=False,
|
||||
description="Dacă să păstreze utilizatorul autentificat mai mult timp"
|
||||
)
|
||||
server_id: Optional[str] = Field(
|
||||
default=None,
|
||||
description="ID-ul serverului Oracle pentru autentificare (opțional în modul single-server)",
|
||||
example="romfast"
|
||||
)
|
||||
|
||||
@validator('username')
|
||||
def username_alphanumeric(cls, v):
|
||||
"""Validează că username-ul conține doar caractere permise (inclusiv email-uri)
|
||||
|
||||
Pentru backward compatibility:
|
||||
- Permite username-uri clasice: litere, cifre, spații, _, -
|
||||
- Permite email-uri pentru noul flow multi-server: @, .
|
||||
"""
|
||||
# Permitem litere, cifre, spații, _, -, @, și . (pentru email-uri)
|
||||
allowed_chars = v.replace(' ', '').replace('_', '').replace('-', '').replace('@', '').replace('.', '')
|
||||
if not allowed_chars.isalnum():
|
||||
raise ValueError('Username-ul poate conține doar litere, cifre, spații, _, -, @ și .')
|
||||
|
||||
# Detectăm dacă este email sau username clasic
|
||||
if '@' in v:
|
||||
# Email: păstrăm lowercase pentru consistență cu email-urile
|
||||
return v.lower().strip()
|
||||
else:
|
||||
# Username clasic: uppercase pentru consistență cu Oracle
|
||||
return v.upper().strip()
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
"""Model pentru răspunsul de autentificare cu token-uri"""
|
||||
access_token: str = Field(description="JWT access token")
|
||||
refresh_token: Optional[str] = Field(
|
||||
default=None,
|
||||
description="JWT refresh token (opțional)"
|
||||
)
|
||||
token_type: str = Field(
|
||||
default="bearer",
|
||||
description="Tipul token-ului (întotdeauna 'bearer')"
|
||||
)
|
||||
expires_in: int = Field(
|
||||
description="Timpul de expirare al access token-ului în secunde"
|
||||
)
|
||||
user: 'CurrentUser' = Field(description="Informațiile utilizatorului autentificat")
|
||||
|
||||
|
||||
class RefreshTokenRequest(BaseModel):
|
||||
"""Model pentru request-ul de refresh token"""
|
||||
refresh_token: str = Field(description="Refresh token-ul valid")
|
||||
|
||||
|
||||
class LogoutRequest(BaseModel):
|
||||
"""Model pentru request-ul de logout"""
|
||||
refresh_token: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Refresh token de invalidat (opțional)"
|
||||
)
|
||||
|
||||
|
||||
class CurrentUser(BaseModel):
|
||||
"""Model pentru utilizatorul curent autentificat"""
|
||||
username: str = Field(description="Numele utilizatorului")
|
||||
user_id: Optional[int] = Field(
|
||||
default=None,
|
||||
description="ID-ul utilizatorului în baza de date"
|
||||
)
|
||||
email: Optional[EmailStr] = Field(
|
||||
default=None,
|
||||
description="Email-ul utilizatorului"
|
||||
)
|
||||
companies: List[str] = Field(
|
||||
default_factory=list,
|
||||
description="Lista codurilor firmelor la care utilizatorul are acces"
|
||||
)
|
||||
permissions: List[PermissionType] = Field(
|
||||
default_factory=lambda: [PermissionType.READ],
|
||||
description="Lista permisiunilor utilizatorului"
|
||||
)
|
||||
is_active: bool = Field(
|
||||
default=True,
|
||||
description="Dacă utilizatorul este activ"
|
||||
)
|
||||
last_login: Optional[datetime] = Field(
|
||||
default=None,
|
||||
description="Data ultimei autentificări"
|
||||
)
|
||||
|
||||
@validator('companies')
|
||||
def companies_not_empty_if_active(cls, v, values):
|
||||
"""Validează că utilizatorii activi au cel puțin o firmă"""
|
||||
if values.get('is_active', True) and not v:
|
||||
raise ValueError('Utilizatorii activi trebuie să aibă acces la cel puțin o firmă')
|
||||
return v
|
||||
|
||||
|
||||
class UserCompany(BaseModel):
|
||||
"""Model pentru o firmă la care utilizatorul are acces"""
|
||||
code: str = Field(description="Codul firmei (schema Oracle)")
|
||||
name: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Numele firmei (dacă este disponibil)"
|
||||
)
|
||||
permissions: List[PermissionType] = Field(
|
||||
default_factory=lambda: [PermissionType.READ],
|
||||
description="Permisiunile utilizatorului pentru această firmă"
|
||||
)
|
||||
is_default: bool = Field(
|
||||
default=False,
|
||||
description="Dacă aceasta este firma implicită pentru utilizator"
|
||||
)
|
||||
|
||||
|
||||
class CompanyAccessRequest(BaseModel):
|
||||
"""Model pentru verificarea accesului la o firmă"""
|
||||
company_code: str = Field(description="Codul firmei de verificat")
|
||||
required_permissions: Optional[List[PermissionType]] = Field(
|
||||
default=None,
|
||||
description="Permisiunile necesare (opțional)"
|
||||
)
|
||||
|
||||
|
||||
class CompanyAccessResponse(BaseModel):
|
||||
"""Model pentru răspunsul de verificare acces firmă"""
|
||||
has_access: bool = Field(description="Dacă utilizatorul are acces")
|
||||
company: Optional[UserCompany] = Field(
|
||||
default=None,
|
||||
description="Detaliile firmei dacă utilizatorul are acces"
|
||||
)
|
||||
missing_permissions: Optional[List[PermissionType]] = Field(
|
||||
default=None,
|
||||
description="Permisiunile lipsă (dacă aplicabil)"
|
||||
)
|
||||
|
||||
|
||||
class AuthError(BaseModel):
|
||||
"""Model pentru erorile de autentificare"""
|
||||
error: str = Field(description="Tipul erorii")
|
||||
error_description: str = Field(description="Descrierea detaliată a erorii")
|
||||
error_code: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Codul de eroare pentru procesare automată"
|
||||
)
|
||||
|
||||
|
||||
class AuthStats(BaseModel):
|
||||
"""Model pentru statisticile de autentificare"""
|
||||
total_users: int = Field(description="Numărul total de utilizatori")
|
||||
active_sessions: int = Field(description="Sesiuni active curente")
|
||||
cache_hit_ratio: float = Field(
|
||||
description="Rata de hit a cache-ului pentru date utilizatori"
|
||||
)
|
||||
last_cleanup: Optional[datetime] = Field(
|
||||
default=None,
|
||||
description="Ultima curățare a cache-ului"
|
||||
)
|
||||
|
||||
|
||||
class PasswordChangeRequest(BaseModel):
|
||||
"""Model pentru schimbarea parolei (pentru viitor)"""
|
||||
current_password: str = Field(description="Parola curentă")
|
||||
new_password: str = Field(
|
||||
min_length=8,
|
||||
description="Noua parolă (minim 8 caractere)"
|
||||
)
|
||||
confirm_password: str = Field(description="Confirmarea noii parole")
|
||||
|
||||
@validator('confirm_password')
|
||||
def passwords_match(cls, v, values):
|
||||
"""Validează că parolele coincid"""
|
||||
if 'new_password' in values and v != values['new_password']:
|
||||
raise ValueError('Parolele nu coincid')
|
||||
return v
|
||||
|
||||
|
||||
class SessionInfo(BaseModel):
|
||||
"""Model pentru informațiile despre sesiune"""
|
||||
session_id: str = Field(description="ID-ul sesiunii")
|
||||
username: str = Field(description="Numele utilizatorului")
|
||||
created_at: datetime = Field(description="Data creării sesiunii")
|
||||
last_activity: datetime = Field(description="Ultima activitate")
|
||||
ip_address: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Adresa IP a utilizatorului"
|
||||
)
|
||||
user_agent: Optional[str] = Field(
|
||||
default=None,
|
||||
description="User agent-ul browserului"
|
||||
)
|
||||
is_active: bool = Field(
|
||||
default=True,
|
||||
description="Dacă sesiunea este încă activă"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MULTI-ORACLE IDENTITY CHECK MODELS (US-004, US-013)
|
||||
# ============================================================================
|
||||
|
||||
class CheckIdentityRequest(BaseModel):
|
||||
"""
|
||||
Model pentru verificarea identității în sistemul multi-Oracle (US-013)
|
||||
|
||||
Suportă atât email cât și username:
|
||||
- Cu '@': tratează ca email și caută în EmailServerCache
|
||||
- Fără '@': tratează ca username și caută în Oracle pe toate serverele
|
||||
"""
|
||||
identity: str = Field(
|
||||
...,
|
||||
min_length=2,
|
||||
max_length=100,
|
||||
description="Email sau username de verificat",
|
||||
example="user@example.com sau MARIUS"
|
||||
)
|
||||
|
||||
@validator('identity')
|
||||
def validate_identity(cls, v):
|
||||
"""Validează și normalizează identitatea"""
|
||||
stripped = v.strip()
|
||||
if not stripped:
|
||||
raise ValueError('Identitatea nu poate fi goală')
|
||||
# Pentru email-uri, normalizăm la lowercase
|
||||
if '@' in stripped:
|
||||
return stripped.lower()
|
||||
# Pentru username-uri, normalizăm la uppercase (convenție Oracle)
|
||||
return stripped.upper()
|
||||
|
||||
|
||||
class CheckEmailRequest(BaseModel):
|
||||
"""
|
||||
Model pentru verificarea email-ului în sistemul multi-Oracle (US-004)
|
||||
|
||||
DEPRECATED: Folosește CheckIdentityRequest pentru suport dual email/username
|
||||
Păstrat pentru backward compatibility.
|
||||
"""
|
||||
email: EmailStr = Field(
|
||||
...,
|
||||
description="Adresa email a utilizatorului de verificat",
|
||||
example="user@example.com"
|
||||
)
|
||||
|
||||
|
||||
class ServerInfo(BaseModel):
|
||||
"""Informații despre un server Oracle disponibil pentru utilizator"""
|
||||
id: str = Field(description="ID-ul serverului (ex: 'romfast')")
|
||||
name: str = Field(description="Numele human-readable al serverului (ex: 'Romfast - Producție')")
|
||||
|
||||
|
||||
class CheckIdentityResponse(BaseModel):
|
||||
"""
|
||||
Răspunsul pentru verificarea identității (email sau username) (US-013).
|
||||
|
||||
SECURITATE:
|
||||
- Pentru identitate validă: returnează exists=True și lista serverelor
|
||||
- Pentru identitate invalidă: returnează exists=False și listă goală de servere
|
||||
(NU expunem serverele disponibile pentru a preveni enumerarea!)
|
||||
"""
|
||||
exists: bool = Field(
|
||||
description="True dacă identitatea există în sistem pe cel puțin un server"
|
||||
)
|
||||
servers: List[ServerInfo] = Field(
|
||||
default_factory=list,
|
||||
description="Lista serverelor pe care există identitatea (goală pentru identitate invalidă)"
|
||||
)
|
||||
identity_type: str = Field(
|
||||
default="unknown",
|
||||
description="Tipul identității: 'email' sau 'username'"
|
||||
)
|
||||
|
||||
|
||||
class CheckEmailResponse(BaseModel):
|
||||
"""
|
||||
Răspunsul pentru verificarea email-ului (US-004).
|
||||
|
||||
DEPRECATED: Folosește CheckIdentityResponse pentru suport dual email/username
|
||||
Păstrat pentru backward compatibility.
|
||||
|
||||
SECURITATE:
|
||||
- Pentru email valid: returnează exists=True și lista serverelor
|
||||
- Pentru email invalid: returnează exists=False și listă goală de servere
|
||||
(NU expunem serverele disponibile pentru a preveni enumerarea!)
|
||||
"""
|
||||
exists: bool = Field(
|
||||
description="True dacă email-ul există în sistem pe cel puțin un server"
|
||||
)
|
||||
servers: List[ServerInfo] = Field(
|
||||
default_factory=list,
|
||||
description="Lista serverelor pe care există email-ul (goală pentru email invalid)"
|
||||
)
|
||||
|
||||
|
||||
# Update la forward references pentru TokenResponse
|
||||
TokenResponse.model_rebuild()
|
||||
Reference in New Issue
Block a user