Files
roa2web-service-auto/shared/auth
Marius Mutu a7a1bef375 Add missing test files and update .gitignore to allow test files
This commit addresses the overly restrictive .gitignore pattern that
was excluding all test files (test_*.py), including legitimate pytest
and unittest test suites essential for code quality and CI/CD.

Changes to .gitignore:
- Added negation patterns !**/tests/test_*.py and !**/test_*.py
  to allow proper test files while still blocking temporary scripts
- This enables pytest test suites to be tracked by git

Added test files (17 files):

Telegram Bot Tests (15 files):
- reports-app/telegram-bot/tests/test_auth.py
  Tests for authentication and account linking flow
- reports-app/telegram-bot/tests/test_callbacks.py
  Tests for callback query handlers
- reports-app/telegram-bot/tests/test_formatters.py
  Tests for message formatting utilities
- reports-app/telegram-bot/tests/test_formatters_extended.py
  Extended formatter tests
- reports-app/telegram-bot/tests/test_handlers_menu.py
  Tests for menu handlers
- reports-app/telegram-bot/tests/test_helpers.py
  Tests for helper functions
- reports-app/telegram-bot/tests/test_helpers_extended.py
  Extended helper tests
- reports-app/telegram-bot/tests/test_helpers_real.py
  Real integration tests for helpers
- reports-app/telegram-bot/tests/test_helpers_real_simple.py
  Simplified integration tests
- reports-app/telegram-bot/tests/test_login_flow.py
  Complete login flow integration tests
- reports-app/telegram-bot/tests/test_menus.py
  Menu system tests
- reports-app/telegram-bot/tests/test_session_company.py
  Session and company management tests
- reports-app/telegram-bot/test_claude_integration.py
  Manual integration test (Claude AI)
- reports-app/telegram-bot/test_claude_response.py
  Response formatting test
- reports-app/telegram-bot/test_db.py
  Database operations manual test

Shared Module Tests (2 files):
- shared/auth/test_auth.py
  Authentication system tests
- shared/database/test_pool.py
  Oracle connection pool tests

Security verification:
 All test files use mock objects, fixtures, and environment variables
 No hardcoded credentials or secrets found
 Safe for version control

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 15:09:43 +03:00
..

🔐 ROA2WEB Shared Authentication System

Sistem de autentificare JWT partajat între toate aplicațiile din ecosistemul ROA2WEB, integrat cu Oracle Database și optimizat pentru aplicații FastAPI.

📋 Table of Contents

Features

Core Features

  • JWT Authentication: Secure token-based authentication cu access și refresh tokens
  • Oracle Database Integration: Folosește pack_drepturi.verificautilizator pentru autentificare
  • Multi-Company Support: Acces controlat la multiple firme/schemas Oracle
  • Permission System: Sistem granular de permisiuni (read, write, admin, reports)
  • FastAPI Integration: Dependencies și middleware native pentru FastAPI
  • Rate Limiting: Protecție împotriva brute force attacks
  • Caching: Cache inteligent pentru performanță optimă

Security Features

  • Token Expiration: Configurabil pentru access și refresh tokens
  • SQL Injection Protection: Parametri legați în toate query-urile
  • Rate Limiting: Configurabil per IP și endpoint
  • CORS Protection: Configurare flexibilă pentru origins
  • Header Security: Security headers automate
  • Token Blacklisting: Suport pentru invalidarea token-urilor (în dezvoltare)

🏗️ Architecture

ROA2WEB Authentication Flow:
┌─────────────┐    ┌──────────────┐    ┌─────────────┐    ┌──────────────┐
│   Client    │───▶│   FastAPI    │───▶│    JWT      │───▶│   Oracle     │
│ (Frontend)  │    │ Application  │    │  Handler    │    │  Database    │
└─────────────┘    └──────────────┘    └─────────────┘    └──────────────┘
                           │                    │                    │
                           ▼                    ▼                    ▼
                   ┌──────────────┐    ┌─────────────┐    ┌──────────────┐
                   │ Auth Service │    │ Middleware  │    │ User Cache   │
                   └──────────────┘    └─────────────┘    └──────────────┘

Components Overview

shared/auth/
├── jwt_handler.py      # JWT token creation și validation
├── auth_service.py     # Oracle database integration
├── models.py           # Pydantic models pentru data validation
├── middleware.py       # FastAPI middleware pentru auto-authentication
├── dependencies.py     # FastAPI dependencies pentru protected routes
├── routes.py           # Pre-built authentication routes
├── test_auth.py        # Comprehensive test suite
├── demo_app.py         # Demo application cu examples
└── README.md           # Această documentație

