2
votes

Currently testing a bunch of frameworks in order to determine a good candidate for future uses in my company, LoopBack caught my attention by being almost perfect for my needs.

However, I get the feeling that their ACL model is quite limited in some cases. Let's take the following use case: on a collaborative travels management website, a user can create and/or join public travels. Let's assume the following API:

  • /Travels lists all travels the user has
  • /Travels/public lists all public travels
  • /Travels/{id}/join join the Travel with the given ID

Would building such an API require to re-invent the wheel? Or is it some middlewares to implement?

The same goes for per-field ACL. Suppose you have a few checklist items, some manually added and some others automatically generated. Can you block the WRITE operations only on automatic ones, except for changing the "done" field?

2

2 Answers

6
votes

By default, a request to

GET /Travels

would list every element of the Travel model. If you set proper relations (probably a many to many relationship between users and travels) the proper way to query for a given user travels is

GET /Users/{id}/Travels

But you can customize the default behavior of Travels.find() using hooks, scopes and even overloading the method prototype.

Regarding /Travels/public that's trivial, you just need to create a remote method. Use the path property to customize the endpoint.

Finally, joining a travel with a request to /Travels/{id}/join would be managed with remote methods too, but this should be a POST request.

Loopback is able to manage many to many relations without specifying a relation table, but in your case I'd rather define it. For example

{
"name": "UserTravel",
  "options": { ... },
  "properties": {
    "id":{"type":"Number", "id":1},
    "userId":{"type":"Number"},
    "travelId":{"type":"Number"}
  },
  "relations": {
    "Travel": {
        "type": "belongsTo",
        "model": "Travel",
        "foreignKey": "travelId"
    },
    "User": {
        "type": "belongsTo",
        "model": "User",
        "foreignKey": "userId"
    }
  }
}

having this model at hand would allow you to insert a specific user/travel tuple when calling the join endpoint. You get the travelId from the request params, and the userId from the request accessToken, provider the user is authenticated in your app.

2
votes

As @amenadiel suggested, you can use a hooks to set a default filter by the logged in user:

MyModel.observe('access', function limitToTenant(ctx, next) {
  ctx.query.where.tenantId = loopback.getCurrentContext().tenantId;
  next();
});

Got this from the docs.