Saltar a contenido

Permisos personalizados

BasePermission class

from fastapi import Request
from fastapi_basekit.aio.permissions.base import BasePermission

from app.models.enums import UserRoleEnum


class AdminPermission(BasePermission):
    message_exception: str = "Se requiere rol admin"

    async def has_permission(self, request: Request) -> bool:
        user = getattr(request.state, "user", None)
        if not user:
            return False
        return user.role == UserRoleEnum.admin

request.state.user lo setea AuthenticationMiddleware por request.

Por acción del controller

def check_permissions(self):
    if self.action in ("delete_thing", "update_thing"):
        return [AdminPermission]
    if self.action == "publish_thing":
        return [AdminPermission, PublisherPermission]   # AND
    return []

@router.delete("/{thing_id}")
async def delete_thing(self, thing_id: uuid.UUID):
    await self.check_permissions_class()    # dispara verificación
    return await self.delete(thing_id)

check_permissions_class() itera la lista y lanza PermissionException (HTTP 403) si alguna has_permission() falla.

Permisos compuestos

class WriterPermission(BasePermission):
    """admin OR manager OR seller (no readonly)."""
    message_exception = "Solo lectura: no puede modificar"

    async def has_permission(self, request: Request) -> bool:
        user = getattr(request.state, "user", None)
        if not user:
            return False
        return user.role in (UserRoleEnum.admin, UserRoleEnum.manager, UserRoleEnum.seller)

Platform admin

class PlatformAdminPermission(BasePermission):
    message_exception = "Se requiere ser administrador de plataforma"

    async def has_permission(self, request: Request) -> bool:
        user = getattr(request.state, "user", None)
        return bool(user and getattr(user, "is_platform_admin", False))

Object-level permissions

BasePermission.has_permission() recibe solo request. Para verificar ownership de objetos:

class ThingOwnerPermission(BasePermission):
    message_exception = "Solo el dueño puede modificar este recurso"

    async def has_permission(self, request: Request) -> bool:
        user = getattr(request.state, "user", None)
        if not user:
            return False
        thing_id = request.path_params.get("thing_id")
        if not thing_id:
            return False
        # OPCIÓN A: query directo (rompe abstraction)
        # OPCIÓN B: setear flag en service.retrieve y verificar request.state
        thing_owner_id = getattr(request.state, "_thing_owner_id", None)
        return thing_owner_id == user.id

Más limpio: hacer el check en el service directo, no en BasePermission.

RBAC con tablas

Para permission system DB-driven (Roles/Permissions/Modules tables + EndpointPermission middleware), ver el ejemplo en axion_accounter_backend o fluxio_core_backend — usan PermissionMiddleware que consulta DB en cada request.

Para apps simples, role-enum + BasePermission subclasses es más liviano.