🚀 Quick Start

1. Environment Setup

# Copy și configurează environment variables
cp .env.example .env

# Edit .env cu configurările tale
JWT_SECRET_KEY=your-super-secret-jwt-key-change-in-production
ORACLE_USER=your_oracle_username
ORACLE_PASSWORD=your_oracle_password
ORACLE_DSN=your_oracle_connection_string

2. Basic Integration

from fastapi import FastAPI, Depends
from roa2web.shared.auth import (
    AuthenticationMiddleware, create_auth_router, 
    get_current_user, CurrentUser
)
from roa2web.shared.database import oracle_pool

app = FastAPI(title="My ROA2WEB App")

# Add authentication middleware
app.add_middleware(
    AuthenticationMiddleware,
    excluded_paths=["/", "/docs", "/health", "/auth/login"]
)

# Include authentication routes
auth_router = create_auth_router()
app.include_router(auth_router)

@app.on_event("startup")
async def startup():
    await oracle_pool.initialize()

@app.get("/protected")
async def protected_endpoint(
    current_user: CurrentUser = Depends(get_current_user)
):
    return {"message": f"Hello {current_user.username}!"}

3. Test Authentication

# Start demo application
cd roa2web/shared/auth
python demo_app.py

# Open browser
open http://localhost:8000/docs

# Login prin Swagger UI cu credențialele Oracle

🧩 Components

JWT Handler (jwt_handler.py)

Gestionează crearea, validarea și refresh-ul token-urilor JWT.

from roa2web.shared.auth import jwt_handler

# Create access token
token = jwt_handler.create_access_token(
    username="admin",
    companies=["COMP1", "COMP2"],
    permissions=["read", "write", "reports"]
)

# Verify token
token_data = jwt_handler.verify_token(token)
if token_data:
    print(f"Valid token for user: {token_data.username}")

Auth Service (auth_service.py)

Integrează cu Oracle Database pentru autentificare și management utilizatori.

from roa2web.shared.auth import auth_service

# Authenticate user
success, token_response, error = await auth_service.authenticate_and_create_tokens(
    "username", "password"
)

if success:
    print(f"Access token: {token_response.access_token}")
else:
    print(f"Authentication failed: {error}")

FastAPI Dependencies (dependencies.py)

Oferă dependencies pentru protejarea endpoint-urilor.

from fastapi import Depends
from roa2web.shared.auth import (
    get_current_user, require_company_access, 
    require_permissions, PermissionType
)

@app.get("/admin-only")
async def admin_endpoint(
    user: CurrentUser = Depends(require_permissions([PermissionType.ADMIN]))
):
    return {"message": "Admin access granted"}

@app.get("/company/{company_code}/data")
async def company_data(
    company_code: str,
    user: CurrentUser = Depends(require_company_access(company_code))
):
    return {"company": company_code, "data": "..."}

Authentication Routes (routes.py)

Pre-built routes pentru operații de autentificare.

from roa2web.shared.auth import create_auth_router

# Basic auth router
auth_router = create_auth_router()
app.include_router(auth_router)

# Auth router cu admin routes
auth_router_admin = create_auth_router(include_admin_routes=True)
app.include_router(auth_router_admin)

Available routes:

  • POST /auth/login - User authentication
  • POST /auth/refresh - Token refresh
  • POST /auth/logout - User logout
  • GET /auth/me - Current user info
  • GET /auth/companies - User companies
  • GET /auth/status - Authentication status

🔧 Integration Guide

Full FastAPI Application

from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager

from roa2web.shared.auth import (
    AuthenticationMiddleware, create_auth_router,
    get_current_user, require_company_access,
    CurrentUser, PermissionType
)
from roa2web.shared.database import oracle_pool

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    await oracle_pool.initialize()
    yield
    # Shutdown
    await oracle_pool.close_pool()

app = FastAPI(
    title="ROA2WEB Application",
    lifespan=lifespan
)

# CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Authentication
app.add_middleware(
    AuthenticationMiddleware,
    excluded_paths=["/", "/docs", "/health"],
    rate_limit_paths=["/auth/login"]
)

# Routes
auth_router = create_auth_router()
app.include_router(auth_router)

# Protected endpoints
@app.get("/")
async def public_endpoint():
    return {"message": "Public endpoint"}

