Control de acceso basado en roles (RBAC) para BD NoSQL en Nodejs

Muchas implementaciones de RBAC (Role-Based Access Control) difieren, pero los fundamentos son ampliamente adoptados ya que simula la asignación de roles (trabajos) en la vida real. - Onur Yıldırım.

· 3 min de lectura
Control de acceso basado en roles (RBAC) para BD NoSQL en Nodejs

El control de acceso basado en roles (RBAC) es un método para modificar el acceso y los privilegios en función de los roles de los usuarios individuales o de los grupos de usuarios de una organización.

El RBAC permite que los miembros de una organización tengan acceso, únicamente, a los recursos que necesitan para realizar su trabajo y restringe o impide los privilegios ilimitados a los recursos que no les corresponden.

RBAC vs ABAC


El control de acceso basado en roles y el control de acceso basado en atributos (ABAC) son dos tipos de métodos de control de acceso, pero sus enfoques son diferentes. Un caso de uso que requiere un sistema RBAC, sería si pretendemos conceder privilegios en la aplicación dependiendo de los roles de los usuarios.

Mientras que ABAC concede el acceso basado en una combinación de atributos, es decir, atributos de usuario, atributos de recursos, atributos asociados con el sistema o la aplicación a la que se accede, atributos del entorno, entre otros. Por ejemplo, los atributos del usuario pueden incluir información de biodatos, identificación única, rol, autorización de seguridad, y más.

Anécdota


En un proyecto reciente, a mi equipo se le encargó la implementación de un marco de control de acceso basado en roles para MongoDB (DB NoSQL), utilizando el esquema de mongoose ORM. En este marco de control de acceso vamos a considerar 3 entidades :

Contexto de implementación

  • El Usuario tiene un Rol
  • La Aplicación ha definido Recursos y,
  • El rol tiene privilegios y permisos sobre los Recursos

Vamos a escribir algunos esquemas de mongoose para nuestras colecciones 😄

Esquema


recursos.modelos.js

/* RBAC - role-based access control
 first create roles; role =[ "user", "guest", "organization",  "superadmin" "globalsuperadmin"]
 next, create resouuce, add their roles_id and permmissions
 finally create users and indcate thier roles
*/
const mongooseClient = app.get('mongooseClient');
    const { Schema } = mongooseClient;
const resources = new Schema({
      name: { type: String, required: true },
      slug: { type: String, required: true },
      resources_roles: [{
        roles_id: { type: Schema.Types.ObjectId, ref : 'Roles' },
        roles_name: { type:  String },
        create: { type: Boolean },
        delete: { type: Boolean },
        update: { type: Boolean },
        read: { type: Boolean },
      }]
    }, {
      timestamps: true
    });
return mongooseClient.model('resources', resources);

roles.models.js

const mongooseClient = app.get('mongooseClient');
    const { Schema } = mongooseClient;
module.exports = function (app) {
    const mongooseClient = app.get('mongooseClient');
    const { Schema } = mongooseClient;
    const roles = new Schema({
      name: { type: String, required: true },
      slug: { type: String, required: true },
    }, {
      timestamps: true
    });
return mongooseClient.model('roles', roles);
  };

users.models.js

const mongooseClient = app.get('mongooseClient');
    const { Schema } = mongooseClient;
module.exports = function (app) {
    const mongooseClient = app.get('mongooseClient');
    const { Schema } = mongooseClient
    const users = new mongooseClient.Schema({
      email: {type: String, unique: true, lowercase: true},
      password: { type: String },
      first_name: { type: String },
      last_name: { type: String },
      roles: { type: Schema.Types.ObjectId, ref : 'Roles' },
}, {
      timestamps: true
    });
return mongooseClient.model('users', users);
  };

Ahora, cuando quieras los permisos de un rol sobre un recurso, sólo tienes que buscar role_id y resource_id y comprobar qué permisos están establecidos como true en el resources collection.

Middleware de autorización

users.post('/', getAuth, someMethod)

Asumiendo que tienes un token de algún tipo en la petición que identifica al usuario que hace la publicación, y adjuntando la instancia del usuario al objeto de la petición puedes hacer esto:

// pseudo code
getAuth = function (req, res, next) {
  if(req.user) {
// query to get the user role's  permissions for a resource
    if(token){
    // handle jwt token authenticity and decrypt payload
// get permission handler
    db.getPerms({ role_id: req.user.role_id, resource_id: req.resource.id})
    .then((perms) => {
       var allow = false;
       // mapping of methods to permissions
       perms.forEach(function(perm){
           if (req.method == "POST" && perms.create) allow = true;
           else if (req.method == "GET" && perms.read) allow = true;
           else if (req.method == "PUT" && perms.write) allow = true;
           else if (req.method == "DELETE" && perm.delete) allow = true;
})
       if (allow) next();
       else {
            res.status(403).send({error: 'access denied'});
        }
    })
    .catch((err)=> {
       //handle your reject and catch here
    })
} else{
    res.status(400).send({error: 'invalid token'})
}

Este es un poco de Pseudo-código, mostrando cómo el middleware de autenticación puede ser escrito.😄

Fuente

¡Este no es el framework más optimizado para implementar RBAC!

Gracias por la audiencia y espero que hayas encontrado este artículo útil.

Plataforma de cursos gratis sobre programación