fix: solución de bloqueos y estandarización de permisos
This commit is contained in:
@@ -18,6 +18,10 @@ from .dependencies import (
|
||||
get_current_user_permissions,
|
||||
)
|
||||
from .routes import router
|
||||
from .seed_v2 import register_core_permissions
|
||||
|
||||
# Registrar permisos al cargar el módulo
|
||||
register_core_permissions()
|
||||
|
||||
__all__ = [
|
||||
# Models
|
||||
|
||||
65
backend/api/v1/modules/core/permissions/cleanup_cli.py
Normal file
65
backend/api/v1/modules/core/permissions/cleanup_cli.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
Script CLI para LIMPIEZA TOTAL del sistema de permisos.
|
||||
Borra todos los roles, asignaciones y el catálogo de permisos.
|
||||
Úselo con precaución.
|
||||
|
||||
Uso:
|
||||
docker exec -it <container> python3 -m api.v1.modules.core.permissions.cleanup_cli
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from sqlalchemy import text
|
||||
|
||||
# Configurar logging
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Asegurar que el backend esté en el path
|
||||
sys.path.append(os.path.abspath("."))
|
||||
sys.path.append(os.path.abspath("backend"))
|
||||
|
||||
from core.database import CoreSessionLocal
|
||||
|
||||
def run_cleanup():
|
||||
"""Ejecuta el borrado de tablas en orden de dependencias."""
|
||||
logger.warning("INICIANDO LIMPIEZA TOTAL DE PERMISOS Y ROLES...")
|
||||
|
||||
db = CoreSessionLocal()
|
||||
try:
|
||||
# 1. Borrar asignaciones directas de permisos a usuarios
|
||||
logger.info("Borrando asignaciones directas de usuario...")
|
||||
db.execute(text("DELETE FROM core.user_company_permissions"))
|
||||
|
||||
# 2. Borrar relación entre roles y permisos
|
||||
logger.info("Borrando mapeo de roles y permisos...")
|
||||
db.execute(text("DELETE FROM core.role_permissions"))
|
||||
|
||||
# 3. Borrar asignación de roles a usuarios
|
||||
logger.info("Borrando asignación de roles a usuarios...")
|
||||
db.execute(text("DELETE FROM core.user_company_roles"))
|
||||
|
||||
# 4. Borrar los roles mismos
|
||||
logger.info("Borrando el catálogo de roles...")
|
||||
db.execute(text("DELETE FROM core.company_roles"))
|
||||
|
||||
# 5. Borrar el catálogo base de permisos
|
||||
logger.info("Borrando el catálogo base de permisos...")
|
||||
db.execute(text("DELETE FROM core.permissions"))
|
||||
|
||||
db.commit()
|
||||
logger.info("=" * 40)
|
||||
logger.info("LIMPIEZA COMPLETADA CON ÉXITO")
|
||||
logger.info("El sistema de permisos está ahora en blanco.")
|
||||
logger.info("=" * 40)
|
||||
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Error crítico durante la limpieza: {e}")
|
||||
sys.exit(1)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_cleanup()
|
||||
101
backend/api/v1/modules/core/permissions/registry.py
Normal file
101
backend/api/v1/modules/core/permissions/registry.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Registro centralizado para la modulación de permisos.
|
||||
Permite que cada módulo registre sus propios permisos de forma dinámica.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@dataclass
|
||||
class PermissionDefinition:
|
||||
"""Representa la definición de un permiso en un módulo."""
|
||||
code: str
|
||||
description: Optional[str] = None
|
||||
module: Optional[str] = None
|
||||
action: Optional[str] = None
|
||||
is_active: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
"""Lógica de autocompletado para evitar redundancia."""
|
||||
# Si el código tiene el formato "modulo.sub.accion" o "modulo.accion"
|
||||
parts = self.code.split(".")
|
||||
|
||||
# Extraer módulo si no se especificó
|
||||
if not self.module and len(parts) > 1:
|
||||
self.module = parts[0]
|
||||
elif not self.module:
|
||||
self.module = "system" # Default fallback
|
||||
|
||||
# Extraer acción si no se especificó (es la última parte del código)
|
||||
if not self.action and len(parts) > 1:
|
||||
self.action = parts[-1]
|
||||
elif not self.action:
|
||||
self.action = "view" # Default fallback
|
||||
|
||||
|
||||
class PermissionRegistry:
|
||||
"""
|
||||
Registro Singleton para permisos de la aplicación.
|
||||
Cada módulo de la API debe importar este registro y dar de alta sus permisos.
|
||||
"""
|
||||
_instance = None
|
||||
_permissions: Dict[str, PermissionDefinition] = {}
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super(PermissionRegistry, cls).__new__(cls)
|
||||
cls._permissions = {}
|
||||
return cls._instance
|
||||
|
||||
@classmethod
|
||||
def register(cls,
|
||||
code: str,
|
||||
description: Optional[str] = None,
|
||||
module: Optional[str] = None,
|
||||
action: Optional[str] = None) -> None:
|
||||
"""
|
||||
Registra un nuevo permiso en el sistema.
|
||||
"""
|
||||
if code in cls._permissions:
|
||||
logger.debug(f"Permiso {code} ya está registrado, actualizando metadatos.")
|
||||
|
||||
cls._permissions[code] = PermissionDefinition(
|
||||
code=code,
|
||||
description=description,
|
||||
module=module,
|
||||
action=action
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def register_many(cls, permissions_list: List[tuple]) -> None:
|
||||
"""
|
||||
Registra múltiples permisos desde una lista de tuplas.
|
||||
Útil para migrar seeds estáticos.
|
||||
"""
|
||||
for item in permissions_list:
|
||||
if len(item) == 2: # (code, description)
|
||||
cls.register(code=item[0], description=item[1])
|
||||
elif len(item) >= 3: # (code, description, module, ...)
|
||||
cls.register(
|
||||
code=item[0],
|
||||
description=item[1],
|
||||
module=item[2],
|
||||
action=item[3] if len(item) > 3 else None
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_all(cls) -> List[PermissionDefinition]:
|
||||
"""Retorna todos los permisos registrados."""
|
||||
return list(cls._permissions.values())
|
||||
|
||||
@classmethod
|
||||
def get_by_module(cls, module_name: str) -> List[PermissionDefinition]:
|
||||
"""Retorna los permisos de un módulo específico."""
|
||||
return [p for p in cls._permissions.values() if p.module == module_name]
|
||||
|
||||
|
||||
# Instancia global para facilitar el acceso
|
||||
registry = PermissionRegistry()
|
||||
@@ -55,17 +55,45 @@ async def get_my_permissions(
|
||||
permission_service: PermissionService = Depends(get_permission_service),
|
||||
):
|
||||
"""
|
||||
Obtiene los permisos y roles del usuario actual en el company actual.
|
||||
No requiere permisos especiales ya que es información propia.
|
||||
Obtiene los permisos y roles del usuario actual en la compañía actual.
|
||||
Si la compañía no tiene roles definidos, realiza un bootstrap automático.
|
||||
También realiza bootstrap si es un usuario con rol 'admin' en Keycloak pero sin roles locales.
|
||||
"""
|
||||
# 1. Validar acceso básico a la compañía
|
||||
# Nota: pass None en required_permissions permite el paso al bootstrap
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
|
||||
user_id = current_user.get("sub") or current_user.get("id")
|
||||
|
||||
# Obtener permisos
|
||||
# 2. Determinar si es un admin de Keycloak para forzar bootstrap si es necesario
|
||||
realm_roles = current_user.get("realm_access", {}).get("roles", [])
|
||||
client_roles = []
|
||||
for client in current_user.get("resource_access", {}).values():
|
||||
client_roles.extend(client.get("roles", []))
|
||||
is_keycloak_admin = "admin" in realm_roles or "admin" in client_roles
|
||||
|
||||
# 3. Bootstrap: si la compañía no tiene roles, o si el usuario es admin, o si estamos en desarrollo y el usuario no tiene roles
|
||||
from .models import CompanyRole, UserCompanyRole
|
||||
from core.config import settings
|
||||
|
||||
has_roles = db.query(CompanyRole).filter(CompanyRole.company_id == company_id).first() is not None
|
||||
user_has_roles = db.query(UserCompanyRole).filter(
|
||||
UserCompanyRole.user_id == user_id,
|
||||
UserCompanyRole.company_id == company_id
|
||||
).first() is not None
|
||||
|
||||
auto_bootstrap = (not has_roles) or \
|
||||
(is_keycloak_admin and not user_has_roles) or \
|
||||
(settings.ENVIRONMENT == "development" and not user_has_roles)
|
||||
|
||||
if auto_bootstrap:
|
||||
# Nota: bootstrap_super_admin ya hace commit e intenta no duplicar si el rol ya existe
|
||||
permission_service.bootstrap_super_admin(user_id, company_id)
|
||||
|
||||
# 4. Obtener permisos finales
|
||||
permissions = permission_service.get_user_permissions(user_id, company_id)
|
||||
|
||||
# Obtener roles
|
||||
# 5. Obtener roles
|
||||
roles = permission_service.get_user_roles(user_id, company_id)
|
||||
role_names = [role.name for role in roles]
|
||||
|
||||
@@ -81,9 +109,9 @@ async def get_my_permissions(
|
||||
async def list_available_permissions(
|
||||
db: Session = Depends(get_core_db),
|
||||
page: int = Query(1, ge=1, description="Número de página"),
|
||||
page_size: int = Query(100, ge=1, le=500, description="Tamaño de página"),
|
||||
page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
|
||||
module: Optional[str] = Query(None, description="Filtrar por módulo"),
|
||||
_: None = Depends(RequirePermission("permissions.view")),
|
||||
_: None = Depends(RequirePermission("roles.view")),
|
||||
):
|
||||
"""
|
||||
Lista todos los permisos disponibles en el sistema.
|
||||
@@ -108,13 +136,13 @@ async def list_company_roles(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
page: int = Query(1, ge=1, description="Número de página"),
|
||||
page_size: int = Query(100, ge=1, le=500, description="Tamaño de página"),
|
||||
page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
|
||||
):
|
||||
"""
|
||||
Lista todos los roles del companye actual.
|
||||
TODO: Agregar verificación de permisos
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
query = db.query(CompanyRole).filter(
|
||||
CompanyRole.company_id == company_id
|
||||
@@ -139,7 +167,7 @@ async def get_user_permissions(
|
||||
Obtiene los permisos y roles de un usuario específico.
|
||||
Requiere permiso: user.view
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
permissions = permission_service.get_user_permissions(user_id, company_id)
|
||||
roles = permission_service.get_user_roles(user_id, company_id)
|
||||
@@ -162,7 +190,7 @@ async def get_user_permissions(
|
||||
async def list_permissions(
|
||||
db: Session = Depends(get_core_db),
|
||||
page: int = Query(1, ge=1, description="Número de página"),
|
||||
page_size: int = Query(100, ge=1, le=500, description="Tamaño de página"),
|
||||
page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
|
||||
module: Optional[str] = Query(None, description="Filtrar por módulo"),
|
||||
action: Optional[str] = Query(None, description="Filtrar por acción"),
|
||||
search: Optional[str] = Query(None, description="Buscar por código o descripción"),
|
||||
@@ -241,7 +269,7 @@ async def list_user_roles(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
page: int = Query(1, ge=1, description="Número de página"),
|
||||
page_size: int = Query(100, ge=1, le=500, description="Tamaño de página"),
|
||||
page_size: int = Query(100, ge=1, le=1000, description="Tamaño de página"),
|
||||
):
|
||||
"""
|
||||
Lista todas las asignaciones de roles a usuarios en la compañía.
|
||||
@@ -249,7 +277,7 @@ async def list_user_roles(
|
||||
from .models import UserCompanyRole
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
query = db.query(UserCompanyRole).options(
|
||||
joinedload(UserCompanyRole.company_role)
|
||||
@@ -286,7 +314,7 @@ async def assign_user_role(
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
assigner_id = current_user.get("sub") or current_user.get("id")
|
||||
|
||||
# Verificar que el rol existe
|
||||
@@ -342,7 +370,7 @@ async def remove_user_role(
|
||||
"""
|
||||
from .models import UserCompanyRole
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
user_role = db.query(UserCompanyRole).filter(
|
||||
UserCompanyRole.id == user_role_id,
|
||||
@@ -380,6 +408,26 @@ async def get_permission(
|
||||
return permission
|
||||
|
||||
|
||||
|
||||
@router.post("/sync", response_model=Dict[str, Any])
|
||||
async def sync_permissions_endpoint(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
permission_service: PermissionService = Depends(get_permission_service),
|
||||
_: None = Depends(RequirePermission("roles.edit")),
|
||||
):
|
||||
"""
|
||||
Sincroniza dinámicamente los permisos registrados en los módulos con la base de datos.
|
||||
Requiere permiso: permissions.edit
|
||||
"""
|
||||
result = permission_service.sync_permissions()
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Permissions synchronized successfully",
|
||||
"data": result
|
||||
}
|
||||
|
||||
|
||||
from .schemas import CreatePermissionRequest
|
||||
|
||||
|
||||
@@ -488,7 +536,7 @@ async def create_role(
|
||||
Crea un nuevo rol personalizado para el companye.
|
||||
TODO: Agregar verificación de permisos
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.create"])
|
||||
|
||||
# Verificar que el código no esté en uso
|
||||
existing = (
|
||||
@@ -540,7 +588,7 @@ async def update_role(
|
||||
Actualiza un rol existente.
|
||||
TODO: Agregar verificación de permisos
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.edit"])
|
||||
|
||||
role = (
|
||||
db.query(CompanyRole)
|
||||
@@ -580,7 +628,7 @@ async def delete_role(
|
||||
"""
|
||||
from .models import RolePermission, UserCompanyRole
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.delete"])
|
||||
|
||||
role = (
|
||||
db.query(CompanyRole)
|
||||
@@ -629,7 +677,7 @@ async def get_role_permissions(
|
||||
"""
|
||||
from .models import RolePermission
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
# Verificar que el rol existe y pertenece al company
|
||||
role = (
|
||||
@@ -687,7 +735,7 @@ async def assign_permission_to_role(
|
||||
"""
|
||||
from .models import RolePermission
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
# Verificar que el rol existe
|
||||
role = (
|
||||
@@ -753,7 +801,7 @@ async def assign_multiple_permissions_to_role(
|
||||
"""
|
||||
from .models import RolePermission
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
# Verificar que el rol existe
|
||||
role = (
|
||||
@@ -822,7 +870,7 @@ async def remove_permission_from_role(
|
||||
"""
|
||||
from .models import RolePermission
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
# Buscar la asignación
|
||||
role_permission = (
|
||||
@@ -865,7 +913,7 @@ async def assign_role(
|
||||
Asigna un rol a un usuario en el companye actual.
|
||||
TODO: Agregar verificación de permisos
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
assigner_id = current_user.get("sub") or current_user.get("id")
|
||||
|
||||
@@ -902,7 +950,7 @@ async def grant_permission(
|
||||
Concede un permiso directo a un usuario en el companye actual.
|
||||
Requiere permiso: permissions.grant
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
assigner_id = current_user.get("sub") or current_user.get("id")
|
||||
|
||||
@@ -1035,7 +1083,7 @@ async def get_user_individual_permissions(
|
||||
from .models import UserCompanyPermission
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
user_permissions = (
|
||||
db.query(UserCompanyPermission)
|
||||
@@ -1072,7 +1120,7 @@ async def get_user_effective_permissions(
|
||||
from .models import UserCompanyPermission, UserCompanyRole, RolePermission
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
# 1. Obtener permisos de roles
|
||||
role_permissions_query = (
|
||||
@@ -1153,7 +1201,7 @@ async def assign_user_permission(
|
||||
"""
|
||||
from .models import UserCompanyPermission
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
# Verificar que el permiso existe
|
||||
permission = db.query(Permission).filter(Permission.id == request.permission_id).first()
|
||||
@@ -1216,7 +1264,7 @@ async def remove_user_permission(
|
||||
"""
|
||||
from .models import UserCompanyPermission
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["roles.view"])
|
||||
|
||||
user_permission = (
|
||||
db.query(UserCompanyPermission)
|
||||
|
||||
@@ -17,47 +17,58 @@ seed_invoices = [
|
||||
("invoice.imp.cm.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("invoice.imp.cm.process", "Procesar facturas", "invoice", "process"),
|
||||
|
||||
("invoice.imp.cr.view", "Ver facturas temporales de importación", "invoice", "view"),
|
||||
("invoice.imp.cr.create", "Crear facturas temporales de importación", "invoice", "create"),
|
||||
("invoice.imp.cr.view", "Ver facturas de importación con corrección", "invoice", "view"),
|
||||
("invoice.imp.cr.create", "Crear facturas de importación con corrección", "invoice", "create"),
|
||||
("invoice.imp.cr.edit", "Editar facturas", "invoice", "edit"),
|
||||
("invoice.imp.cr.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("invoice.imp.cr.process", "Procesar facturas", "invoice", "process"),
|
||||
|
||||
("invoice.exp.view", "Ver facturas temporales de importación", "invoice", "view"),
|
||||
("invoice.exp.create", "Crear facturas temporales de importación", "invoice", "create"),
|
||||
("invoice.exp.edit", "Editar facturas", "invoice", "edit"),
|
||||
("invoice.exp.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("invoice.exp.process", "Procesar facturas", "invoice", "process"),
|
||||
|
||||
("invoice.exp.rep.view", "Ver facturas temporales de importación", "invoice", "view"),
|
||||
("invoice.exp.rep.create", "Crear facturas temporales de importación", "invoice", "create"),
|
||||
("invoice.exp.rep.edit", "Editar facturas", "invoice", "edit"),
|
||||
("invoice.exp.rep.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("invoice.exp.rep.process", "Procesar facturas", "invoice", "process"),
|
||||
("invoice.imp.rep.view", "Ver facturas de importación reparación (Activo Fijo)", "invoice", "view"),
|
||||
("invoice.imp.rep.create", "Crear facturas de importación reparación (Activo Fijo)", "invoice", "create"),
|
||||
("invoice.imp.rep.edit", "Editar facturas de importación reparación", "invoice", "edit"),
|
||||
("invoice.imp.rep.delete", "Eliminar facturas de importación reparación", "invoice", "delete"),
|
||||
("invoice.imp.rep.process", "Procesar facturas de importación reparación", "invoice", "process"),
|
||||
|
||||
("invoice.exp.view", "Ver facturas de exportación", "invoice", "view"),
|
||||
("invoice.exp.create", "Crear facturas de exportación", "invoice", "create"),
|
||||
("invoice.exp.edit", "Editar facturas de exportación", "invoice", "edit"),
|
||||
("invoice.exp.delete", "Eliminar facturas de exportación", "invoice", "delete"),
|
||||
("invoice.exp.process", "Procesar facturas de exportación", "invoice", "process"),
|
||||
|
||||
("invoice.exp.rep.view", "Ver facturas de exportación reparación", "invoice", "view"),
|
||||
("invoice.exp.rep.create", "Crear facturas de exportación reparación", "invoice", "create"),
|
||||
("invoice.exp.rep.edit", "Editar facturas de exportación reparación", "invoice", "edit"),
|
||||
("invoice.exp.rep.delete", "Eliminar facturas de exportación reparación", "invoice", "delete"),
|
||||
("invoice.exp.rep.process", "Procesar facturas de exportación reparación", "invoice", "process"),
|
||||
|
||||
("invoice.exp.donac.view", "Ver facturas de exportación donación", "invoice", "view"),
|
||||
("invoice.exp.donac.create", "Crear facturas de exportación donación", "invoice", "create"),
|
||||
("invoice.exp.donac.edit", "Editar facturas de exportación donación", "invoice", "edit"),
|
||||
("invoice.exp.donac.delete", "Eliminar facturas de exportación donación", "invoice", "delete"),
|
||||
("invoice.exp.donac.process", "Procesar facturas de exportación donación", "invoice", "process"),
|
||||
|
||||
]
|
||||
|
||||
seed_user = [
|
||||
("user.view", "Ver facturas temporales de importación", "invoice", "view"),
|
||||
("user.create", "Crear facturas temporales de importación", "invoice", "create"),
|
||||
("user.edit", "Editar facturas", "invoice", "edit"),
|
||||
("user.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("user.process", "Procesar facturas", "invoice", "process"),
|
||||
("user.view", "Ver Usuarios", "invoice", "view"),
|
||||
("user.create", "Crear Usuarios", "invoice", "create"),
|
||||
("user.edit", "Editar Usuarios", "invoice", "edit"),
|
||||
("user.delete", "Eliminar Usuarios", "invoice", "delete"),
|
||||
("user.process", "Procesar Usuarios", "invoice", "process"),
|
||||
]
|
||||
|
||||
seed_report = [
|
||||
("report.view", "Ver facturas temporales de importación", "invoice", "view"),
|
||||
("report.create", "Crear facturas temporales de importación", "invoice", "create"),
|
||||
("report.edit", "Editar facturas", "invoice", "edit"),
|
||||
("report.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("report.process", "Procesar facturas", "invoice", "process"),
|
||||
("report.view", "Ver Reportes", "invoice", "view"),
|
||||
("report.create", "Crear Reportes", "invoice", "create"),
|
||||
("report.edit", "Editar Reportes", "invoice", "edit"),
|
||||
("report.delete", "Eliminar Reportes", "invoice", "delete"),
|
||||
("report.process", "Procesar Reportes", "invoice", "process"),
|
||||
]
|
||||
|
||||
seed_roles = [
|
||||
("roles.view", "Ver facturas temporales de importación", "invoice", "view"),
|
||||
("roles.create", "Crear facturas temporales de importación", "invoice", "create"),
|
||||
("roles.edit", "Editar facturas", "invoice", "edit"),
|
||||
("roles.delete", "Eliminar facturas", "invoice", "delete"),
|
||||
("roles.process", "Procesar facturas", "invoice", "process"),
|
||||
("roles.view", "Ver Roles", "invoice", "view"),
|
||||
("roles.create", "Crear Roles", "invoice", "create"),
|
||||
("roles.edit", "Editar Roles", "invoice", "edit"),
|
||||
("roles.delete", "Eliminar Roles", "invoice", "delete"),
|
||||
("roles.process", "Procesar Roles", "invoice", "process"),
|
||||
]
|
||||
232
backend/api/v1/modules/core/permissions/seed_v2.py
Normal file
232
backend/api/v1/modules/core/permissions/seed_v2.py
Normal file
@@ -0,0 +1,232 @@
|
||||
"""
|
||||
Semilla V2 para el sistema de permisos.
|
||||
Corrige las descripciones erróneas y organiza los permisos por módulos reales.
|
||||
Utiliza el PermissionRegistry para la modulación por API.
|
||||
"""
|
||||
|
||||
from .registry import registry
|
||||
from .seed import seed_invoices, seed_user, seed_report, seed_roles
|
||||
|
||||
# ============================================================================
|
||||
# HELPER: LIMPIEZA DE MÓDULOS DESDE SEED.PY
|
||||
# ============================================================================
|
||||
def clean_seed_permissions(perms):
|
||||
"""
|
||||
Toma los permisos de seed.py (que vienen todos como module='invoice')
|
||||
y los re-asigna al módulo correcto según su prefijo para la UI.
|
||||
"""
|
||||
cleaned = []
|
||||
for code, desc, _, action in perms:
|
||||
module = "invoice_imp" # Default
|
||||
if code.startswith("user."): module = "users"
|
||||
elif code.startswith("report."): module = "reports"
|
||||
elif code.startswith("roles."): module = "roles"
|
||||
elif code.startswith("invoice.exp"): module = "invoice_exp"
|
||||
|
||||
cleaned.append((code, desc, module, action))
|
||||
return cleaned
|
||||
|
||||
# ============================================================================
|
||||
# PERMISOS DE USUARIOS (CORREGIDOS)
|
||||
# ============================================================================
|
||||
|
||||
# ============================================================================
|
||||
# AUDITORÍA Y BITÁCORA
|
||||
# ============================================================================
|
||||
permissions_audit = [
|
||||
("audit_logs.view", "Ver bitácora de movimientos", "audit_logs", "view"),
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# DATOS DE REFERENCIA (Catálogos Fijos - Solo Ver)
|
||||
# ============================================================================
|
||||
permissions_reference = [
|
||||
("ref_pedimento_regimens.view", "Ver Regímenes de Pedimento", "reference_data", "view"),
|
||||
("ref_containers.view", "Ver Contenedores", "reference_data", "view"),
|
||||
("ref_countries.view", "Ver Países", "reference_data", "view"),
|
||||
("ref_currency_types.view", "Ver Tipos de Moneda", "reference_data", "view"),
|
||||
("ref_customs_sections.view", "Ver Secciones Aduaneras", "reference_data", "view"),
|
||||
("ref_customs_warehouses.view", "Ver Recintos Fiscalizados", "reference_data", "view"),
|
||||
("ref_incoterms.view", "Ver Incoterms", "reference_data", "view"),
|
||||
("ref_invoice_types.view", "Ver Tipos de Factura", "reference_data", "view"),
|
||||
("ref_material_types.view", "Ver Tipos de Material", "reference_data", "view"),
|
||||
("ref_payment_methods.view", "Ver Formas de Pago", "reference_data", "view"),
|
||||
("ref_pedimento_codes.view", "Ver Claves de Pedimento", "reference_data", "view"),
|
||||
("ref_sectors.view", "Ver Sectores", "reference_data", "view"),
|
||||
("ref_states.view", "Ver Estados", "reference_data", "view"),
|
||||
("ref_transport_modes.view", "Ver Modos de Transporte", "reference_data", "view"),
|
||||
("ref_transport_types.view", "Ver Tipos de Transporte", "reference_data", "view"),
|
||||
("ref_valuation_methods.view", "Ver Métodos de Valoración", "reference_data", "view"),
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# CATÁLOGOS GENERALES (CRUD)
|
||||
# ============================================================================
|
||||
def gen_crud(resource, desc, module):
|
||||
return [
|
||||
(f"{resource}.view", f"Ver {desc}", module, "view"),
|
||||
(f"{resource}.create", f"Crear {desc}", module, "create"),
|
||||
(f"{resource}.edit", f"Editar {desc}", module, "edit"),
|
||||
(f"{resource}.delete", f"Eliminar {desc}", module, "delete"),
|
||||
]
|
||||
|
||||
permissions_general = (
|
||||
gen_crud("cat_company", "Información de la Empresa", "general_catalogs") +
|
||||
gen_crud("cat_packages", "Bultos", "general_catalogs") +
|
||||
gen_crud("cat_concepts", "Conceptos", "general_catalogs") +
|
||||
gen_crud("cat_broker_concepts", "Conceptos de Agente Aduanal", "general_catalogs") +
|
||||
gen_crud("cat_classification", "Clasificación de Conceptos", "general_catalogs") +
|
||||
gen_crud("cat_identifiers", "Identificadores", "general_catalogs") +
|
||||
gen_crud("cat_incoterms", "Incoterms (Catálogo)", "general_catalogs") +
|
||||
gen_crud("cat_inpc", "INPC", "general_catalogs") +
|
||||
gen_crud("cat_legends", "Leyendas Fijas", "general_catalogs") +
|
||||
gen_crud("cat_seals", "Sellos", "general_catalogs") +
|
||||
gen_crud("cat_valuation", "Métodos de Valoración", "general_catalogs") +
|
||||
gen_crud("cat_countries", "Países", "general_catalogs") +
|
||||
gen_crud("cat_ports", "Puertos", "general_catalogs") +
|
||||
gen_crud("cat_um_general", "UdM Generales", "general_catalogs") +
|
||||
gen_crud("cat_um_customs", "UdM Aduana MEX", "general_catalogs") +
|
||||
gen_crud("cat_um_american", "UdM Aduana AME", "general_catalogs") +
|
||||
gen_crud("cat_um_ace", "UdM ACE", "general_catalogs") +
|
||||
gen_crud("cat_um_oma", "UdM OMA", "general_catalogs") +
|
||||
gen_crud("cat_unit_conversions", "Conversiones", "general_catalogs") +
|
||||
gen_crud("cat_equivalencies", "Equivalencias", "general_catalogs") +
|
||||
gen_crud("cat_exchange_rates", "Tipos de Cambio", "general_catalogs") +
|
||||
gen_crud("cat_currency", "Tipos de Moneda", "general_catalogs") +
|
||||
gen_crud("cat_multi_currency_types", "Tipos de Moneda (Múltiples)", "general_catalogs") +
|
||||
gen_crud("cat_inv_types", "Tipos de Factura", "general_catalogs") +
|
||||
gen_crud("cat_signatures", "Firmas Electrónicas", "general_catalogs") +
|
||||
gen_crud("cat_errors", "Catálogo de Errores", "general_catalogs") +
|
||||
gen_crud("cat_doda", "DODA", "general_catalogs") +
|
||||
gen_crud("cat_prevalidators", "Prevalidadores", "general_catalogs") +
|
||||
gen_crud("cat_notices", "Avisos Electrónicos", "general_catalogs") +
|
||||
gen_crud("cat_crossing", "Avisos de Cruce", "general_catalogs") +
|
||||
gen_crud("cat_warehouses", "Recintos", "general_catalogs") +
|
||||
gen_crud("cat_sectors", "Sectores", "general_catalogs")
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# FRACCIONES
|
||||
# ============================================================================
|
||||
permissions_fractions = [
|
||||
# Solo ver para SITAR
|
||||
("frac_sitar.view", "Ver Fracciones SITAR", "fractions", "view"),
|
||||
("frac_sitar_7.view", "Ver Fracciones SITAR (Séptima Enmienda)", "fractions", "view"),
|
||||
("frac_sitar_us.view", "Ver Fracciones SITAR (USA)", "fractions", "view"),
|
||||
] + gen_crud("frac_american", "Fracciones Americanas", "fractions") + \
|
||||
gen_crud("frac_canadian", "Fracciones Canadienses", "fractions") + \
|
||||
gen_crud("frac_historical", "Fracciones Históricas", "fractions") + \
|
||||
[("frac_sectors.view", "Ver Sectores", "fractions", "view")]
|
||||
|
||||
# ============================================================================
|
||||
# TRANSPORTES (CRUD)
|
||||
# ============================================================================
|
||||
permissions_transports = (
|
||||
gen_crud("transporters", "Transportistas", "transports") +
|
||||
gen_crud("drivers", "Conductores", "transports") +
|
||||
gen_crud("trailers", "Trailers", "transports") +
|
||||
gen_crud("vehicles", "Vehículos", "transports")
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# MERCANCÍAS (CRUD)
|
||||
# ============================================================================
|
||||
permissions_goods = (
|
||||
gen_crud("goods_classes", "Clases de Activo Fijo", "goods") +
|
||||
gen_crud("goods_parts", "Partes / Productos", "goods") +
|
||||
gen_crud("goods_fda", "Códigos FDA", "goods")
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# PEDIMENTOS
|
||||
# ============================================================================
|
||||
permissions_pedimentos = (
|
||||
gen_crud("pedimentos_mgmt", "Gestión de Pedimentos", "pedimentos") +
|
||||
[
|
||||
("pedimentos_codes.view", "Ver Claves de Pedimento", "pedimentos", "view"),
|
||||
("pedimentos_regimes.view", "Ver Regímenes Aduanales", "pedimentos", "view"),
|
||||
("pedimentos_payments.view", "Ver Formas de Pago", "pedimentos", "view"),
|
||||
("pedimentos_sections.view", "Ver Secciones Aduaneras", "pedimentos", "view"),
|
||||
("pedimentos_anexo22.view", "Ver Anexo 22 / Apéndice 31", "pedimentos", "view"),
|
||||
]
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# EXPORTACIÓN
|
||||
# ============================================================================
|
||||
permissions_export = (
|
||||
[("export_catalog.view", "Ver Catálogo Base Exportación", "export", "view")] +
|
||||
[("export_repair.view", "Ver Reparaciones", "export", "view")] +
|
||||
gen_crud("export_manifest", "Manifiestos de Exportación", "export") +
|
||||
[("export_proforma.view", "Ver Proformas", "export", "view")] +
|
||||
[("export_reports.view", "Ver Reportes Oficiales Exportación", "export", "view")] +
|
||||
[("export_used.view", "Ver Materiales Utilizados", "export", "view")] +
|
||||
[("export_destruction.view", "Ver Destrucciones", "export", "view")] +
|
||||
[("export_special.view", "Ver Procesos Especiales", "export", "view")]
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# FACTURACIÓN, USUARIOS, REPORTES Y ROLES (DESDE SEED.PY)
|
||||
# ============================================================================
|
||||
# Estas secciones se nutren directamente de seed.py
|
||||
permissions_invoice_imp_seed = clean_seed_permissions(seed_invoices)
|
||||
permissions_users_seed = clean_seed_permissions(seed_user)
|
||||
permissions_reports_seed = clean_seed_permissions(seed_report)
|
||||
permissions_roles_seed = clean_seed_permissions(seed_roles)
|
||||
|
||||
# ============================================================================
|
||||
# CLIENTES Y PROVEEDORES (CRUD)
|
||||
# ============================================================================
|
||||
permissions_clients = gen_crud("partners_mgmt", "Clientes y Proveedores", "partners")
|
||||
|
||||
# ============================================================================
|
||||
# AGENTES ADUANALES (CRUD)
|
||||
# ============================================================================
|
||||
permissions_brokers = gen_crud("customs_brokers", "Agentes Aduanales", "brokers")
|
||||
|
||||
# ======================================# USUARIOS DEL SISTEMA (YA NO SE USA AQUÍ, VIENE DE SEED.PY)
|
||||
# ============================================================================
|
||||
# permissions_users = ...
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURACIÓN GENERAL Y AYUDA
|
||||
# ============================================================================
|
||||
permissions_settings = [
|
||||
("settings_general.view", "Ver Configuración General", "settings", "view"),
|
||||
("settings_general.edit", "Editar Configuración General", "settings", "edit"),
|
||||
]
|
||||
|
||||
permissions_help = gen_crud("help_center", "Centro de Ayuda y Documentos", "help")
|
||||
|
||||
# ============================================================================
|
||||
# CSV UPLOAD MASIVO
|
||||
# ============================================================================
|
||||
permissions_csv = [
|
||||
("csv_upload.process", "Procesar Cargas Masivas CSV", "csv_upload", "process"),
|
||||
]
|
||||
|
||||
def register_core_permissions():
|
||||
"""Registra los permisos granulados de la aplicación según Sidebar."""
|
||||
registry.register_many(permissions_audit)
|
||||
registry.register_many(permissions_reference)
|
||||
registry.register_many(permissions_general)
|
||||
registry.register_many(permissions_fractions)
|
||||
registry.register_many(permissions_transports)
|
||||
registry.register_many(permissions_goods)
|
||||
registry.register_many(permissions_pedimentos)
|
||||
|
||||
# Registro de permisos desde seed.py (limpios)
|
||||
registry.register_many(permissions_invoice_imp_seed)
|
||||
registry.register_many(permissions_users_seed)
|
||||
registry.register_many(permissions_reports_seed)
|
||||
registry.register_many(permissions_roles_seed)
|
||||
|
||||
registry.register_many(permissions_export)
|
||||
registry.register_many(permissions_clients)
|
||||
registry.register_many(permissions_brokers)
|
||||
registry.register_many(permissions_settings)
|
||||
registry.register_many(permissions_help)
|
||||
registry.register_many(permissions_csv)
|
||||
|
||||
# Ejecutar registro al importar este módulo
|
||||
register_core_permissions()
|
||||
@@ -296,3 +296,153 @@ class PermissionService:
|
||||
self.db.refresh(user_permission)
|
||||
|
||||
return user_permission
|
||||
|
||||
def sync_permissions(self) -> dict:
|
||||
"""
|
||||
Sincroniza los permisos registrados en el registry con la base de datos.
|
||||
Inserta nuevos permisos y actualiza los existentes.
|
||||
"""
|
||||
from .registry import registry
|
||||
# IMPORTANTE: Importar seed_v2 para que se ejecute register_core_permissions()
|
||||
from . import seed_v2
|
||||
|
||||
registered_permissions = registry.get_all()
|
||||
synced_count = 0
|
||||
updated_count = 0
|
||||
|
||||
for p_def in registered_permissions:
|
||||
# Buscar permiso existente
|
||||
db_permission = self.db.query(Permission).filter(Permission.code == p_def.code).first()
|
||||
|
||||
if db_permission:
|
||||
# Actualizar si hay cambios
|
||||
changed = False
|
||||
if db_permission.description != p_def.description:
|
||||
db_permission.description = p_def.description
|
||||
changed = True
|
||||
if db_permission.module != p_def.module:
|
||||
db_permission.module = p_def.module
|
||||
changed = True
|
||||
if db_permission.action != p_def.action:
|
||||
db_permission.action = p_def.action
|
||||
changed = True
|
||||
if db_permission.is_active != p_def.is_active:
|
||||
db_permission.is_active = p_def.is_active
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
updated_count += 1
|
||||
else:
|
||||
# Crear nuevo
|
||||
new_permission = Permission(
|
||||
code=p_def.code,
|
||||
description=p_def.description,
|
||||
module=p_def.module,
|
||||
action=p_def.action,
|
||||
is_active=p_def.is_active
|
||||
)
|
||||
self.db.add(new_permission)
|
||||
synced_count += 1
|
||||
|
||||
self.db.commit()
|
||||
|
||||
return {
|
||||
"synced": synced_count,
|
||||
"updated": updated_count,
|
||||
"total_registered": len(registered_permissions)
|
||||
}
|
||||
|
||||
def bootstrap_super_admin(self, user_id: str, company_id: int) -> bool:
|
||||
"""
|
||||
Crea un rol de Super Administrador con todos los permisos y se lo asigna al usuario.
|
||||
Diseñado para el primer inicio de una compañía o para asegurar acceso a administradores.
|
||||
"""
|
||||
from .models import CompanyRole, RolePermission, UserCompanyRole, Permission
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# 0. Sincronizar permisos por si la tabla esta vacía
|
||||
# Esto puebla la tabla 'permissions' desde el registry de código
|
||||
logger.info("Bootstrap: Sincronizando catálogo de permisos desde el registry...")
|
||||
from . import seed_v2
|
||||
sync_res = self.sync_permissions()
|
||||
logger.info(f"Bootstrap: Sincronización completa. {sync_res.get('synced', 0)} nuevos, {sync_res.get('total_registered', 0)} totales.")
|
||||
|
||||
# 1. Obtener el tenant_id
|
||||
tenant_id = 1
|
||||
try:
|
||||
from api.v1.modules.a76.general_catalogs.company.models import Company
|
||||
company = self.db.query(Company).filter(Company.id == company_id).first()
|
||||
if company:
|
||||
tenant_id = company.tenant_id
|
||||
except:
|
||||
pass
|
||||
|
||||
# 2. Buscar si ya existe el rol "super_admin"
|
||||
admin_role = self.db.query(CompanyRole).filter(
|
||||
CompanyRole.company_id == company_id,
|
||||
CompanyRole.code == "super_admin"
|
||||
).first()
|
||||
|
||||
if not admin_role:
|
||||
logger.info(f"Bootstrap: Creando Super Administrador para usuario {user_id} (Company: {company_id})")
|
||||
# Crear el rol Super Administrador si no existe
|
||||
admin_role = CompanyRole(
|
||||
company_id=company_id,
|
||||
tenant_id=tenant_id,
|
||||
name="Super Administrador",
|
||||
code="super_admin",
|
||||
description="Rol con acceso total al sistema (generado automáticamente)",
|
||||
is_active=True
|
||||
)
|
||||
self.db.add(admin_role)
|
||||
self.db.flush()
|
||||
|
||||
# 4. Asignar TODOS los permisos activos al rol
|
||||
all_perms = self.db.query(Permission).filter(Permission.is_active == True).all()
|
||||
if not all_perms:
|
||||
logger.warning("Bootstrap: ¡ALERTA! No se encontraron permisos en la DB ni tras la sincronización.")
|
||||
|
||||
for perm in all_perms:
|
||||
role_perm = RolePermission(
|
||||
company_role_id=admin_role.id,
|
||||
permission_id=perm.id,
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id
|
||||
)
|
||||
self.db.add(role_perm)
|
||||
else:
|
||||
logger.info(f"Bootstrap: El rol super_admin ya existe para la compañía {company_id}.")
|
||||
|
||||
# 5. Asegurar que el usuario tenga el rol asignado
|
||||
user_has_role = self.db.query(UserCompanyRole).filter(
|
||||
UserCompanyRole.user_id == user_id,
|
||||
UserCompanyRole.company_id == company_id,
|
||||
UserCompanyRole.company_role_id == admin_role.id
|
||||
).first()
|
||||
|
||||
if not user_has_role:
|
||||
logger.info(f"Bootstrap: Asignando rol Super Administrador al usuario {user_id}")
|
||||
user_role = UserCompanyRole(
|
||||
user_id=user_id,
|
||||
company_id=company_id,
|
||||
tenant_id=tenant_id,
|
||||
company_role_id=admin_role.id,
|
||||
is_active=True,
|
||||
assigned_by="SYSTEM_BOOTSTRAP"
|
||||
)
|
||||
self.db.add(user_role)
|
||||
self.db.commit()
|
||||
return True
|
||||
else:
|
||||
logger.info(f"Bootstrap: El usuario {user_id} ya tiene el rol asignado.")
|
||||
self.db.commit()
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.db.rollback()
|
||||
logger.error(f"Bootstrap: ERROR CRÍTICO - {str(e)}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
50
backend/api/v1/modules/core/permissions/sync_cli.py
Normal file
50
backend/api/v1/modules/core/permissions/sync_cli.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""
|
||||
Script CLI para sincronizar los permisos de la aplicación.
|
||||
Útil para el bootstrap inicial cuando el endpoint /sync aún no es accesible
|
||||
o cuando se desea forzar una actualización desde la consola/Docker.
|
||||
|
||||
Uso:
|
||||
docker exec -it <container> python3 -m api.v1.modules.core.permissions.sync_cli
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
# Configurar logging básico para ver resultados en consola
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Asegurar que el backend esté en el path
|
||||
sys.path.append(os.path.abspath("."))
|
||||
sys.path.append(os.path.abspath("backend"))
|
||||
|
||||
from core.database import CoreSessionLocal
|
||||
from api.v1.modules.core.permissions.service import PermissionService
|
||||
|
||||
def run_sync():
|
||||
"""Ejecuta la lógica de sincronización modular."""
|
||||
logger.info("Iniciando Sincronización Modular de Permisos...")
|
||||
|
||||
db = CoreSessionLocal()
|
||||
try:
|
||||
service = PermissionService(db)
|
||||
result = service.sync_permissions()
|
||||
|
||||
logger.info("-" * 40)
|
||||
logger.info(f"Sincronización Exitosa!")
|
||||
logger.info(f" - Nuevos insertados: {result['synced']}")
|
||||
logger.info(f" - Existentes actualizados: {result['updated']}")
|
||||
logger.info(f" - Total en Registry: {result['total_registered']}")
|
||||
logger.info("-" * 40)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error crítico durante la sincronización: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_sync()
|
||||
Reference in New Issue
Block a user