Fix .gitignore and add missing authentication source files

This commit fixes overly broad .gitignore patterns that were excluding
important source code files from version control. Previously, wildcard
patterns like *auth*, *token*, *secret*, *connection*, and *credential*
were excluding ALL files containing these words, including critical
application code.

Changes:
- Updated .gitignore with specific patterns for sensitive config files
  (*.json, *.txt, *.yml, *.yaml extensions only)
- Removed broad wildcards that excluded source code files

Added missing source files:
- shared/auth/ (9 files): Complete authentication system
  - JWT handler, middleware, auth service, models, routes
- reports-app/backend/app/routers/auth.py: Authentication API router
- reports-app/backend/app/auth_middleware_wrapper.py: Middleware wrapper
- reports-app/frontend/src/stores/auth.js: Vue.js auth store
- reports-app/frontend/tests/: E2E tests and fixtures for auth
- reports-app/telegram-bot/app/auth/: Telegram auth linking module
- deployment/windows/scripts/Setup-ClaudeAuth.ps1: Windows deployment script
- security/secrets_scanner.py: Security scanning utility

These files are essential for the application to function and should
have been included in the initial commit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-25 15:02:28 +03:00
parent 6b13ffa183
commit f42eff71a6
19 changed files with 5035 additions and 21 deletions

231
shared/auth/models.py Normal file
View File

@@ -0,0 +1,231 @@
"""
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"
)
@validator('username')
def username_alphanumeric(cls, v):
"""Validează că username-ul conține doar caractere permise (inclusiv spații)"""
# Permitem litere, cifre, spații, _, și -
allowed_chars = v.replace(' ', '').replace('_', '').replace('-', '')
if not allowed_chars.isalnum():
raise ValueError('Username-ul poate conține doar litere, cifre, spații, _ și -')
return v.upper() # Convertim la uppercase pentru consistență cu Oracle
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ă"
)
# Update la forward references pentru TokenResponse
TokenResponse.model_rebuild()