Can you elaborate your use cases?
I have cars collection. Each car in this collection has a custom authorization regarding users and groups(roles). For example I have instance of car with following permissions:
users 1,2,3 are allowed to read this instance (find...)
user 1 allowed to write to this instance (update...)
role 10,12 allowed to read
role 10 allowed to write
this set of custom permissions is per row/model instance.
Maybe I can build upon this example with a similar request that I'm searching for:
Imagine a model called "cars", with a datasource using Mongo. The model has a property called dealership (which could be a relationship to another model). For example:
{
"id" : "1234",
"model" : "Ford",
"make" : "Escape",
"dealership" : "NYC"
}
{
"id" : "2345",
"make" : "Honda",
"model" : "Odyssey",
"dealership" : "Boston"
}
When Bob (a dealer in New York) makes a call to GET /api/cars he only gets returned the Ford.
Joe (a dealer in Boston) makes the same GET call, but gets the Honda back.
Susan (the area manager for the East Coast) makes the same authenticated call, but gets both vehicles returned.
Does this help? Possible?
Loopback has that and it's covered pretty extensively http://docs.strongloop.com/display/public/LB/Controlling+data+access
You can use the $owner role resolver which is provided by Loopback.
Regarding @simonguest request, it's not going to work. What you can do is to add a relation Dealer hasMany Car and just use /api/dealer/:id/cars.
And don't forget to disable permissions on the base /api/cars (or leave that only for Admin)
Thanks, Federico. I had to refactor some Angular code (it was calling Cars directly), but this works. To complete the loop here, is there any way of writing middleware that would intercept any call to /api/dealer/* ? Then I could check whether the user matches the dealer (in some custom code).
Yes, you can add a operation hook which adds a filter with the current logged in user.
Something like this (untested code based on snippets found online - and with looopback instability, they're probably not working):
Car.observe('access', function(ctx, next) {
ctx.query.filter = { userId: ctx.get('accessToken').userId };
next();
});
But I don't think it's the loopback way to solve this task.
Thanks again, Federico. I had to mod slightly, but it seems to work well. For reference, here is the code:
Car.observe('access', function (ctx, next) {
var lb = loopback.getCurrentContext();
if (lb.get('accessToken')) {
ctx.query.where = { dealers: lb.get('accessToken').userId.toString() };
}
next();
});
@framp $owner it only assigns the creator to a row - this doesn't solve my problem of assigned multiple users/groups to a row. I would also like to know if there is a scalable solution, since too many relations may hurt performance.
There is nothing for hasMany relationships unfortunately, but you can write your own role resolver: https://github.com/strongloop/loopback-example-access-control/blob/master/server/boot/role-resolver.js
Thanks, I checked this one too.
At this time I'm using an authorization column per each row.
So every car has it's own auth document (read/write/user/group) data on it. I embed the authorization in the mongo query.
Interesting. So you have information regarding all roles inside each row?
I personally went the role resolver route (I need to check for organizations and dynamic roles inside each organization).
I have a json that looks like this, on each row:
{
"roles": {
"read": [roleId1, roleId2],
"write": [roleId1]
},
"users": {
"read": [userId1, userId2],
"write": [userId1]
}
}
Most helpful comment
Thanks again, Federico. I had to mod slightly, but it seems to work well. For reference, here is the code: