Group/rule-based authorization approach in node.js and express.js

后端 未结 6 1481
鱼传尺愫
鱼传尺愫 2021-01-30 00:31

What are good strategies for role-based authorization in express.js? Especially with express-resource?

With Express-resource there are no handlers, so I think there are

相关标签:
6条回答
  • 2021-01-30 00:51

    I have been researching the same question and have come across a few good modules. I have been focusing on the node-acl package that can be found here. https://github.com/optimalbits/node_acl.

    This package seems to have implemented the ACL pattern in a very understandable way and has provided ways to easily integrate it into your node/express application.

    Firstly, you'll want to define your resources, roles, and permissions.

    For example, the resources can be:

    /
      /forums
        /forums/threads
    

    The roles can be

    public
    admin
    user
       john
       jane
    

    In this example, the roles john and jane can map to actual user accounts, but they will inherit all the permissions of the user role.

    The permissions on the resources

    • create
    • show
    • update
    • destroy

    Or your standard CRUD operations.

    Now that those have been defined, we can take a look at how it would look to set up the acl using node-acl. These notes are derived from the documentation

    import the package

    var acl = require('acl');
    

    Set up your backend. My app is using mongodb, but the node-acl package does support other storage mechanisms

    acl = new acl(new acl.mongodbBackend(dbInstance, prefix));
    

    My app is using mongoose so dbInstance would be replaced with mongoose.connection.db

    Now lets add our roles to the ACL. In node-acl, roles are created by giving them permissions. Its like killing two birds with one stone (no birds are actually harmed)

    acl.allow('admin', ['/', '/forum', '/forum/threads'], '*');
    acl.allow('public', ['/', '/forum', '/forum/threads'], 'show');
    acl.allow('user', ['/', '/forum', '/forum/threads'], ['create', 'show']);
    

    Lets assume a new resource is created by john, we will add a new record that allows john to also update and delete that resource.

    acl.allow('john', ['/forum/threads/abc123'], ['update', 'delete']);
    

    My application is also using express, so I will use the routing middleware approach to check routes. In my routing configuration, I would add the line

    In most express configurations, this looks like for the pos

    app.post('/', acl.middleware(), function(req, res, next) {...});
    app.post('/forums', acl.middleware(), function(req, res, next) {...});
    app.post('/forums/:forumId', acl.middleware(), function(req, res, next) {...});
    app.post('/forums/threads', acl.middleware(), function(req, res, next) {...});
    app.post('/forums/threads/:threadId', acl.middleware(), function(req, res, next) {...});
    

    When no parameters are passed, this will check if the role defined in req.userId is allowed to execute the http method on the resource identified but the route.

    In this example the http method is post, but it will do the same thing for each http idenitified in your configuration.

    This raises the question, about the permissions defined earlier. To answer those questions, we would have to change the permissions from

    • create
    • show
    • update
    • destroy

    To the conventional

    • post
    • get
    • put
    • delete

    Although this example shows everything hardcoded, the better practice is to have a management interface for your permissions so they can be created, read, updated, and deleted dynamically without having to modify your code.

    I like the node-acl plugins approach as it allows for very fine grained permission-role assignments using a very straight forward and flexible api. There is a lot more in their documentation, my example shows were I am with the package.

    Hopefully this helps.

    0 讨论(0)
  • 2021-01-30 00:53

    You can try Casbin: https://casbin.org/, it has a Node.js version. It also has a Express.js middleware called express-authz: https://casbin.org/docs/en/middlewares

    0 讨论(0)
  • 2021-01-30 00:56

    Connect-roles is quite good, simple and the documentation is also very clear.

    var user = roles;
    
    app.get('/profile/:id', user.can('edit profile'), function (req, res) {
      req.render('profile-edit', { id: req.params.id }); 
    })
    app.get('/admin', user.is('admin'), function (req, res) {
      res.render('admin');
    }
    
    0 讨论(0)
  • 2021-01-30 00:59

    In express you can add a handler that hooks into every operator (http://expressjs.com/guide.html#passing-route control) where you can do precondition validation. Here you can retrieve the role for the user and restrict access based on the HTTP verb (PUT, DELETE, etc.) or the URL (param('op') is 'edit' or so).

    app.all('/user/:id/:op?', function(req, res, next){
      req.user = users[req.params.id];
      if (req.user) {
        next();
      } else {
        next(new Error('cannot find user ' + req.params.id));
      }
    });
    
    0 讨论(0)
  • 2021-01-30 01:07

    I would say that it's hard to solve this in a clean manner using express-resource, since it doesn't allow for route-specific middleware (at least not in a clean way).

    I would opt for a similar layout as an express-resource module, but route it with plain old express. Something like this:

    // Resource
    var forum = {
      index: // ...
      show: // ...
      create: // ...
      update: // ...
      destroy: // ...
    };
    
    // Middleware
    var requireRole = function(role) {
      return function(req, res, next) {
        if('user' in req.session && req.session.user.role === role)
          next();
        else
          res.send(403);
      }
    };
    
    // Routing
    app.get('/forums', forum.index);
    app.get('/forums/:id', forum.show);
    app.post('/forums', requireRole('moderator'), forum.create); // Only moderators can create forums
    app.delete('/forums/:id', requireRole('admin'), forum.destroy); // Only admins can delete forums
    

    UPDATE: There have been ongoing discussions regarding route-specific middleware in express-resource, e.g. here. The prevailing view seems to be to have an array per action, e.g.:

    var forums = {
      index: [ requireRole('foo'), function(req, res, next) { ... } ]
    };
    

    You could take a look through the pull requests and see if there is anything you could use. I totally understand it, of course, if you don't feel comfortable with that. I'm pretty sure we will see something like this in express-resource in the future.

    The only other solution I can think of is along the lines of Jan Jongboom's answer, which would be to mount the resources with express-resource, but have middleware attached "outside" of that, something like:

    app.delete('*', requireRole('admin')); // Only admins are allowed to delete anything
    app.put('/forums/*', requireRole('moderator')); // Only moderators are allowed to update forums
    

    But I regret that this leaks URLs all over the place.

    0 讨论(0)
  • 2021-01-30 01:09

    I wrote a module as non-explicit routing middleware. Works well with express-routes.

    Gandalf on GitHub

    0 讨论(0)
提交回复
热议问题