Definition
Modelo de autorización donde permisos se asignan a roles, y roles se asignan a usuarios. Los usuarios heredan todos los permisos de sus roles asignados. Simplifica gestión de permisos agrupando permisos relacionados en funciones laborales significativas.
Example
Rol “Editor” tiene permisos [read:articles, write:articles, publish:articles]. Rol “Viewer” tiene [read:articles]. Usuario Alice asignado rol “Editor” hereda los tres permisos. Usuario Bob asignado “Viewer” solo puede leer.
Analogía
Como títulos de trabajo en una compañía. Un rol “Gerente” tiene ciertas responsabilidades y acceso (aprobar gastos, ver salarios). Cualquiera contratado como Gerente automáticamente obtiene esos privilegios. Cuando dejan ese rol, los pierden. Mucho más fácil que rastrear permisos individuales para cada empleado.
Code Example
// RBAC Implementation
class RBACManager {
constructor() {
// Define roles and their permissions
this.roles = {
'admin': {
permissions: ['*:*'], // Superuser
description: 'Full system access'
},
'editor': {
permissions: [
'read:articles',
'write:articles',
'publish:articles',
'read:comments',
'moderate:comments'
],
description: 'Content editor'
},
'author': {
permissions: [
'read:articles',
'write:own-articles',
'read:comments'
],
description: 'Content author'
},
'viewer': {
permissions: [
'read:articles',
'read:comments'
],
description: 'Read-only access'
},
'moderator': {
permissions: [
'read:*',
'delete:comments',
'ban:users'
],
description: 'Community moderator'
}
}
// Role hierarchy (optional)
this.hierarchy = {
'admin': ['editor', 'moderator', 'author', 'viewer'],
'editor': ['author', 'viewer'],
'author': ['viewer']
}
}
// Check if user has required role
hasRole(user, requiredRole) {
if (!user.roles) return false
return user.roles.includes(requiredRole)
}
// Check if user has any of required roles
hasAnyRole(user, requiredRoles) {
if (!user.roles) return false
return requiredRoles.some(role => user.roles.includes(role))
}
// Get all permissions for a user based on their roles
getUserPermissions(user) {
const permissions = new Set()
if (!user.roles) return permissions
user.roles.forEach(roleName => {
const role = this.roles[roleName]
if (role) {
role.permissions.forEach(perm => permissions.add(perm))
}
// Include inherited permissions via hierarchy
const inheritedRoles = this.hierarchy[roleName] || []
inheritedRoles.forEach(inheritedRole => {
const inherited = this.roles[inheritedRole]
if (inherited) {
inherited.permissions.forEach(perm => permissions.add(perm))
}
})
})
return permissions
}
// Check if user has specific permission via their roles
hasPermission(user, requiredPermission) {
const userPermissions = this.getUserPermissions(user)
// Check direct match
if (userPermissions.has(requiredPermission)) {
return true
}
// Check wildcard permissions
const [action, resource] = requiredPermission.split(':')
return (
userPermissions.has('*:*') ||
userPermissions.has(`${action}:*`) ||
userPermissions.has(`*:${resource}`)
)
}
// Assign role to user (with validation)
assignRole(user, roleName) {
if (!this.roles[roleName]) {
throw new Error(`Invalid role: ${roleName}`)
}
if (!user.roles) {
user.roles = []
}
if (!user.roles.includes(roleName)) {
user.roles.push(roleName)
}
}
// Remove role from user
removeRole(user, roleName) {
if (user.roles) {
user.roles = user.roles.filter(role => role !== roleName)
}
}
}
// Middleware for role-based authorization
const rbac = new RBACManager()
function requireRole(roleName) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' })
}
if (!rbac.hasRole(req.user, roleName)) {
return res.status(403).json({
error: 'Forbidden',
message: `Required role: ${roleName}`
})
}
next()
}
}
function requireAnyRole(...roleNames) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' })
}
if (!rbac.hasAnyRole(req.user, roleNames)) {
return res.status(403).json({
error: 'Forbidden',
message: `Required one of: ${roleNames.join(', ')}`
})
}
next()
}
}
// Usage in routes
app.get('/api/articles',
requireAnyRole('viewer', 'author', 'editor', 'admin'),
async (req, res) => {
const articles = await db.articles.findAll()
res.json(articles)
}
)
app.post('/api/articles',
requireRole('editor'),
async (req, res) => {
const article = await db.articles.create(req.body)
res.json(article)
}
)
app.delete('/api/users/:id',
requireRole('admin'),
async (req, res) => {
await db.users.delete(req.params.id)
res.status(204).send()
}
)
// Dynamic RBAC with database
class DynamicRBAC {
async getRolePermissions(roleName) {
// Fetch from database instead of hardcoded
const role = await db.roles.findOne({ where: { name: roleName }})
return role?.permissions || []
}
async createRole(name, permissions, description) {
return await db.roles.create({
name,
permissions,
description,
createdAt: new Date()
})
}
async updateRolePermissions(roleName, permissions) {
await db.roles.update(
{ permissions },
{ where: { name: roleName }}
)
// Invalidate permission cache
await this.invalidatePermissionCache(roleName)
}
async assignRoleToUser(userId, roleName) {
await db.userRoles.create({
userId,
roleName,
assignedAt: new Date()
})
// Audit log
await auditLog.log({
action: 'role_assigned',
userId,
roleName,
timestamp: new Date()
})
}
}
Notas de Seguridad
CRÍTICO: RBAC es ampliamente usado pero tiene consideraciones de seguridad críticas. Vulnerabilidades: (1) Explosión de roles - …
Configuración:
- demasiados roles granulares derrota el propósito, se vuelve inmanejable, (2) Acumulación de privilegios - usuarios acumulan roles con el tiempo, nunca pierden los antiguos, (3) Bypass de asignación de roles - usuarios capaces de asignarse roles a sí mismos, (4) Validación de roles faltante - aceptar cualquier nombre de rol sin validación, (5) Escalada horizontal - “editor” para un tenant accediendo recursos de otro tenant, (6) Confusión de jerarquía de roles - no claro qué rol tiene precedencia.
- Mejores prácticas: Implementar principio de menor privilegio (roles mínimos necesarios), auditar regularmente asignaciones de roles (revisiones trimestrales), implementar expiración de roles para acceso temporal, separar asignación de roles de operaciones normales, validar nombres de roles contra lista blanca, implementar multi-tenancy cuidadosamente (rol limitado a tenant), registrar todos los cambios de roles con quién/cuándo/por qué, implementar acceso de emergencia “romper vidrio”, evitar permisos superpuestos en roles, usar jerarquía de roles con moderación (es complejo), nunca confiar en roles proporcionados por cliente (siempre obtener del servidor), implementar certificación de roles (gerentes aprueban roles de subordinados), probar combinaciones de roles para conflictos.
Mejores Prácticas:
- demasiados roles granulares derrota el propósito, se vuelve inmanejable, (2) Acumulación de privilegios - usuarios acumulan roles con el tiempo, nunca pierden los antiguos, (3) Bypass de asignación de roles - usuarios capaces de asignarse roles a sí mismos, (4) Validación de roles faltante - aceptar cualquier nombre de rol sin validación, (5) Escalada horizontal - “editor” para un tenant accediendo recursos de otro tenant, (6) Confusión de jerarquía de roles - no claro qué rol tiene precedencia.
- Mejores prácticas: Implementar principio de menor privilegio (roles mínimos necesarios), auditar regularmente asignaciones de roles (revisiones trimestrales), implementar expiración de roles para acceso temporal, separar asignación de roles de operaciones normales, validar nombres de roles contra lista blanca, implementar multi-tenancy cuidadosamente (rol limitado a tenant), registrar todos los cambios de roles con quién/cuándo/por qué, implementar acceso de emergencia “romper vidrio”, evitar permisos superpuestos en roles, usar jerarquía de roles con moderación (es complejo), nunca confiar en roles proporcionados por cliente (siempre obtener del servidor), implementar certificación de roles (gerentes aprueban roles de subordinados), probar combinaciones de roles para conflictos.