Search code examples
node.jsloopbackjsstronglooploopback

How to use checkAccessForContext in Loopback


I have

common/models/list.json

...
  "acls": [
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    },
    {
      "accessType": "READ",
      "principalType": "ROLE",
      "principalId": "$authenticated",
      "permission": "ALLOW",
      "property": "find"
    },
    {
      "principalType": "ROLE",
      "principalId": "$authenticated",
      "permission": "ALLOW",
      "property": "create"
    },
    {
      "accessType": "*",
      "principalType": "ROLE",
      "principalId": "$owner",
      "permission": "ALLOW"
    }
  ],
...

When I do GET /lists/{id} with id 1 (my token is owner for this list) I've got my List with 200 response. It's ok.

But, when I make a call in app

app.models.ACL.checkAccessForContext({
              principals: [{
                type: 'ROLE',
                id: '$owner'
              }],
              model: 'List',
              id: 1,
              property: '*',
              accessType: 'READ'
            }, (error, request) => {
              console.log(request);
            });

I've got request.permission === 'DENY'. Why it's happened? Am I passing correct principals?

Thanks for any help.


Solution

  • Your code is fine. This is a bug(#2153) in loopback. I have made a pull request for it. For now you could monkey-patch the method till the bugfix is merged:

    const { AccessRequest } = require('loopback/lib/access-context');
    
    ACL.resolvePermission = function resolvePermission(acls, req) {
        if (!(req instanceof AccessRequest)) {
          req = new AccessRequest(req);
        }
        // Sort by the matching score in descending order
        acls = acls.sort(function(rule1, rule2) {
          return ACL.getMatchingScore(rule2, req) - ACL.getMatchingScore(rule1, req);
        });
        var permission = ACL.DEFAULT;
        var score = 0;
    
        for (var i = 0; i < acls.length; i++) {
          var candidate = acls[i];
          score = ACL.getMatchingScore(candidate, req);
          if (score < 0) {
            // the highest scored ACL did not match
            break;
          }
          if (!req.isWildcard()) {
            // We should stop from the first match for non-wildcard
            permission = candidate.permission;
            break;
          } else {
            if (req.exactlyMatches(candidate)) {
              permission = candidate.permission;
              break;
            }
            // For wildcard match, find the strongest permission
            var candidateOrder = AccessContext.permissionOrder[candidate.permission];
            var permissionOrder = AccessContext.permissionOrder[permission];
            if (candidateOrder > permissionOrder) {
              permission = candidate.permission;
               //@issuehere
              break; //This is the fix
            }
          }
        }
    
        if (debug.enabled) {
          debug('The following ACLs were searched: ');
          acls.forEach(function(acl) {
            acl.debug();
            debug('with score:', acl.score(req));
          });
        }
    
        var res = new AccessRequest(req.model, req.property, req.accessType,
            permission || ACL.DEFAULT);
        return res;
      };