""" Servicio para gestionar relaciones entre usuarios y tenants """ import logging from typing import List, Optional from fastapi import HTTPException from sqlalchemy import and_ from sqlalchemy.orm import Session from ..tenants.models import Tenant from .models import UserTenant 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) 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) 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() else: self.db.delete(user_tenant) self.db.commit() 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, ) ) .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)) .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)) .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, ) ) .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) return user_tenant