Donal Rafferty
Donal Rafferty

Reputation: 19826

Working with MEAN.JS users authorization

I have started using the MEAN.js full stack solution to learn how to create a web application.

I have used the generators to create several CRUD models and it has generated all the express routes etc...

It has also created a User model as a template with some roles and some authenticated and authorized middleware.

I want to tweak the middleware slightly but I am not aware of the best way to do this.

For one of my models (Called Themeparks) I want to make sure Users are not only logged in but also have authorization based on their role to perform the post action.

The code below is the code I currently have from the generators and as you can see the '/themeparks/:themeparkId' route has the themeparks.hasAuthorization middleware function call. However this doesn't work on the '/themeparks' route nor does the users.hasAuthorization middleware function.

So I am wondering what is the best way to add user authorization to the post method of the '/themeparks' route? And maybe some resources that have tutorials or cover the user model in the MEAN.js stack?

//Routes

'use strict';

module.exports = function(app) {
var users = require('../../app/controllers/users.server.controller');
var themeparks = require('../../app/controllers/themeparks.server.controller');

// Themeparks Routes
app.route('/themeparks')
    .get(themeparks.list)
    .post(users.requiresLogin, themeparks.create); //<----How do I add authorization based on a role here??

app.route('/themeparks/:themeparkId')
    .get(themeparks.read)
    .put(users.requiresLogin, themeparks.hasAuthorization, themeparks.update)
    .delete(users.requiresLogin, themeparks.hasAuthorization, themeparks.delete);

// Finish by binding the Themepark middleware
app.param('themeparkId', themeparks.themeparkByID);
};

//User middleware

/**
 * Require login routing middleware
 */
exports.requiresLogin = function(req, res, next) {
    if (!req.isAuthenticated()) {
        return res.status(401).send({
            message: 'User is not logged in'
        });
    }

    next();
};

/**
 * User authorizations routing middleware
 */
exports.hasAuthorization = function(roles) {
    var _this = this;

    return function(req, res, next) {
        _this.requiresLogin(req, res, function() {
            if (_.intersection(req.user.roles, roles).length) {
                return next();
            } else {
                return res.status(403).send({
                    message: 'User is not authorized'
                });
            }
        });
    };
};

//Themeparks middleware

/**
 * Themepark middleware
 */
exports.themeparkByID = function(req, res, next, id) { //TODO: This is probably what is pulling the user in through the middleware.
    Themepark.findById(id).populate('user', 'displayName').exec(function(err, themepark) {
        if (err) return next(err);
        if (! themepark) return next(new Error('Failed to load Themepark ' + id));
        req.themepark = themepark ;
        next();
    });
};

/**
 * Themepark authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
    if (req.themepark.user.id !== req.user.id) {
        return res.status(403).send('User is not authorized');
    }
    next();
};

Upvotes: 0

Views: 771

Answers (1)

dreamerkumar
dreamerkumar

Reputation: 1550

You add the middleware in the same manner:

app.route('/themeparks')
.get(themeparks.list)
.post(users.requiresLogin, themeparks.userRoleHasAuthorization, themeparks.create);

In the themeparks module, you add this function, just like the hasAuthorization function and also include a check for user to be in role. You already have the user id from the request.Use it to query the roles that the user has access to from the database.

(Ideally, if the roles are in the database, I would retrieve that when the user object itself is retrieved, so that the front end can also use the role info when they need to and you don't have to query within the authorization module.)

Based on the user id, and the roles information determine if the user is in the role that allows for update to this module. Return a 401 Not Authorized otherwise. Below is how you can structure the code. The canUpdate function needs to be implemented based on how you want to store the roles and info about the modules that should have access to the roles.

exports.userRoleHasAuthorization = function(req, res, next) {
if (req.themepark.user.id !== req.user.id || !canUpdate(req.user.id, 'themeparks') {
    return res.status(403).send('User is not authorized');
}
next();
};

Upvotes: 1

Related Questions