@app.get("/me")
async def my_info(current_user: CurrentUser = Depends(get_current_user)):
    return current_user

@app.get("/company/{company_code}/invoices")
async def get_invoices(
    company_code: str,
    current_user: CurrentUser = Depends(require_company_access(company_code))
):
    # Business logic here
    return {"company": company_code, "invoices": []}

Custom Permissions

from roa2web.shared.auth import require_permissions, PermissionType

# Define custom permissions
class CustomPermissionType(str, Enum):
    INVOICE_READ = "invoice_read"
    INVOICE_WRITE = "invoice_write"
    REPORT_EXPORT = "report_export"

# Use in endpoints
@app.get("/invoices")
async def get_invoices(
    user: CurrentUser = Depends(require_permissions([CustomPermissionType.INVOICE_READ]))
):
    return {"invoices": []}

Company-Specific Endpoints

from fastapi import Header
from roa2web.shared.auth import get_current_company_from_header

@app.get("/current-company-data")
async def get_current_company_data(
    company_code: str = Depends(get_current_company_from_header),
    current_user: CurrentUser = Depends(get_current_user)
):
    # company_code is automatically extracted from X-Company-Code header
    # and validated against user's accessible companies
    return {"company": company_code, "data": "..."}

🔒 Security Features

JWT Configuration

# Environment variables
JWT_SECRET_KEY=your-super-secret-jwt-key-change-in-production
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_DAYS=7

Rate Limiting

from roa2web.shared.auth import RateLimiter, AuthenticationMiddleware

# Custom rate limiter
custom_rate_limiter = RateLimiter(
    max_requests=10,    # 10 requests
    time_window=60      # per minute
)

app.add_middleware(
    AuthenticationMiddleware,
    rate_limit_paths=["/auth/login", "/auth/register"],
    rate_limiter=custom_rate_limiter
)

Security Headers

Middleware-ul adaugă automat header-e de securitate:

X-Content-Type-Options: nosniff
X-Frame-Options: DENY  
X-XSS-Protection: 1; mode=block

📚 API Reference

JWT Handler Methods

class JWTHandler:
    def create_access_token(username, companies, user_id=None, permissions=None) -> str
    def create_refresh_token(username, user_id=None) -> str
    def verify_token(token) -> Optional[TokenData]
    def refresh_access_token(refresh_token, companies, permissions=None) -> Optional[str]
    def create_token_response(username, companies, ...) -> TokenResponse

Auth Service Methods

class UserAuthService:
    async def verify_user_credentials(username, password) -> bool
    async def get_user_companies(username) -> List[str]
    async def get_user_permissions(username, company) -> List[str]
    async def authenticate_and_create_tokens(username, password) -> Tuple[bool, TokenResponse, str]
    async def validate_user_company_access(username, company) -> bool

FastAPI Dependencies

# User dependencies
get_current_user() -> CurrentUser
get_optional_user() -> Optional[CurrentUser]

# Permission dependencies
require_permissions(permissions: List[PermissionType])
require_company_access(company_code: str)
require_company_and_permissions(company_code: str, permissions: List[PermissionType])

# Utility dependencies
get_current_company_from_header() -> str

🧪 Testing

Running Tests

# Install test dependencies
pip install pytest pytest-asyncio httpx

# Run all tests
cd roa2web/shared/auth
python -m pytest test_auth.py -v

# Run specific test categories
python -m pytest test_auth.py::TestJWTHandler -v
python -m pytest test_auth.py::TestUserAuthService -v
python -m pytest test_auth.py::TestSecurityFeatures -v

# Run with coverage
python -m pytest test_auth.py --cov=. --cov-report=html

Test Categories

  • Unit Tests: JWT operations, auth service methods
  • Integration Tests: Database integration, full auth flow
  • Security Tests: Token tampering, SQL injection, rate limiting
  • Performance Tests: Token creation/verification speed

Demo Application

# Start demo app for manual testing
cd roa2web/shared/auth
python demo_app.py

# Available demo endpoints:
# http://localhost:8000/ - Home page cu documentație
# http://localhost:8000/docs - Swagger UI pentru testare
# http://localhost:8000/demo/* - Various demo endpoints

🚀 Deployment

Production Configuration

# Strong JWT secret key
JWT_SECRET_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")

# Shorter token expiration
ACCESS_TOKEN_EXPIRE_MINUTES=15
REFRESH_TOKEN_EXPIRE_DAYS=1

# Strict rate limiting
RATE_LIMIT_MAX_REQUESTS=3
RATE_LIMIT_TIME_WINDOW=300

