Exprimez middleware: permettre l'accès à seul administrateur ou le modérateur

voix
0

Je veux une route pour être accessible par un modérateur ou administrateur. J'ai essayé d'appliquer un middleware de réseau sur la route. Mais, il refuse tout accès si un middleware ne demande pas.
donc, dire que je suis admin ou modérateur je peux accéder à /store-detail.
Mais ici, si je suis admin je ne peux pas y accéder car il vérifier modérateur aussi bien.
Ici _both intergiciels adminet moderatorest appliquée.
Je veux appliquer adminou moderator.
Comment puis - je faire une d'entre eux à appliquer?
Alors que seul administrateur ou le modérateur y ont accès.
verifymiddleware est de vérifier jeton JWT.
routeur

router.post('/store-detail', [verify, admin, moderator], async (req, res) => {
    //validate data
}}

intergiciels

const User = require('../models').User

module.exports = async function (req, res, next) { 
    // 401 Unauthorized
    const user = await User.findOne({ where: { id : req.user.id}})
    if(user.role !== 'moderator') return res.status(403).send({error: { status:403, message:'Access denied.'}});
    next();
  }
const User = require('../models').User

module.exports = async function (req, res, next) { 
    // 401 Unauthorized
    const user = await User.findOne({ where: { id : req.user.id}})
    if(user.role !== 'admin') return res.status(403).send({error: { status:403, message:'Access denied.'}});
    next();
  }
Créé 09/10/2019 à 12:52
source utilisateur
Dans d'autres langues...                            


4 réponses

voix
1

Intergiciels sont exécutées successivement, de sorte que le premier middleware qui refuse l'accès, envoie une erreur. Je suggère de créer un middleware unique qui accepte un paramètre:

module.exports = function hasRole(roles) {
  return async function(req, res, next) {
    const user = await User.findOne({ where: { id: req.user.id } });
    if (!user || !roles.includes(user.role) {
      return res.status(403).send({error: { status:403, message:'Access denied.'}});
    }
    next();
  }
}

Et utiliser le middleware comme ceci:

router.post('/store-detail', verify, hasRole(['admin', 'moderator']), async (req, res) => {})
Créé 09/10/2019 à 13:09
source utilisateur

voix
0

Une solution de JWT: (désolé pour le long courrier, si cela est unappropriate, je peux supprimer)

utilisateurs itinéraire (itinéraires / users.js) je reviens ici un auth-jeton en service de connexion.

const auth = require("../middleware/auth");
const hasRole = require("../middleware/hasRole");
const bcrypt = require("bcryptjs");
const { User } = require("../models/user");
const express = require("express");
const router = express.Router();

router.post("/register", async (req, res) => {
  const { name, email, password } = req.body;

  let user = await User.findOne({ email });
  if (user) return res.status(400).send("User already registered.");

  user = new User({ name, email, password });
  const salt = await bcrypt.genSalt(10);
  user.password = await bcrypt.hash(user.password, salt);
  await user.save();

  const token = user.generateAuthToken();
  res.header("auth-token", token).send({ name, email });
});

router.post("/login", async (req, res) => {
  const { email, password } = req.body;

  let user = await User.findOne({ email });

  if (!user) return res.status(400).send("Invalid email or password.");

  const validPassword = await bcrypt.compare(password, user.password);

  if (!validPassword) return res.status(400).send("Invalid email or password.");

  const token = user.generateAuthToken();

  res.send(token);
});

module.exports = router;

user.js (modèle utilisateur) J'ajouter ici les informations de rôle au JWT lors de la signature, il:

const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  role: {
    type: String,
    default: "user"
  }
});

userSchema.methods.generateAuthToken = function() {
  const token = jwt.sign(
    { _id: this._id, role: this.role },
    process.env.JWT_PRIVATE_KEY
  );
  return token;
};

const User = mongoose.model("User", userSchema);

exports.User = User;

middleware / auth.js Ici, j'authentifier l'utilisateur final, et en ajoutant jeton décodés à la demande afin que nous puissions utiliser dans les prochains intergiciels.

const jwt = require("jsonwebtoken");

module.exports = function(req, res, next) {
  const token = req.header("auth-token");
  if (!token) return res.status(401).send("Access denied. No token provided.");

  try {
    const decoded = jwt.verify(token, process.env.JWT_PRIVATE_KEY);
    req.user = decoded;
    next();
  } catch (ex) {
    res.status(400).send("Invalid token.");
  }
};

middleware / hasRole.js nous vérifions ici si le user.role satisfait aux rôles requis.

