Permiso vistas implementados
This commit is contained in:
130
backend/app/api/v1/endpoints/permissions.py
Normal file
130
backend/app/api/v1/endpoints/permissions.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""Permissions endpoints - ServiceManagerWeb"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete
|
||||
from typing import Dict
|
||||
import uuid
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.api.deps import get_current_user
|
||||
from app.models.user import User, UserRole
|
||||
from app.models.permission import TenantPermission, DEFAULT_PERMISSIONS, CLIENT_PERMISSIONS
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/my")
|
||||
async def get_my_permissions(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> Dict[str, bool]:
|
||||
"""Obtener permisos efectivos del usuario actual."""
|
||||
if current_user.role.is_global:
|
||||
# Staff interno — devolver permisos del rol por defecto
|
||||
role_defaults = DEFAULT_PERMISSIONS.get(current_user.role.value, {})
|
||||
return role_defaults
|
||||
|
||||
# Buscar overrides individuales primero
|
||||
result = await db.execute(
|
||||
select(TenantPermission).where(
|
||||
TenantPermission.tenant_id == current_user.tenant_id,
|
||||
TenantPermission.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
user_overrides = {p.permission: p.granted for p in result.scalars().all()}
|
||||
|
||||
# Buscar defaults del rol
|
||||
result = await db.execute(
|
||||
select(TenantPermission).where(
|
||||
TenantPermission.tenant_id == current_user.tenant_id,
|
||||
TenantPermission.user_id == None,
|
||||
TenantPermission.role == current_user.role.value,
|
||||
)
|
||||
)
|
||||
role_defaults = {p.permission: p.granted for p in result.scalars().all()}
|
||||
|
||||
# Merge: override individual > default del rol > system default
|
||||
system_defaults = DEFAULT_PERMISSIONS.get(current_user.role.value, {})
|
||||
effective = {**system_defaults, **role_defaults, **user_overrides}
|
||||
return effective
|
||||
|
||||
|
||||
@router.get("/user/{user_id}")
|
||||
async def get_user_permissions(
|
||||
user_id: uuid.UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> Dict[str, bool]:
|
||||
"""Obtener permisos efectivos de un usuario específico."""
|
||||
if current_user.role not in (UserRole.ADMIN, UserRole.SUPPORT_MANAGER, UserRole.CLIENT_ADMIN):
|
||||
raise HTTPException(status_code=403, detail="Sin permisos")
|
||||
|
||||
target = await db.get(User, user_id)
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="Usuario no encontrado")
|
||||
|
||||
# Buscar overrides individuales
|
||||
result = await db.execute(
|
||||
select(TenantPermission).where(
|
||||
TenantPermission.user_id == user_id,
|
||||
)
|
||||
)
|
||||
user_overrides = {p.permission: p.granted for p in result.scalars().all()}
|
||||
|
||||
# Buscar defaults del rol en el tenant
|
||||
result = await db.execute(
|
||||
select(TenantPermission).where(
|
||||
TenantPermission.tenant_id == target.tenant_id,
|
||||
TenantPermission.user_id == None,
|
||||
TenantPermission.role == target.role.value,
|
||||
)
|
||||
)
|
||||
role_defaults = {p.permission: p.granted for p in result.scalars().all()}
|
||||
|
||||
system_defaults = DEFAULT_PERMISSIONS.get(target.role.value, {})
|
||||
effective = {**system_defaults, **role_defaults, **user_overrides}
|
||||
return effective
|
||||
|
||||
|
||||
@router.put("/user/{user_id}")
|
||||
async def update_user_permissions(
|
||||
user_id: uuid.UUID,
|
||||
data: Dict[str, bool],
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Actualizar permisos individuales de un usuario.
|
||||
Solo ADMIN, SUPPORT_MANAGER o CLIENT_ADMIN pueden hacerlo.
|
||||
"""
|
||||
if current_user.role not in (UserRole.ADMIN, UserRole.SUPPORT_MANAGER, UserRole.CLIENT_ADMIN):
|
||||
raise HTTPException(status_code=403, detail="Sin permisos")
|
||||
|
||||
target = await db.get(User, user_id)
|
||||
if not target:
|
||||
raise HTTPException(status_code=404, detail="Usuario no encontrado")
|
||||
|
||||
# Borrar overrides existentes del usuario
|
||||
await db.execute(
|
||||
delete(TenantPermission).where(
|
||||
TenantPermission.user_id == user_id,
|
||||
)
|
||||
)
|
||||
|
||||
# Insertar nuevos overrides
|
||||
from datetime import datetime
|
||||
now = datetime.utcnow()
|
||||
for permission, granted in data.items():
|
||||
db.add(TenantPermission(
|
||||
id=uuid.uuid4(),
|
||||
tenant_id=target.tenant_id,
|
||||
user_id=user_id,
|
||||
role=target.role.value,
|
||||
permission=permission,
|
||||
granted=granted,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
))
|
||||
|
||||
await db.commit()
|
||||
return {"message": "Permisos actualizados"}
|
||||
@@ -6,7 +6,7 @@ Router principal para la API v1
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.v1.endpoints import auth, health, tenants, users, systems, categories, tickets, client_profile, audit, sla, reports, participants
|
||||
from app.api.v1.endpoints import auth, health, tenants, users, systems, categories, tickets, client_profile, audit, sla, reports, participants, permissions
|
||||
|
||||
api_router = APIRouter()
|
||||
|
||||
@@ -86,4 +86,11 @@ api_router.include_router(
|
||||
reports.router,
|
||||
prefix="/reports",
|
||||
tags=["reports"]
|
||||
)
|
||||
|
||||
# Permissions routes
|
||||
api_router.include_router(
|
||||
permissions.router,
|
||||
prefix="/permissions",
|
||||
tags=["permissions"]
|
||||
)
|
||||
@@ -14,24 +14,99 @@ from app.core.database import Base, GUID
|
||||
|
||||
|
||||
CLIENT_PERMISSIONS = [
|
||||
# Permisos funcionales
|
||||
"view_tickets",
|
||||
"create_tickets",
|
||||
"close_tickets",
|
||||
"view_reports",
|
||||
"manage_tenant_users",
|
||||
"create_issues",
|
||||
# Permisos de navegación - cliente
|
||||
"nav:tickets",
|
||||
"nav:organization",
|
||||
"nav:usuarios",
|
||||
]
|
||||
|
||||
INTERNAL_PERMISSIONS = [
|
||||
# Permisos de navegación - interno
|
||||
"nav:tickets",
|
||||
"nav:users",
|
||||
"nav:tenants",
|
||||
"nav:categories",
|
||||
"nav:systems",
|
||||
"nav:sla",
|
||||
"nav:audit",
|
||||
"nav:reports",
|
||||
]
|
||||
|
||||
DEFAULT_PERMISSIONS = {
|
||||
"CLIENT_ADMIN": {p: True for p in CLIENT_PERMISSIONS},
|
||||
"CLIENT_ADMIN": {
|
||||
# Funcionales — todo ON
|
||||
"view_tickets": True,
|
||||
"create_tickets": True,
|
||||
"close_tickets": True,
|
||||
"view_reports": True,
|
||||
"manage_tenant_users": True,
|
||||
"create_issues": True,
|
||||
# Navegación cliente — todo ON
|
||||
"nav:tickets": True,
|
||||
"nav:organization": True,
|
||||
"nav:usuarios": True,
|
||||
},
|
||||
"CLIENT_USER": {
|
||||
# Funcionales
|
||||
"view_tickets": True,
|
||||
"create_tickets": True,
|
||||
"close_tickets": False,
|
||||
"view_reports": False,
|
||||
"manage_tenant_users": False,
|
||||
"create_issues": False,
|
||||
}
|
||||
# Navegación cliente
|
||||
"nav:tickets": True,
|
||||
"nav:organization": False,
|
||||
"nav:usuarios": False,
|
||||
},
|
||||
"ADMIN": {
|
||||
# Navegación interna — todo ON
|
||||
"nav:tickets": True,
|
||||
"nav:users": True,
|
||||
"nav:tenants": True,
|
||||
"nav:categories": True,
|
||||
"nav:systems": True,
|
||||
"nav:sla": True,
|
||||
"nav:audit": True,
|
||||
"nav:reports": True,
|
||||
},
|
||||
"SUPPORT_MANAGER": {
|
||||
"nav:tickets": True,
|
||||
"nav:users": True,
|
||||
"nav:tenants": False,
|
||||
"nav:categories": True,
|
||||
"nav:systems": True,
|
||||
"nav:sla": True,
|
||||
"nav:audit": True,
|
||||
"nav:reports": True,
|
||||
},
|
||||
"AGENT": {
|
||||
"nav:tickets": True,
|
||||
"nav:users": False,
|
||||
"nav:tenants": False,
|
||||
"nav:categories": False,
|
||||
"nav:systems": False,
|
||||
"nav:sla": False,
|
||||
"nav:audit": False,
|
||||
"nav:reports": False,
|
||||
},
|
||||
"AUDITOR": {
|
||||
"nav:tickets": True,
|
||||
"nav:users": False,
|
||||
"nav:tenants": False,
|
||||
"nav:categories": False,
|
||||
"nav:systems": False,
|
||||
"nav:sla": False,
|
||||
"nav:audit": True,
|
||||
"nav:reports": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user