Add improvements

This commit is contained in:
Urtzi Alfaro
2026-01-12 14:24:14 +01:00
parent 6037faaf8c
commit 230bbe6a19
61 changed files with 1668 additions and 894 deletions

View File

@@ -367,26 +367,47 @@ async def get_profile(
db: AsyncSession = Depends(get_db)
):
"""Get user profile - works for JWT auth AND demo sessions"""
logger.info(f"📋 Profile request received",
user_id=current_user.get("user_id"),
is_demo=current_user.get("is_demo", False),
demo_session_id=current_user.get("demo_session_id", ""),
email=current_user.get("email", ""),
path="/api/v1/auth/me")
try:
user_id = current_user.get("user_id")
if not user_id:
logger.error(f"❌ No user_id in current_user context for profile request",
current_user=current_user)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid user context"
)
logger.info(f"🔎 Fetching user profile for user_id: {user_id}",
is_demo=current_user.get("is_demo", False),
demo_session_id=current_user.get("demo_session_id", ""))
# Fetch user from database
from app.repositories import UserRepository
user_repo = UserRepository(User, db)
user = await user_repo.get_by_id(user_id)
if not user:
logger.error(f"🚨 User not found in database",
user_id=user_id,
is_demo=current_user.get("is_demo", False))
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User profile not found"
)
logger.info(f"🎉 User profile found",
user_id=user.id,
email=user.email,
full_name=user.full_name,
is_active=user.is_active)
return UserResponse(
id=str(user.id),
email=user.email,

View File

@@ -30,14 +30,6 @@ router = APIRouter(prefix="/internal/demo", tags=["internal"])
DEMO_TENANT_PROFESSIONAL = "a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6"
def verify_internal_api_key(x_internal_api_key: Optional[str] = Header(None)):
"""Verify internal API key for service-to-service communication"""
if x_internal_api_key != settings.INTERNAL_API_KEY:
logger.warning("Unauthorized internal API access attempted")
raise HTTPException(status_code=403, detail="Invalid internal API key")
return True
@router.post("/clone")
async def clone_demo_data(
base_tenant_id: str,
@@ -45,8 +37,7 @@ async def clone_demo_data(
demo_account_type: str,
session_id: Optional[str] = None,
session_created_at: Optional[str] = None,
db: AsyncSession = Depends(get_db),
_: bool = Depends(verify_internal_api_key)
db: AsyncSession = Depends(get_db)
):
"""
Clone auth service data for a virtual demo tenant
@@ -226,7 +217,7 @@ async def clone_demo_data(
@router.get("/clone/health")
async def clone_health_check(_: bool = Depends(verify_internal_api_key)):
async def clone_health_check():
"""
Health check for internal cloning endpoint
Used by orchestrator to verify service availability

View File

@@ -239,24 +239,26 @@ class SecurityManager:
return hashlib.sha256(data.encode()).hexdigest()
@staticmethod
def create_service_token(service_name: str) -> str:
def create_service_token(service_name: str, tenant_id: Optional[str] = None) -> str:
"""
Create JWT service token for inter-service communication
✅ FIXED: Proper service token creation with JWT
UNIFIED: Uses shared JWT handler for consistent token creation
✅ ENHANCED: Supports tenant context for tenant-scoped operations
Args:
service_name: Name of the service (e.g., 'auth-service', 'tenant-service')
tenant_id: Optional tenant ID for tenant-scoped service operations
Returns:
Encoded JWT service token
"""
try:
# Create service token payload
payload = {
"sub": service_name,
"service": service_name,
"type": "service",
"role": "admin",
"is_service": True
}
# Use JWT handler to create service token
token = jwt_handler.create_service_token(service_name)
logger.debug(f"Created service token for {service_name}")
# Use unified JWT handler to create service token
token = jwt_handler.create_service_token(
service_name=service_name,
tenant_id=tenant_id
)
logger.debug(f"Created service token for {service_name}", tenant_id=tenant_id)
return token
except Exception as e:

View File

@@ -517,6 +517,22 @@ class EnhancedAuthService:
detail="Invalid token"
)
# Handle service tokens (used for inter-service communication)
if payload.get("type") == "service":
logger.debug("Service token verified successfully",
service=payload.get("service"),
tenant_id=payload.get("tenant_id"))
return {
"valid": True,
"user_id": payload.get("user_id", f"{payload.get('service')}-service"),
"email": payload.get("email", f"{payload.get('service')}-service@internal"),
"role": payload.get("role", "admin"),
"exp": payload.get("exp"),
"service": payload.get("service"),
"tenant_id": payload.get("tenant_id")
}
# Handle regular user tokens
return payload
except Exception as e:
@@ -689,16 +705,22 @@ class EnhancedAuthService:
error=str(e))
return False
async def _get_service_token(self) -> str:
async def _get_service_token(self, tenant_id: Optional[str] = None) -> str:
"""
Get service token for inter-service communication.
This is used to fetch subscription data from tenant service.
Args:
tenant_id: Optional tenant ID for tenant-scoped service operations
Returns:
JWT service token
"""
try:
# Create a proper service token with JWT using SecurityManager
service_token = SecurityManager.create_service_token("auth-service")
service_token = SecurityManager.create_service_token("auth-service", tenant_id)
logger.debug("Generated service token for tenant service communication")
logger.debug("Generated service token for tenant service communication", tenant_id=tenant_id)
return service_token
except Exception as e:
logger.error(f"Failed to get service token: {e}")

View File

@@ -14,6 +14,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.models.deletion_job import DeletionJob as DeletionJobModel
from app.repositories.deletion_job_repository import DeletionJobRepository
from shared.auth.jwt_handler import JWTHandler
logger = structlog.get_logger()
@@ -145,13 +146,17 @@ class DeletionOrchestrator:
Initialize orchestrator
Args:
auth_token: JWT token for service-to-service authentication
auth_token: JWT token for service-to-service authentication (deprecated - will be auto-generated)
db: Database session for persistence (optional for backward compatibility)
"""
self.auth_token = auth_token
self.auth_token = auth_token # Deprecated: kept for backward compatibility
self.db = db
self.jobs: Dict[str, DeletionJob] = {} # In-memory cache for active jobs
# Initialize JWT handler for creating service tokens
from app.core.config import settings
self.jwt_handler = JWTHandler(settings.JWT_SECRET_KEY, settings.JWT_ALGORITHM)
async def _save_job_to_db(self, job: DeletionJob) -> None:
"""Save or update job to database"""
if not self.db:
@@ -406,14 +411,18 @@ class DeletionOrchestrator:
tenant_id=tenant_id)
try:
# Always create a service token with tenant context for secure service-to-service communication
service_token = self.jwt_handler.create_service_token(
service_name="auth",
tenant_id=tenant_id
)
headers = {
"X-Internal-Service": "auth-service",
"Authorization": f"Bearer {service_token}",
"X-Service": "auth-service",
"Content-Type": "application/json"
}
if self.auth_token:
headers["Authorization"] = f"Bearer {self.auth_token}"
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.delete(endpoint, headers=headers)