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
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
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
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.
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)
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.