Loopback: Creating Roles and ACL at run time

Created on 9 Feb 2016  路  1Comment  路  Source: strongloop/loopback

In the app we are developing, we allow admins to create Roles that have different, granular permissions (READ on this endpoint, WRITE on that endpoint, etc...). What we are trying to do is create Roles, Users and ACLs at run time through the REST API.

Users works fine. We extend User to user and POST to /api/users

{
  "profile": {
    "firstname": "Bob",
    "lastname": "Bobby"
  },
  "primary": false,
  "username": "bbobb",
  "email": "[email protected]",
  "status": "isActive",
  "password": "#$@#$@#%%"
}

Roles works fine. We extend Role to role and POST to /api/roles

{
  "name": "teammember",
  "description": "Organization Team Member"
}

RoleMapping works fine. We POST to /api/roles/{id}/principles

 {
                "principalType": "USER",
                "principalId": teamMemberUserId
 }

As long as we use the predefined ACLs in the .json file it works without a hitch.
However, teammember is not allowed to GET /api/mymodel

But we want to add a new ACL for the new Role so we POST /api/acls

{
    "model": "mymodel",
    "property": "find",
    "accessType": "EXECUTE",
    "permission": "ALLOW",
    "principalType": "ROLE",
    "principalId": "teammember",
  }

and

  {
    "model": "mymodel",
    "property": "find",
    "accessType": "READ",
    "permission": "ALLOW",
    "principalType": "ROLE",
    "principalId": "teammember",
  }

We login with the user (Bob) and get the access token and we get a 401 for GET /api/mymodel

We have tried

  1. extending ACL to acl and POST (no effect on permissions)
  2. exposing ACL directly and POST (no effect on permissions)
  3. Pushing the ACL to the Model.settings.acls in a boot script

None of it works. Only adding to the mymodel.json file and restart node works.
I rand DEBUG loopback:security:* it doesn't seem to even see the new ACLs.

What are we missing? Is the supposed to work? Are we excepting more out of loopback that it provides?

Thanks

 loopback:security:role isInRole(): $everyone +49s
  loopback:security:access-context ---AccessContext--- +0ms
  loopback:security:access-context principals: +0ms
  loopback:security:access-context principal: {"type":"USER","id":"56ba2464d7cbe21b2e51383b"} +0ms
  loopback:security:access-context modelName mymodel +0ms
  loopback:security:access-context modelId undefined +0ms
  loopback:security:access-context property find +0ms
  loopback:security:access-context method find +0ms
  loopback:security:access-context accessType READ +0ms
  loopback:security:access-context accessToken: +0ms
  loopback:security:access-context   id "TGVnVoQaCvwEcDRwdYSEDnEDQI1bViJIlw7pwsQ4LFIZ8dvDiF4pb3RRrEZWMMG7" +0ms
  loopback:security:access-context   ttl 1209600 +0ms
  loopback:security:access-context getUserId() 56ba2464d7cbe21b2e51383b +0ms
  loopback:security:access-context isAuthenticated() true +0ms
  loopback:security:role Custom resolver found for role $everyone +0ms
  loopback:security:role isInRole(): admin +0ms
  loopback:security:access-context ---AccessContext--- +0ms
  loopback:security:access-context principals: +0ms
  loopback:security:access-context principal: {"type":"USER","id":"56ba2464d7cbe21b2e51383b"} +0ms
  loopback:security:access-context modelName mymodel +0ms
  loopback:security:access-context modelId undefined +0ms
  loopback:security:access-context property find +0ms
  loopback:security:access-context method find +0ms
  loopback:security:access-context accessType READ +0ms
  loopback:security:access-context accessToken: +0ms
  loopback:security:access-context   id "TGVnVoQaCvwEcDRwdYSEDnEDQI1bViJIlw7pwsQ4LFIZ8dvDiF4pb3RRrEZWMMG7" +0ms
  loopback:security:access-context   ttl 1209600 +0ms
  loopback:security:access-context getUserId() 56ba2464d7cbe21b2e51383b +0ms
  loopback:security:access-context isAuthenticated() true +0ms
  loopback:security:role isInRole(): admin +1ms
  loopback:security:access-context ---AccessContext--- +0ms
  loopback:security:access-context principals: +0ms
  loopback:security:access-context principal: {"type":"USER","id":"56ba2464d7cbe21b2e51383b"} +0ms
  loopback:security:access-context modelName mymodel +0ms
  loopback:security:access-context modelId undefined +0ms
  loopback:security:access-context property find +0ms
  loopback:security:access-context method find +0ms
  loopback:security:access-context accessType READ +0ms
  loopback:security:access-context accessToken: +0ms
  loopback:security:access-context   id "TGVnVoQaCvwEcDRwdYSEDnEDQI1bViJIlw7pwsQ4LFIZ8dvDiF4pb3RRrEZWMMG7" +0ms
  loopback:security:access-context   ttl 1209600 +0ms
  loopback:security:access-context getUserId() 56ba2464d7cbe21b2e51383b +0ms
  loopback:security:access-context isAuthenticated() true +0ms
  loopback:security:role isInRole(): teammember +0ms
  loopback:security:access-context ---AccessContext--- +0ms
  loopback:security:access-context principals: +0ms
  loopback:security:access-context principal: {"type":"USER","id":"56ba2464d7cbe21b2e51383b"} +0ms
  loopback:security:access-context modelName mymodel +0ms
  loopback:security:access-context modelId undefined +0ms
  loopback:security:access-context property find +0ms
  loopback:security:access-context method find +0ms
  loopback:security:access-context accessType READ +0ms
  loopback:security:access-context accessToken: +0ms
  loopback:security:access-context   id "TGVnVoQaCvwEcDRwdYSEDnEDQI1bViJIlw7pwsQ4LFIZ8dvDiF4pb3RRrEZWMMG7" +0ms
  loopback:security:access-context   ttl 1209600 +1ms
  loopback:security:access-context getUserId() 56ba2464d7cbe21b2e51383b +0ms
  loopback:security:access-context isAuthenticated() true +0ms
  loopback:security:role isInRole(): teammember +0ms
  loopback:security:access-context ---AccessContext--- +0ms
  loopback:security:access-context principals: +0ms
  loopback:security:access-context principal: {"type":"USER","id":"56ba2464d7cbe21b2e51383b"} +0ms
  loopback:security:access-context modelName mymodel +0ms
  loopback:security:access-context modelId undefined +0ms
  loopback:security:access-context property find +0ms
  loopback:security:access-context method find +0ms
  loopback:security:access-context accessType READ +0ms
  loopback:security:access-context accessToken: +0ms
  loopback:security:access-context   id "TGVnVoQaCvwEcDRwdYSEDnEDQI1bViJIlw7pwsQ4LFIZ8dvDiF4pb3RRrEZWMMG7" +0ms
  loopback:security:access-context   ttl 1209600 +0ms
  loopback:security:access-context getUserId() 56ba2464d7cbe21b2e51383b +0ms
  loopback:security:access-context isAuthenticated() true +0ms
  loopback:security:role Role found: {"id":"56b8db8270b45727672bfd7e","name":"admin","description":"System Administrator","created":"2016-02-08T18:16:34.660Z","modified":"2016-02-08T18:16:34.660Z"} +1ms
  loopback:security:role Role found: {"id":"56b8db8270b45727672bfd7e","name":"admin","description":"System Administrator","created":"2016-02-08T18:16:34.660Z","modified":"2016-02-08T18:16:34.660Z"} +2ms
  loopback:security:role Role found: {"id":"56b8db8270b45727672bfd7f","name":"teammember","description":"Team Member","created":"2016-02-08T18:16:34.672Z","modified":"2016-02-08T18:16:34.672Z"} +1ms
  loopback:security:role Role found: {"id":"56b8db8270b45727672bfd7f","name":"teammember","description":"Team Member","created":"2016-02-08T18:16:34.672Z","modified":"2016-02-08T18:16:34.672Z"} +2ms
  loopback:security:role Role mapping found: null +1ms
  loopback:security:role isInRole() returns: false +0ms
  loopback:security:role Role mapping found: null +0ms
  loopback:security:role isInRole() returns: false +0ms
  loopback:security:role Role mapping found: null +0ms
  loopback:security:role isInRole() returns: false +0ms
  loopback:security:role Role mapping found: null +0ms
  loopback:security:role isInRole() returns: false +0ms
  loopback:security:acl The following ACLs were searched:  +1ms
  loopback:security:acl ---ACL--- +0ms
  loopback:security:acl model mymodel +0ms
  loopback:security:acl property * +0ms
  loopback:security:acl principalType ROLE +0ms
  loopback:security:acl principalId $everyone +0ms
  loopback:security:acl accessType * +0ms
  loopback:security:acl permission DENY +0ms
  loopback:security:acl with score: +0ms 7495
  loopback:security:acl ---Resolved--- +0ms
  loopback:security:access-context ---AccessRequest--- +0ms
  loopback:security:access-context  model mymodel +0ms
  loopback:security:access-context  property find +0ms
  loopback:security:access-context  accessType READ +0ms
  loopback:security:access-context  permission DENY +0ms
  loopback:security:access-context  isWildcard() false +0ms
  loopback:security:access-context  isAllowed() false +0ms

