CreaRack-SL

Servicio has_permission — Evaluador de permisos multi-capa

Entidadactivecreado Tue Jun 02#core#security#rbac#permissions#python#django

Resumen

Función central de evaluación de permisos en el núcleo. Verifica si un usuario tiene acceso a una acción específica (scope/level) según una jerarquía de cuatro capas.

Función: core.utils.permissions.has_permission(user, scope: str, level: str = "view") -> bool

Módulo: core/utils/permissions.py

Arquitectura de evaluación (4 capas)

1. Role-based defaults (SaaSModule + Plan)
   ↓ (si existe)
2. TemporaryAccess (grants temporales por tiempo limitado)
   ↓ (si existe + si DB disponible)
3. ModulePermission (overrides explícitos por usuario)
   ↓ (si existe + si DB disponible)
4. Fallback a role defaults

Primero se evalúa rol; si hay grant temporal o override explícito, se usa. Si no, vuelve a los defaults del rol.

Parámetros

  • user — Usuario Django (tenga o no role, organization, module_permissions, etc.).
  • scope — Cadena del dominio (racks, network, inventory, monitoring, etc.).
  • level — Nivel de acceso: view, edit, admin. Default: view.

Retorno

  • True — Usuario tiene permisos.
  • False — Usuario no tiene permisos.

Orden jerárquico de niveles

LEVEL_ORDER = {"view": 1, "edit": 2, "admin": 3}

Nivel edit incluye view. Nivel admin incluye edit y view.

Requisitos de seguridad (s104) — Fail-open fix

Problema histórico: Los bloques try/except de TemporaryAccess y ModulePermission tragaban toda excepción con except Exception: pass, incluyendo errores inesperados de BD. Esto causaba un fail-open silencioso: ante un error BD imprevisto, el usuario caía a los defaults del rol (potencialmente más permisivo que una denegación explícita).

Fix (s104, 2026-06-02):

  • El except se estrecha a ProgrammingError / OperationalError (caso “tabla no migrada” en deployments iniciales).
  • Cualquier otra excepción se registra con logger.exception(...), haciendo visible el fallo.
  • El usuario sigue cayendo a los defaults del rol, pero el error está documentado en logs.

Casos de uso

  • Verificar acceso al endpoint GET /api/credentials/{id}/decrypt → requiere network/edit.
  • Verificar acceso al endpoint PUT /api/settings/company → requiere is_admin().
  • Filtros en vistas (ej. if has_permission(user, "racks", "edit"): ...).
  • Gate de mutadores HTMX de racks/papelera (core/htmx_views.py) — antes solo @login_required dejaba mutar a cualquier readonly; ahora has_permission(request.user, "racks", level) + fragmento 403 (racks:edit en creación/edición, racks:admin en borrado permanente y vaciado de papelera). Tanda 1 Auditoría Suprema 2, v1.90.0.

Corrección 06-09-2026 (task #279, v1.118.0, commit b160abd9): core/htmx_device_groups.py (grupos de dispositivos) exigía has_permission(request.user, "racks", level) — dominio equivocado, copiado de los mutadores de racks de arriba. Un grupo de dispositivos es dominio “network”, no “racks”: ahora network:edit al crear y network:admin al borrar, alineado con sus gemelos de la API Ninja (network/api/device_groups.py::create_device_group/delete_device_group). Se añadió también log_action en ambos mutadores, que no lo tenían.

Tests

  • tests/test_permissions.py::test_unexpected_error_is_logged_not_swallowed — Fallo no-BD → logger.exception() invocado.
  • tests/test_permissions.py::test_migration_error_stays_silent — ProgrammingError → sin logging (esperado).
  • tests/test_htmx_authz.py — tests del gate en mutadores HTMX (racks/papelera/grupos); +3 tests 06-09-2026 (task #279) cubriendo el nuevo scope network de core/htmx_device_groups.py (usuario con network:none no crea, operator no borra sin network:admin, admin sí borra).

Véase también

  • [[entity—core—model—temporary-access]]
  • [[entity—core—model—module-permission]]
  • [[entity—core—service—decrypt-credential]]
  • [[entity—core—endpoint—update-company-settings]]
  • [[feature—security—s104-auditoria-suprema-fixes-core]]
  • [[concept—security—fail-open]]
  • [[concept—security—role-based-access-control]]
  • [[feature—security—auditoria-suprema-2-tanda-1-core]]
  • [[entity—core—service—require-perm]]