The acl document

instructions

Q: What does this tool do

A: Users have different permissions, such as administrator, VIP and ordinary user. Each user has A different page for accessing API

Nodejs has two well-known permission management modules, one is ACL and the other is RBAC. After comprehensive comparison, ACL was selected in the project

Method of use

  1. Set up the configuration file
  2. Users are assigned corresponding permissions after login
  3. Use ACLs to check the control areas

The configuration file

const Acl = require('acl');
const aclConfig = require('.. /conf/acl_conf');

module.exports = function (app, express) {
    const acl = new Acl(new Acl.memoryBackend()); // eslint-disable-line

    acl.allow(aclConfig);

    return acl;
};

// acl_conf

module.exports = [
    {
        roles: 'normal', // Allows: [{resources: ['/admin/reserve'], permissions: ['get'] },
        ]
    },
    {
        roles: 'member', // Allows members: [{resources: ['/admin/reserve'.'/admin/sign'], permissions: ['get'] },
            { resources: ['/admin/reserve/add-visitor'.'/admin/reserve/add-visitor-excel'.'/admin/reserve/audit'.'/admin/sign/ban'], permissions: ['post'] },
        ]
    },
    {
        roles: 'admin', // allows management: [{resources: ['/admin/reserve'.'/admin/sign'.'/admin/set'], permissions: ['get'] },
            { resources: ['/admin/set/add-user'.'/admin/set/modify-user'], permissions: ['post'] },
        ]
    },
    {
        roles: 'root', // Allows: [{resources: ['/admin/reserve'.'/admin/sign'.'/admin/set'], permissions: ['get']},]}];Copy the code

check

Here is a combination of express do check… It turned out that the middleware provided by acl itself was too weak, so I rewrote one here.

function auth() {
        return async function (req, res, next) {
            let resource = req.baseUrl;
            if(req.route) {// If the route attribute is used in control but app.use, there is no resource = resource + req.route. } console.log('resource', resource); // if /admin/sign/ is accessed, the/symbol is considered tooif (resource[resource.length - 1] === '/') {
                resource = resource.slice(0, -1);
            }

            let role = await acl.hasRole(req.session.userName, 'root');

            if (role) {
                return next();
            }

            let result = await acl.isAllowed(req.session.userName, resource, req.method.toLowerCase());
            // if(! result) { //let err = {
            //         errorCode: 401,
            //         message: 'User unauthorized access'/ /}; //return res.status(401).send(err.message);
            // }
            next();
        };
    }
Copy the code

Express. Router supports exporting a Router module to app.use, but if you use app.use(‘/admin/user’,auth(), userRoute); Req. route is not available in auth. Because acLs are strong matches for access permissions, they need to be fault-tolerant

Assign login permissions

Result is the user information queried by the database or returned by the background API. The switch here can be in the form of configuration file. Since I only have three permissions for this project, I simply write it here.

let roleName = 'normal';

    switch (result.result.privilege) {
        case 0:
            roleName = 'admin';
            break;
        case 1:
            roleName = 'normal';
            break;
        case 2:
            roleName = 'member';
            break;
    }

    if (result.result.name === 'Nathan') {
        roleName = 'root';
    }

    req.session['role'] = roleName;
    // req.session['role'] = 'root';   // test
    acl.addUserRoles(result.result.name, roleName);
    // acl.addUserRoles(result.result.name, 'root'); // test
Copy the code

Render logic control in puG pages

In express+pug, app.locals. Auth = async function(){} is not available for pug rendering because pug is synchronous. How do I control whether the current page or the current button user has the permission to display it? Here are some common practices

  1. The user has a routing table and component table when he logs in and then renders against this table when he renders
  2. Where permission control is required, use functions to determine whether a user has permission to access

2. Because it is convenient, but the problem is that Express + PUG does not support asynchronous writing, while ACL provides all asynchronous writing. Due to time reasons, I did not go into the judgment inside, but adopted a judgment method with high coupling but relatively convenient.

app.locals.hasRole = function (userRole, path, method = 'get') {

    if (userRole === 'root') {
        return true;
    }

    const current = aclConf.find((n) => {
        return n['roles'] === userRole;
    });

    let isFind = false;
    for (leti of current.allows) { const currentPath = i.resources; IsFind = currentPath.includes(path);if(isFind) {// If the path is found and the method is also on the path then passif (i.permissions.includes(method)) {
                break; } // If method does not match the path, continue to search.continue; }}return isFind;
};
Copy the code

The above code page is relatively simple to iterate through the acl_conf to see if the user has permissions for the current page or button because the acl_conf is already written to memory when it is loaded, so the performance cost is not particularly high. Take the following example.

if hasRole(user.role, '/admin/reserve/audit'.'post').col.l3. Right-align a.waaves -effect.waves-light.btn.margin-right.blue.font12.js-reviewe-ok Agreed Aleem walji aves - effect. The waves - light. The BTN. Pink. Accent - 3. Font12. Js - reviewe - no rejectionCopy the code

At the end

The ACL component can be used to quickly build a user permission management module. If you use removeAllow to dynamically change the user’s permission table, then the hasRole function is cumbersome. So there are several solutions in this case

  1. Start with acl source code
  2. Prepare the data every time you render
const hasBtn1Role = hasRole(user.role, '/xxx'.'get');
res.render('a.pug',{hasBtn1Role})
Copy the code