fullcube / loopback-component-access-groups

Access controls for Loopback.
59 stars 21 forks source link

Bypass all access control for certain users #15

Open bostondv opened 8 years ago

bostondv commented 8 years ago

Hi there, is there a way to bypass group access control for specific user(s) so they could access all content?

I've considered several ideas such as filtering all requests for particular users but haven't been able to get a working solution.

Any help would be appreciated!

bostondv commented 8 years ago

FWIW I found a way but suspect it could be done better, especially the 2nd part below which feels like it would be best to prevent the query from being changed in the first place for admin users.

1) Registered a role resolver with model ACL's to allow users with a truthy "admin" property on their user model.

2) Observe access to all models and if admin user, remove any "where" queries that are added to the context by this component.

module.exports = function observeAdminQueries(server) {
  const settings = server.settings['loopback-component-access-groups'] || {};
  const foreignKey = settings.foreignKey || 'groupId';
  const groupModelName = settings.groupModel || 'Group';

  function isGroupModel(modelClass) {
    if (modelClass) {
      const groupModel = server.models[groupModelName];

      return modelClass === groupModel ||
        modelClass.prototype instanceof groupModel ||
        modelClass === groupModel;
    }
    return false;
  }

  Object.keys(server.models).forEach(modelName => {
    const Model = server.models[modelName];

    if (typeof Model.observe === 'function') {
      debug('Attaching access observer to %s', modelName);
      Model.observe('access', (ctx, next) => {
        const currentCtx = server.loopback.getCurrentContext();
        const currentUser = currentCtx && currentCtx.get('currentUser') || null;
        const Model = ctx.Model;
        const key = isGroupModel(Model) ? Model.getIdName() : foreignKey;

        if (currentUser && currentUser.admin) {
          debug('Current user is admin, clear group access query params');
          if (ctx.query.where && ctx.query.where[key]) {
            debug('original query: %o', JSON.stringify(ctx.query, null, 4));
            delete ctx.query.where[key];
            debug('modified query: %s', JSON.stringify(ctx.query, null, 4));
          }
        }

        return next();
      });
    }
  });
};
Undrium commented 7 years ago

Thanks, I'm using this aswell. Feels like there should be a better way to get around this.

Undrium commented 7 years ago

To elaborate some more on this hack, the query should only be modified if the "inq" is present and it contains an empty array. I'm sure there is more cases I've missed.

Quickfix:

if (ctx.query.where && ctx.query.where[key] && ctx.query.where[key].inq && ctx.query.where[key].inq.length == 0) {
Undrium commented 7 years ago

I do think the best solution for this would be to respect the ACL, if it does look like this:

{
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    },
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "admin",
      "permission": "ALLOW"
    },
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$group:member",
      "permission": "ALLOW"
    }

Then definately users with the admin role should still have all access. The hack provided in this thread doesn't work on all cases.