Loopback provides great "class-level" ACL's for restricting access to a whole Model or its mehods, but greatly lacks the ability to restric access to individual objects. This project tries to solve this, by setting object-level ACL's on each object, and manipulates Loopback's Query to only return objects the requesting user has access to.
Lets say we want a Book-object only to be readable (pun intended) by 3 users (id: "aaa", id: "bbb" and id: "ccc"):
POST /api/books
{
"title": "Clean Code",
"subtitle": "A Handbook of Agile Software Craftsmanship",
"_acl":{
"r_perm": {
"users":["aaa", "bbb", "ccc"]
}
}
}
The mixin will parse the object to be stored as in Mongo:
{
id: ObjectId("123"),
title: "Clean Code",
subtitle: "A Handbook of Agile Software Craftsmanship",
r: {
u: ["aaa", "bbb", "ccc"]
g: []
},
w: {
u: [],
g: []
}
}
This object can now only be accessed by a user with an id of "aaa", "bbb" or "ccc" and no one else. When retrieving, the mixin will parse the object's ACL right back again:
GET /api/books/123
authorization: accessToken-aaa
returns:
{
"id": "123"
"title": "Clean Code",
"subtitle": "A Handbook of Agile Software Craftsmanship",
"_acl":{
"r_perm": {
"users":["aaa", "bbb", "ccc"]
}
}
}
Whereas requesting without permissions leads to:
GET /api/books/123
authorization: accessToken-ddd
returns:
404 Not found
To specifiy every user that will have access to the object can be cumbersome and timeconsuming. This is where groups come in handy.
POST /api/books
{
"title": "Clean Code",
"subtitle": "A Handbook of Agile Software Craftsmanship",
"_acl":{
"r_perm": {
"groups":["group-id-1"]
}
}
}
As you've might guessed, this object is now accessible by users who has group-id-1
specified in acl_groups
on the User object.
If user-id-1
and user-id-2
is not in group-id-1
then these users can have explicit access this way:
POST /api/books
{
"title": "Clean Code",
"subtitle": "A Handbook of Agile Software Craftsmanship",
"_acl":{
"r_perm": {
"groups":["group-id-1"],
"users":["user-id-1", "user-id-2"]
}
}
}
If you have installed the mixin on your model but you dont specify $acl
on creation of a new object, the objects visibility will be public, ex:
POST /api/books
{
"title": "Clean Code",
"subtitle": "A Handbook of Agile Software Craftsmanship"
}
returns
{
"title": "Clean Code",
"subtitle": "A Handbook of Agile Software Craftsmanship",
"_acl":{
"r_perm": {
"groups":["*"],
"users":["*"]
},
"w_perm": {
"groups":["*"],
"users":["*"]
}
}
}
npm install --save loopback-object-acl
In model-config.json
add ../node_modules/loopback-object-acl
to mixins
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"../common/models",
"./models"
],
"mixins": [
"loopback/common/mixins",
"loopback/server/mixins",
"../common/mixins",
"./mixins",
"../node_modules/loopback-object-acl"
]
}
Set ObjectAclController
on what ever model you would like to protect with Object-level ACL:
book.json
{
"name": "Book",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"mixins": {
"ObjectAclController": {}
}
...
}
This mixins expects a currentUser
object on the options
object. This is not default Loopback v3.x behavior, and must be implemented before usage.
Implementation can found here: http://loopback.io//doc/en/lb3/Using-current-context.html#use-a-custom-strong-remoting-phase
This mixin is only tested with Loopback v3.X and using MongoDB as DataSource
- Do only return objects from database that the requesting user has access to.
Version 2.0
- Set ACL on object-creation
- Set permissions on user creation
- ObjectAcl.js
- CurrentUserUtil.js