# Secure headers
SECURE_SSL_REDIRECT=true
SESSION_COOKIE_SECURE=true

Docker Integration

# În Dockerfile-ul aplicației
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

# Environment pentru container
ENV JWT_SECRET_KEY=${JWT_SECRET_KEY}
ENV ORACLE_USER=${ORACLE_USER}
ENV ORACLE_PASSWORD=${ORACLE_PASSWORD}
ENV ORACLE_DSN=${ORACLE_DSN}

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Health Checks

@app.get("/health")
async def health_check():
    # Test database connection
    try:
        async with oracle_pool.get_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute("SELECT 1 FROM DUAL")
        db_status = "healthy"
    except Exception as e:
        db_status = f"error: {str(e)}"
    
    return {
        "status": "healthy" if db_status == "healthy" else "degraded",
        "database": db_status,
        "jwt": "functional",
        "timestamp": datetime.now().isoformat()
    }

🔧 Troubleshooting

Common Issues

1. "Invalid token" errors

# Check JWT secret key consistency
print(f"JWT Secret: {os.getenv('JWT_SECRET_KEY')}")

# Verify token creation and validation
token = jwt_handler.create_access_token("test", ["COMP1"])
token_data = jwt_handler.verify_token(token)
print(f"Token valid: {token_data is not None}")

2. Database connection errors

# Test Oracle connection
try:
    async with oracle_pool.get_connection() as conn:
        with conn.cursor() as cursor:
            cursor.execute("SELECT 1 FROM DUAL")
            result = cursor.fetchone()
    print("Database connection: OK")
except Exception as e:
    print(f"Database error: {e}")

3. Rate limiting issues

# Check rate limiter stats
from roa2web.shared.auth import default_rate_limiter

client_ip = "192.168.1.1"
allowed = default_rate_limiter.is_allowed(client_ip)
reset_time = default_rate_limiter.get_reset_time(client_ip)
print(f"IP {client_ip} allowed: {allowed}, resets at: {reset_time}")

4. Permission denied errors

# Check user companies and permissions
companies = await auth_service.get_user_companies("username")
permissions = await auth_service.get_user_permissions("username", "COMP1")
print(f"User companies: {companies}")
print(f"User permissions: {permissions}")

Debug Mode

import logging

# Enable debug logging
logging.basicConfig(level=logging.DEBUG)

# Specific loggers
logging.getLogger("roa2web.shared.auth").setLevel(logging.DEBUG)

Environment Validation

from roa2web.shared.utils.config import shared_config

# Validate configuration
print(f"Oracle User: {shared_config.oracle_user}")
print(f"JWT Secret set: {'***' if shared_config.jwt_secret_key else 'NOT SET'}")
print(f"Token expiry: {shared_config.access_token_expire_minutes} minutes")

📈 Performance Optimization

Caching

# Cache configuration
AUTH_CACHE_TTL_MINUTES=15  # User data cache TTL

# Monitor cache performance
stats = auth_service.get_cache_stats()
print(f"Cache hit ratio: {stats['cache_hit_ratio']:.2%}")

Connection Pooling

# Oracle pool configuration
DB_MIN_CONNECTIONS=2
DB_MAX_CONNECTIONS=10
DB_CONNECTION_INCREMENT=1

Token Optimization

# Optimize token size by limiting payload
token = jwt_handler.create_access_token(
    username="user",
    companies=["COMP1"],  # Limit companies in token
    permissions=["read"]   # Essential permissions only
)

🤝 Contributing

Pentru contribuții la sistemul de autentificare:

  1. Fork repository-ul și creează o ramură pentru feature
  2. Implementează schimbările cu tests comprehensive
  3. Rulează toate testele pentru a verifica compatibilitatea
  4. Actualizează documentația dacă este necesar
  5. Creează Pull Request cu descriere detaliată

Development Setup

# Clone repository
git clone [repository-url]
cd roa-flask

# Setup environment
python -m venv venv
source venv/bin/activate  # Linux/Mac
# or
venv\Scripts\activate  # Windows

pip install -r requirements.txt

# Run tests
cd roa2web/shared/auth
python -m pytest test_auth.py -v

📜 License

Acest sistem de autentificare face parte din proiectul ROA2WEB și este disponibil sub aceleași condiții de licențiere ca și proiectul principal.


ROA2WEB Authentication System v1.0.0
Secure, scalable, Oracle-integrated authentication pentru aplicații moderne