""" Servicio para gestionar relaciones entre usuarios y tenants """ from sqlalchemy.orm import Session from sqlalchemy import and_ from typing import List, Optional from fastapi import HTTPException import logging from .models import UserTenant from ..tenants.models import Tenant logger = logging.getLogger(__name__) class UserTenantService: """Servicio para gestionar acceso de usuarios a tenants""" def __init__(self, db: Session): self.db = db def add_user_to_tenant( self, keycloak_user_id: str, tenant_id: int, role: Optional[str] = None ) -> UserTenant: """ Agrega un usuario a un tenant Args: keycloak_user_id: ID del usuario en Keycloak tenant_id: ID del tenant role: Rol opcional del usuario en este tenant Returns: UserTenant creado """ # Verificar que el tenant existe tenant = self.db.query(Tenant).filter(Tenant.id == tenant_id).first() if not tenant: raise HTTPException(status_code=404, detail="Tenant not found") # Verificar si la relación ya existe existing = self.db.query(UserTenant).filter( and_( UserTenant.keycloak_user_id == keycloak_user_id, UserTenant.tenant_id == tenant_id ) ).first() if existing: # Si existe pero está inactiva, reactivarla if not existing.is_active: existing.is_active = True existing.role = role self.db.commit() self.db.refresh(existing) logger.info(f"Reactivated user {keycloak_user_id} in tenant {tenant_id}") return existing else: raise HTTPException( status_code=409, detail="User already has access to this tenant" ) # Crear nueva relación user_tenant = UserTenant( keycloak_user_id=keycloak_user_id, tenant_id=tenant_id, role=role, is_active=True ) self.db.add(user_tenant) self.db.commit() self.db.refresh(user_tenant) logger.info(f"Added user {keycloak_user_id} to tenant {tenant_id}") return user_tenant def remove_user_from_tenant( self, keycloak_user_id: str, tenant_id: int, soft_delete: bool = True ) -> bool: """ Elimina un usuario de un tenant Args: keycloak_user_id: ID del usuario en Keycloak tenant_id: ID del tenant soft_delete: Si True, solo marca como inactivo. Si False, elimina físicamente Returns: True si se eliminó correctamente """ user_tenant = self.db.query(UserTenant).filter( and_( UserTenant.keycloak_user_id == keycloak_user_id, UserTenant.tenant_id == tenant_id ) ).first() if not user_tenant: raise HTTPException( status_code=404, detail="User-tenant relationship not found" ) if soft_delete: user_tenant.is_active = False self.db.commit() logger.info(f"Deactivated user {keycloak_user_id} from tenant {tenant_id}") else: self.db.delete(user_tenant) self.db.commit() logger.info(f"Deleted user {keycloak_user_id} from tenant {tenant_id}") return True def get_user_tenants(self, keycloak_user_id: str) -> List[Tenant]: """ Obtiene todos los tenants a los que tiene acceso un usuario Args: keycloak_user_id: ID del usuario en Keycloak Returns: Lista de tenants """ user_tenants = self.db.query(UserTenant).filter( and_( UserTenant.keycloak_user_id == keycloak_user_id, UserTenant.is_active == True ) ).all() tenant_ids = [ut.tenant_id for ut in user_tenants] tenants = self.db.query(Tenant).filter( and_( Tenant.id.in_(tenant_ids), Tenant.is_active == True ) ).all() return tenants def get_tenant_users(self, tenant_id: int) -> List[UserTenant]: """ Obtiene todos los usuarios que tienen acceso a un tenant Args: tenant_id: ID del tenant Returns: Lista de relaciones UserTenant """ return self.db.query(UserTenant).filter( and_( UserTenant.tenant_id == tenant_id, UserTenant.is_active == True ) ).all() def user_has_access_to_tenant( self, keycloak_user_id: str, tenant_id: int ) -> bool: """ Verifica si un usuario tiene acceso a un tenant Args: keycloak_user_id: ID del usuario en Keycloak tenant_id: ID del tenant Returns: True si tiene acceso, False en caso contrario """ user_tenant = self.db.query(UserTenant).filter( and_( UserTenant.keycloak_user_id == keycloak_user_id, UserTenant.tenant_id == tenant_id, UserTenant.is_active == True ) ).first() return user_tenant is not None def update_user_role_in_tenant( self, keycloak_user_id: str, tenant_id: int, role: str ) -> UserTenant: """ Actualiza el rol de un usuario en un tenant Args: keycloak_user_id: ID del usuario en Keycloak tenant_id: ID del tenant role: Nuevo rol Returns: UserTenant actualizado """ user_tenant = self.db.query(UserTenant).filter( and_( UserTenant.keycloak_user_id == keycloak_user_id, UserTenant.tenant_id == tenant_id ) ).first() if not user_tenant: raise HTTPException( status_code=404, detail="User-tenant relationship not found" ) user_tenant.role = role self.db.commit() self.db.refresh(user_tenant) logger.info(f"Updated role for user {keycloak_user_id} in tenant {tenant_id} to {role}") return user_tenant