Crear un middleware expressjs que acepte parámetros

105

Estoy intentando crear un middleware que pueda aceptar parámetros. ¿Cómo se puede hacer esto?

ejemplo

app.get('/hasToBeAdmin', HasRole('Admin'), function(req,res){

})

HasRole = function(role, req, res, next){
   if(role != user.role){
      res.redirect('/NotInRole);
   }

   next();
}
Vartan Arabyan
fuente
41
Ja, has hecho mi pregunta exacta para mi escenario exacto, pero 6 años antes. ASÍ que es impresionante.
Aero
6
@aero exactamente lo mismo que estaba buscando: D
Jeson Dias
4
@aero 7 años después estoy buscando exactamente el mismo: D
jean d'arme
4
@aero 7+ años después, ¡estoy buscando exactamente lo mismo!
sidd
4
@aero 8 ~ años después, ¡estoy buscando exactamente lo mismo! \ o /
Ítalo Sousa

Respuestas:

162
function HasRole(role) {
  return function(req, res, next) {
    if (role !== req.user.role) res.redirect(...);
    else next();
  }
}

También quiero asegurarme de no hacer varias copias de la misma función:

function HasRole(role) {
  return HasRole[role] || (HasRole[role] = function(req, res, next) {
    if (role !== req.user.role) res.redirect(...);
    else next();
  })
}
Jonathan Ong
fuente
9
El segundo método almacena en caché los parámetros, que pueden ser o no el comportamiento deseado.
Pier-Luc Gendreau
1
@Jonathan Ong, ¿podría explicar la segunda definición de la función? ¿Lo que está sucediendo allí? No entiendo la siguiente línea return HasRole [role] || (HasRole [role] = function (req, res, next) {
Rafay Hassan
1
@JonathanOng, ¿podría explicar el segundo ejemplo un poco más para aquellos que no conocen el nodo tan bien? (incluyéndome a mí). ¿Cuándo puede obtener varias copias de la misma función y cuándo esto puede causar problemas? Gracias.
Dave
sin almacenamiento en caché, app.get('/a', hasRole('admin'))y app.get('/b', hasRole('admin'))crearía un nuevo cierre para cada uno hasRole. esto no importa demasiado de manera realista a menos que tenga una aplicación realmente grande. Solo codifico así por defecto.
Jonathan Ong
14
app.get('/hasToBeAdmin', (req, res, next) => {
  hasRole(req, res, next, 'admin');
}, (req,res) => { 
    // regular route 
});

const hasRole = (req, res, next, role) => {
   if(role != user.role){
      res.redirect('/NotInRole');
   }
   next();
};
chovy
fuente
Buena y sencilla idea. El middleware realmente es solo una función normal. ¿Por qué no pasarle otros valores? Incluya req, res y next en la primera función.
zevero
1
Si bien el código a menudo habla por sí mismo, es bueno agregar alguna explicación a su código. Esto apareció en la cola de revisión, como tienden a hacerlo las respuestas de solo código.
Will el
Estaba pensando en ese enfoque antes de visitar SO, pero pensé "Meh, seguramente hay una manera mejor". Después de visitar, veo que esta es realmente la forma más sencilla y eficaz.
Fusseldieb
3

Alternativamente, si no tiene demasiados casos o si el rol NO es una cadena:

function HasRole(role) {
  return function (req, res, next) {
    if (role !== req.user.role) res.redirect(/* ... */);
    else next();
  }
}

var middlware_hasRoleAdmin = HasRole('admin'); // define router only once

app.get('/hasToBeAdmin', middlware_hasRoleAdmin, function (req, res) {

})
zevero
fuente
solución elegante
Leos Literak
2

Si tiene varios niveles de permisos, puede estructurarlos así:

const LEVELS = Object.freeze({
  basic: 1,
  pro: 2,
  admin: 3
});

/**
 *  Check if user has the required permission level
 */
module.exports = (role) => {
  return (req, res, next) => {
    if (LEVELS[req.user.role] < LEVELS[role]) return res.status(401).end();
    return next();
  }
}
asdf
fuente
0

Yo uso esta solución. Recibo un token jwt en body req y obtengo información de rol desde allí

//roleMiddleware.js

const checkRole = role => {
    
    return (req, res, next) => {
        if (req.role == role) {
            console.log(`${role} role granted`)
            next()
        } else {
            res.status(401).send({ result: 'error', message: `No ${role} permission granted` })
        }
    }
}

module.exports = { checkRole }

Entonces, primero uso el middleware de autenticación para saber si es un usuario válido, y luego el middleware de rol para saber si el usuario tiene acceso a la ruta de la API

// router.js

router.post('/v1/something-protected', requireAuth, checkRole('commercial'), (req, res) => {
    // do what you want...
})

Espero ser de utilidad

jarraga
fuente