AlainD
AlainD

Reputation: 6356

Role based data access

Is it possible and how to filter the data based on the role ?

For example, I have an app with entities Customer and Salesrep. Each customer is assigned exactly one Salesrep. I need a Salesrep with role "sales" to see/edit only the customers assigned to her, but her boss (which has role "supervisor") to list but not edit all the customers assigned to one of his employee.

In SQL, I would add WHERE role="sales" AND employee.name=user.name OR role="supervisor" AND employee.group=user.group to each query.

Upvotes: 0

Views: 44

Answers (1)

Yabada
Yabada

Reputation: 1758

First of all, you can define the sales role to have the read/write rights (see/edit), and role supervisor to have read right (not write).

That being said, the answer is no, you can't filter the customers list only using roles. Roles only give you basic CRUD action control over entities.

To do so, you're gonna need some custom dev on the route of your customer entity controller /customer/datalist.

How it works

By default, this route look like this :

router.post('/datalist', block_access.isLoggedIn, block_access.actionAccessMiddleware("customer", "read"), function(req, res) {
    filterDataTable("E_customer", req.body).then(function(data) {
        res.send(data).end();
    }).catch(function(err) {
        logger.debug(err);
        res.end();
    });
});

It is called using ajax from the list view of customer entity.

The filterDataTable function handles the filtering/ordering/pagination of your datalist. It takes two optional parameters: speInclude and speWhere that allows you to customize the sequelize ORM query that will be generated (see Sequelize where and Sequelize include).

Solutions

Depending on how your relation/foreign keys are defined, you can choose between these solutions. It's up to you to use the best one for your situation :

Using the speWhere parameter :

router.post('/datalist', block_access.isLoggedIn, block_access.actionAccessMiddleware("customer", "read"), function(req, res) {
    var user = req.session.passport.user;
    var speWhere;
    if (user.r_role.f_label == 'sales')
        speWhere = {f_id_salesrep: user.id};

    filterDataTable("E_customer", req.body, null, speWhere).then(function(data) {
        res.send(data).end();
    }).catch(function(err) {
        logger.debug(err);
        res.end();
    });
});

With the speWhere parameter, only customers associated with your salesrep will be fetched from database.

Using the speInclude parameter :

You can change the entity on which you do the query (salesrep), and ask to include every customers associated with this entity.

router.post('/datalist', block_access.isLoggedIn, block_access.actionAccessMiddleware("customer", "read"), function(req, res) {
    var user = req.session.passport.user;
    var speInclude;var targetEntity = 'E_customer';
    if (user.r_role.f_label == 'sales') {
        targetEntity = 'E_salesrep';
        speInclude = [
            model: models.E_customer,
            as: 'r_customer'
        ];
    }

    filterDataTable(targetEntity, req.body, speInclude).then(function(data) {
        if (speInclude)
            data.data = data.data.r_customer; // Set the salesrep's customers to the returned data
        res.send(data).end();
    }).catch(function(err) {
        logger.debug(err);
        res.end();
    });
});

With this solution, you'll query the salesrep model instead of the customer model, and get an array of customers associated to your salesrep.

Filtering the query's result

router.post('/datalist', block_access.isLoggedIn, block_access.actionAccessMiddleware("customer", "read"), function(req, res) {
    filterDataTable("E_customer", req.body).then(function(data) {
        var filteredCustomers = [];
        var user = req.session.passport.user;
        for (var i = 0; i < data.data.length; i++)
            if (data.data[i].f_id_salesrep == user.id)
                filteredCustomers.push(data.data[i]);
        data.data = filteredCustomers;
        res.send(data).end();
    }).catch(function(err) {
        logger.debug(err);
        res.end();
    });
});

This method should be used only as a last solution since you will need to redefine the pagination.

These pieces of code haven't been tested and might need some adjustements. They only show what is possible and how to proceed.

Upvotes: 1

Related Questions