Most helpful comment

I solved my own problem so you can close it if you like. But here is the answer.

It isn't enough to create ACLs you have to attach them to to the models themselves using
MyModel.settings.acls.push(...)

So I created remoteMethods in myModel to handle adding, removing, and listing ACLs for that model.
(I simplified the error handling for clarity)

  MyModel.aclsPost = function (acl, context, cb) {
        MyModel.settings.acls.push(acl);
        cb(null, {});
    }

    MyModel.remoteMethod(
        'aclsPost',
        {
            accepts: [
                {arg: 'acl', type: 'Object', http: {source: 'body'}},
                {arg: 'context', type: 'object', http: {source: 'context'}}
            ],
            http: {path: '/acls', verb: 'post', status: 201, errorStatus: 404}
        }
    );

After adding this I was able to dynamically create ACLs for the model using the REST interface and the new permissions pass all testing.

Make sure you lock down the /api/model/acls endpoints at the highest security level, but there it is if someone can use it.

>All comments

I solved my own problem so you can close it if you like. But here is the answer.

It isn't enough to create ACLs you have to attach them to to the models themselves using
MyModel.settings.acls.push(...)

So I created remoteMethods in myModel to handle adding, removing, and listing ACLs for that model.
(I simplified the error handling for clarity)

  MyModel.aclsPost = function (acl, context, cb) {
        MyModel.settings.acls.push(acl);
        cb(null, {});
    }

    MyModel.remoteMethod(
        'aclsPost',
        {
            accepts: [
                {arg: 'acl', type: 'Object', http: {source: 'body'}},
                {arg: 'context', type: 'object', http: {source: 'context'}}
            ],
            http: {path: '/acls', verb: 'post', status: 201, errorStatus: 404}
        }
    );

After adding this I was able to dynamically create ACLs for the model using the REST interface and the new permissions pass all testing.

Make sure you lock down the /api/model/acls endpoints at the highest security level, but there it is if someone can use it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ImanMh picture ImanMh  路  4Comments

bajtos picture bajtos  路  4Comments

devotox picture devotox  路  4Comments

nmklong picture nmklong  路  3Comments

Overdrivr picture Overdrivr  路  3Comments