CreaRack-SL

Refactor R7 lote P2 · Autorización por scope módulo (is_admin → require_perm)

Resumen

Refactor arquitectónico de autorización (auditoría s187 §3.3-P2): 52 gates de endpoints Ninja migrados del helper legacy is_admin() a require_perm(request, "<scope>", "admin"). Con roles por defecto el comportamiento es idéntico (solo admin alcanza nivel admin); la mejora deliberada es que los overrides por módulo (ModulePermission / TemporaryAccess) ahora se evalúan contra el scope correcto del módulo, en lugar de siempre exigir users:admin.

Versión: v1.45.0 (2026-07-04)
Scopes migrados: users, racks, network, fleet
Archivos afectados: 15 (core/api, racks/api, network/api, terminal/api)


Cambios por módulo

Nota de curación (s197): la primera versión de esta sección listaba RUTAS inventadas por el escritor; se reemplazó por el inventario real a nivel de función, verificado contra el diff del PR #270.

Core → scope users (6 ficheros, 22 gates)

  • core/api/users.py: _list_users_impl, _create_user_impl, get_users_mfa_status, delete_user, get_user_mfa_devices, delete_user_mfa_device (+ update_user usa has_permission en su lógica admin-o-self).
  • core/api/admin.py: get_permissions, set_permissions, reset_permissions, grant_temp_access, revoke_temp_access.
  • core/api/settings.py: update_company_settings, upload_company_logo (los endpoints de onboarding y session-timeout NO tenían gate y siguen igual).
  • core/api/logs.py: get_logs_data, export_logs_csv.
  • core/api/login_history.py: get_login_history.
  • core/api/backup.py: download_latest_backup.

Racks → scope racks (7 ficheros, 10 gates)

  • racks/api/library.py: move_stencil, delete_library_category, rename_library_category.
  • racks/api/library_files.py: analyze_visio.
  • racks/api/export/restore.py: restore_full · export/backup.py: backup_full · export/csv_export.py: export_csv · export/json_export.py: import_racks · export/network_config.py: save_device_network_config, import_image.

Network → scope network (1 fichero, 7 gates)

  • network/api/vendor.py: create_vendor_profile, update_vendor_profile, upload_mib, delete_custom_mib, apply_custom_oids, mib_assistant_analyze, mib_assistant_apply (catálogo VendorProfile GLOBAL → admin only).

Terminal → scope fleet (1 fichero, 5 gates)

  • terminal/api/fleet.py: set_fleet_config, promote_agent, demote_agent, delete_agent, reauth_agent.

Mejoras de autorización

Antes

  • Todos estos 52 endpoints exigían is_admin(user) → verificaban solo si el usuario tenía rol admin global.
  • Los overrides por módulo (ModulePermission: dar a alguien nivel admin SOLO de Network, racks, etc.) se ignoraban.
  • Respuestas 403 inconsistentes: convivían {"message"}, {"error"}, HttpError con textos distintos.

Después

  • Cada endpoint pide require_perm(request, "<scope>", "admin") del módulo correcto.
  • Overrides por módulo (ModulePermission, TemporaryAccess) se evalúan contra el scope del módulo:
    • require_perm(request, "racks", "admin") respeta permisos tipo racks:admin.
    • require_perm(request, "network", "admin") respeta permisos tipo network:admin.
  • Respuesta 403 unificada: {"detail": "Forbidden: requires 'admin' permission on '<scope>'"} (formato estándar de Ninja).
  • is_admin() queda SOLO para las vistas Django clásicas (core/views.py, core/htmx_views.py), donde require_perm de Ninja no aplica.

Fixes colaterales

GET /api/backup/latest (latent 500)

  • Problema: endpoint declaraba en response= solo {200, 404} pero devolvía tuplas 403 (rechazo) y 400 (error de validación) → error de configuración de Ninja (500 silencioso al negar acceso).
  • Solución: agregado 400: ErrorSchema en response=; el 403 ahora sale limpio por require_perm.

Deprecación

ChartDefaultsIn.refresh_interval (retirada)

  • Toggle de refresco por pestaña de Observatory retirado en v1.43.0 (cadencia centralizada por dispositivo, task #175).
  • Ningún consumidor —ni código JS ni backend— leía ya este campo.
  • Acción: removido de monitoring/api/schemas.py.

Impacto usuario

  • Roles estándar (admin/operator/readonly): sin cambios visibles. El comportamiento es idéntico.
  • Overrides por módulo (permisos finos): ahora se respetan en todas partes, incluso en estos 52 endpoints antiguos.
  • Accesos temporales (TemporaryAccess): granularidad mejorada — ya no necesitan nivel users:admin global para operar en un módulo específico.
  • Mensajes de error: más consistentes y legibles.

Testing

  • Sin tests nuevos: los tests de autorización EXISTENTES cubren el sweep y siguen en verde (test_network_sa3.py 24 ✓ · test_racks_security.py 7 ✓ · test_auth.py) — asertan códigos 403, no cuerpos, y las semánticas por rol son idénticas.
  • Suite completa verde en el gate pre-push local (1131 tests) + CI del PR #270 (2 shards en runners self-hosted).

Véase también

  • [[entity—core—service—require-perm]]
  • [[entity—core—service—has-permission]]
  • [[decision—20260609—require-perm-unified-authorization]]
  • [[concept—saas—multi-tenancy]]
  • [[concept—saas—rbac-granular]]