2025-07-19 17:49:03 +02:00
|
|
|
# shared/auth/decorators.py - NEW FILE
|
2025-07-17 13:09:24 +02:00
|
|
|
"""
|
2025-07-19 17:49:03 +02:00
|
|
|
Authentication decorators for microservices
|
2025-07-17 13:09:24 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from functools import wraps
|
2025-07-19 17:49:03 +02:00
|
|
|
from fastapi import HTTPException, status, Request
|
|
|
|
|
from typing import Callable, Optional
|
2025-07-17 13:09:24 +02:00
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
def require_authentication(func: Callable) -> Callable:
|
|
|
|
|
"""Decorator to require authentication - assumes gateway has validated token"""
|
|
|
|
|
|
|
|
|
|
@wraps(func)
|
|
|
|
|
async def wrapper(*args, **kwargs):
|
|
|
|
|
# Find request object in arguments
|
|
|
|
|
request = None
|
|
|
|
|
for arg in args:
|
|
|
|
|
if isinstance(arg, Request):
|
|
|
|
|
request = arg
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not request:
|
|
|
|
|
# Check kwargs
|
|
|
|
|
request = kwargs.get('request')
|
|
|
|
|
|
|
|
|
|
if not request:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Request object not found"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Check if user context exists (set by gateway)
|
|
|
|
|
if not hasattr(request.state, 'user') or not request.state.user:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
detail="Authentication required"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return await func(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
return wrapper
|
2025-07-17 13:09:24 +02:00
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
def require_tenant_access(func: Callable) -> Callable:
|
|
|
|
|
"""Decorator to require tenant access"""
|
2025-07-17 13:09:24 +02:00
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
@wraps(func)
|
|
|
|
|
async def wrapper(*args, **kwargs):
|
|
|
|
|
# Find request object
|
|
|
|
|
request = None
|
|
|
|
|
for arg in args:
|
|
|
|
|
if isinstance(arg, Request):
|
|
|
|
|
request = arg
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if not request or not hasattr(request.state, 'tenant_id'):
|
2025-07-17 13:09:24 +02:00
|
|
|
raise HTTPException(
|
2025-07-19 17:49:03 +02:00
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="Tenant access required"
|
2025-07-17 13:09:24 +02:00
|
|
|
)
|
2025-07-19 17:49:03 +02:00
|
|
|
|
|
|
|
|
return await func(*args, **kwargs)
|
2025-07-17 13:09:24 +02:00
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
|
def get_current_user(request: Request) -> dict:
|
|
|
|
|
"""Get current user from request state"""
|
|
|
|
|
if not hasattr(request.state, 'user'):
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
|
|
|
detail="User not authenticated"
|
|
|
|
|
)
|
|
|
|
|
return request.state.user
|
|
|
|
|
|
|
|
|
|
def get_current_tenant_id(request: Request) -> Optional[str]:
|
|
|
|
|
"""Get current tenant ID from request state"""
|
|
|
|
|
return getattr(request.state, 'tenant_id', None)
|