Matriz de roles y permisos
Documento RUP — 2026-06-07
Define quién puede hacer cada acción en el sistema IslaBox. Esta matriz es la fuente única de verdad para autorización. Se referencia desde el backend (middleware de permisos), desde el frontend (ocultar/mostrar botones) y desde el contrato API (filtros de scope por token).
Roles del sistema
| Rol |
Código |
Descripción |
| Usuario Final |
user |
Persona radicada en EE.UU. que se registra y opera la app móvil |
| Operador |
operator |
Personal interno de IslaBox que opera el backoffice (recibe paquetes, KYC, conciliaciones) |
| Administrador |
admin |
Personal interno con permisos de configuración del sistema (tarifas, políticas, catálogos) |
| Sysadmin |
sysadmin |
Personal técnico con acceso a logs, métricas y operaciones destructivas |
| Agencia Aliada (sistema) |
agency |
API externa que recibe webhooks de IslaBox y consulta estado de envíos |
| Stripe (sistema) |
stripe |
Pasarela de pago que recibe webhooks de confirmación de cobro |
Modelo de datos
El rol vive en users.role (enum) y los permisos granulares se derivan de un mapa estático en backend (core/permissions.py). Para el MVP no se requiere una tabla permissions + role_permissions; se valida en código. Si en el futuro se necesita mayor granularidad, se introduce el modelo RBAC completo.
Matriz de acciones
Leyenda: ✅ Permitido · ⚠️ Permitido con restricción · ❌ Prohibido · 🔵 Solo lectura
Autenticación y perfil
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Crear cuenta propia |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Iniciar sesión |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Editar perfil propio |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Ver perfil de otro usuario |
❌ |
⚠️ (solo si es su usuario asignado) |
✅ |
✅ |
— |
— |
| Cambiar rol de un usuario |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Desactivar cuenta |
❌ |
❌ |
✅ |
✅ |
— |
— |
Casillero y dirección
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Ver su propia dirección del casillero |
✅ |
✅ (cualquiera) |
✅ |
✅ |
— |
— |
| Ver dirección de otro usuario |
❌ |
✅ |
✅ |
✅ |
⚠️ (solo el user_id que les compete) |
— |
| Modificar dirección del casillero |
❌ |
❌ |
✅ |
✅ |
— |
— |
Prealerta (reporte de compra)
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Crear prealerta |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Ver sus prealertas |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Ver prealertas de todos |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Editar prealerta propia |
✅ (solo borrador, reportada_sin_tracking, requiere_correccion) |
✅ |
✅ |
✅ |
— |
— |
| Cancelar prealerta propia |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Adjuntar foto de la orden |
✅ |
✅ |
✅ |
✅ |
— |
— |
Paquetes físicos (recepción en almacén)
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Ver paquete propio |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Ver paquete cualquiera |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Recibir paquete físico (peso, dimensiones, fotos) |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Conciliar paquete sin identificar |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Cambiar estado logístico manualmente |
❌ |
✅ (cualquiera) |
✅ |
✅ |
— |
— |
| Escanear código de barras/QR |
❌ |
✅ |
✅ |
✅ |
— |
— |
KYC
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Cargar documentos KYC propios |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Ver su propio estado KYC |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Ver documentos KYC de cualquier usuario |
❌ |
⚠️ (datos enmascarados, solo para revisión) |
✅ |
✅ |
— |
— |
| Aprobar KYC |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Rechazar KYC (con motivo) |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Solicitar más información |
❌ |
✅ |
✅ |
✅ |
— |
— |
Consolidación
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Crear consolidación |
✅ (si KYC aprobado) |
❌ |
❌ |
❌ |
— |
— |
| Consolidar manualmente (operador) |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Ver sus consolidaciones |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Ver cualquier consolidación |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Cancelar consolidación propia |
✅ (si borrador, cotizada, pendiente_pago) |
✅ |
✅ |
✅ |
— |
— |
Cotización y pago
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Generar cotización |
❌ (la dispara el sistema al consolidar) |
✅ (manual) |
✅ |
✅ |
— |
— |
| Ver cotización propia |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Aprobar cotización |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Pagar envío (MVP: registro manual) |
❌ |
✅ (registra el pago del usuario) |
✅ |
✅ |
— |
— |
| Pagar envío online (Stripe) |
✅ (solo cuando esté integrado) |
❌ |
❌ |
❌ |
— |
— |
| Registrar pago manual |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Reembolsar |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Webhook de Stripe confirma pago |
— |
— |
— |
— |
— |
✅ (es el único que puede confirmar) |
Despacho y envío
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Ver su envío |
✅ |
✅ |
✅ |
✅ |
⚠️ (solo los que la agencia transporta) |
— |
Confirmar despacho (marcar como despachado_desde_almacen) |
❌ |
✅ |
✅ |
✅ |
⚠️ (vía webhook POST /webhooks/agency) |
— |
| Actualizar estado de envío en tránsito |
❌ |
❌ |
❌ |
❌ |
✅ (vía webhook) |
— |
Servicios adicionales
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Solicitar inspección / reempaque / devolución |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Cancelar servicio propio (si no está en proceso) |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Cargar evidencia (fotos, videos) del servicio |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Aprobar costo de servicio adicional |
✅ |
❌ |
❌ |
❌ |
— |
— |
Incidencias y reclamos
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Abrir incidencia sobre su paquete |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Ver sus incidencias |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Ver cualquier incidencia |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Responder a incidencia (mensaje) |
✅ (solo sobre las suyas) |
✅ |
✅ |
✅ |
— |
— |
| Cambiar estado de incidencia |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Cerrar incidencia con resolución |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Iniciar reembolso por incidencia |
❌ |
⚠️ (escala a admin) |
✅ |
✅ |
— |
— |
Notificaciones
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Ver sus notificaciones |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Marcar notificación como leída |
✅ |
✅ |
✅ |
✅ |
— |
— |
| Configurar preferencias |
✅ |
❌ |
❌ |
❌ |
— |
— |
| Disparar notificación push al usuario |
❌ |
✅ (alertas operativas) |
✅ |
✅ |
— |
— |
Administración y configuración
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Configurar días gratis y costo de almacenaje |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Configurar tarifas de envío (barco/avión) |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Configurar catálogo de categorías aduaneras |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Ver logs de auditoría |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Ejecutar operaciones destructivas (migraciones, reset) |
❌ |
❌ |
❌ |
✅ |
— |
— |
| Ver métricas y dashboards internos |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Gestionar personal (alta/baja de operadores) |
❌ |
❌ |
✅ |
✅ |
— |
— |
Reportes administrativos
| Acción |
user |
operator |
admin |
sysadmin |
agency |
stripe |
| Reporte diario de paquetes recibidos |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Reporte de ingresos por período |
❌ |
❌ |
✅ |
✅ |
— |
— |
| Reporte de incidencias abiertas/cerradas |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Reporte de consolidados pendientes |
❌ |
✅ |
✅ |
✅ |
— |
— |
| Exportar reportes a CSV/PDF |
❌ |
✅ |
✅ |
✅ |
— |
— |
Reglas derivadas de la matriz
Estas reglas se numeran con RN- y se referencian desde el código. Ver reglas-negocio.md para el catálogo completo.
| ID |
Regla |
| RN-RBAC-001 |
Toda acción sobre un recurso que no es propio requiere scope admin o superior, salvo las acciones marcadas con ⚠️. |
| RN-RBAC-002 |
Los webhooks externos (agency, stripe) solo pueden usar los endpoints listados en stack/api-contract.md con el scope correspondiente. |
| RN-RBAC-003 |
Toda acción queda registrada en audit_logs con user_id, action, resource_type, resource_id, timestamp, ip, result (success/forbidden). |
| RN-RBAC-004 |
Los endpoints de admin (/api/v1/admin/*) rechazan requests sin scope admin o sysadmin. |
| RN-RBAC-005 |
El sysadmin es el único que puede ejecutar operaciones destructivas (DROP, TRUNCATE, FORCE_DELETE). Toda operación de sysadmin requiere segundo factor (TOTP). |
Cómo aplicar esta matriz
Backend (Django + FastAPI)
# core/permissions.py
PERMISSIONS = {
("user", "package", "read", "own"): True,
("user", "package", "read", "any"): False,
("operator", "package", "read", "any"): True,
("operator", "package", "update", "any"): True,
("admin", "tariff", "update", "any"): True,
# ...
}
def check_permission(role, resource, action, scope) -> bool:
return PERMISSIONS.get((role, resource, action, scope), False)
Frontend (Android Kotlin)
// ui/permissions/Can.kt
@Composable
fun Can(action: String, resource: String) {
val role = CurrentUser.role
val visible = PermissionMap.get(role)?.get("$resource:$action") == true
if (!visible) {
// Ocultar botón o deshabilitar
}
}
Contrato API
Ver api-contract.md — cada endpoint declara su permiso requerido, ej:
- path: /api/v1/admin/storage-policy
method: PATCH
permission: admin
audit: true