module.exports = function hasRole(roles) {
  return function(req, res, next) {
    if (!req.user.role || !roles.includes(req.user.role)) {
      return res.status(403).send("Access denied.");
    }

    next();
  };
};


on ajoute enfin le chemin suivant à la route des utilisateurs d'être en mesure de tester l'autorisation de rôle.

router.get("/me", [auth, hasRole(["admin", "moderator"])], async (req, res) => {
  const user = await User.findById(req.user._id).select("-password");
  res.send(user);
});

Tester:

Créer 3 utilisateurs séparément suivant avec POST à ​​l'adresse suivante

http: // localhost: 3000 / api / users / register

{
    "name": "admin",
    "email": "admin@so.com",
    "password" : "123456"
}

{
    "name": "moderator",
    "email": "moderator@so.com",
    "password" : "123456"
}

{
    "name": "user",
    "email": "user@so.com",
    "password" : "123456"
}

En MongoDB, dans la collection des utilisateurs que nous mettons à jour la propriété de rôle à admin pour l'utilisateur admin et modérateur pour l'utilisateur du modérateur.

L'utilisation postier ou un outil similaire, nous avec des informations d'identification de Se connecter admin ou modérateur.

Dans sa réponse, nous obtenons le jeton auth, nous l'utilisons en-tête auth-jeton pour ce paramètre protégé requête GET.

http: // localhost: 3000 / api / utilisateurs / Me

Vous obtiendrez le code d'état 200.

Cette fois, nous connectons avec les informations d'identification d'utilisateur normal. Dans la réponse, on copie du jeton d'authentification, et l'utiliser dans la tête auth-jeton pour ce paramètre protégé requête GET.

http: // localhost: 3000 / api / utilisateurs / Me

Vous obtiendrez le code d'état 403 Interdit.

Créé 09/10/2019 à 14:04
source utilisateur

voix
0

une partie non pertinente

Du point de vue de l'architecture du système, voici ce que je ferai.

  1. Au lieu de faire la demande de réseau à l'intérieur du middleware à chaque fois, stocker le niveau d'accès utilisateur dans un jeton (par exemple jeton JWT).

  2. La connexion de l'utilisateur moment au système, faire une demande au back-end, le back-end fera la demande à db retrive des détails (qui devrait prendre en compte le niveau d'accès) et crypter les informations utilisateur en jeton. Votre jeton doit contenir le niveau d'accès.

  3. A chaque demande, vous aurez jeton dans les en-têtes, de-crypte le jeton, et définir les détails des niveaux d'accès (res.locals devrait être l'endroit idéal pour mettre en jetons).

   const verifyUser = async (req, res, next) => {
        const token = req.cookies.userId
        if(token) {
          try {
            const tokenVerficiation = await verifyToken(token)
            res.locals.userId = tokenVerficiation.userId
            next()
          } catch (error) {
            return res.status(401).send(`Invalid Access token`)
          }
        } else { 
         return res.status(401).send(`Not Authorized to view this.`)
        }
    }

partie pertinente

Puisque vous définissez res.status(403).send({error: { status:403, message:'Access denied.'}});il est logique pourquoi il ne se déplace pas au prochain middleware.

Depuis un APi vous appelez pouvez *Probablyenvoyer des en- têtes une fois Comme suggéré par Jérémie L création d' un middleware unique nouvelle serait la meilleure approche

module.exports = function hasRole(roles) {
  return async function(req, res, next) {
    const user = await User.findOne({ where: { id: req.user.id } });
    if (!user || !roles.includes(user.role) {
      return res.status(403).send({error: { status:403, message:'Access denied.'}});
    }
    next();
  }
}
Créé 09/10/2019 à 13:59
source utilisateur

voix
0

Créer une nouvelle fonction , par exemple isAuthorizedpour gérer cette logique.

Si vous appelez [verify, admin, moderator]ils sont séparément ne dépendaient les uns des autres.

route:

router.post('/store-detail', isAuthorized, async (req, res) => {
    //validate data
}}

function isAuthorized(req, res, next) {
  if (!verify(req)) 
    return next();
  if (!admin(req) && !moderator(req)) 
    return res.status(403).send({error: { status:403, message:'Access denied.'}});
}

middleware:

const User = require('../models').User

module.exports = async function (req, res, next) { 
  // 401 Unauthorized
  const user = await User.findOne({ where: { id : req.user.id}})
  if(user.role === 'moderator') 
    return true;
  return false;
}

Je pense que vous devez modifier verify(req), admin(req)et moderator(req).

Créé 09/10/2019 à 13:06
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more