Fine-Grained Data Level Permissions in Laravel RESTful API

laravelpermissionsPHPrest

The problem:

We want to restrict access to properties returned from our services.

Details:

We have route level permissions working with no issue.

For a user that can view /product/{id} for example, that user may or may not have permission to view the purchasePrice property. (if they don't have permission it would either be empty or not exist in the JSON response) It seems by default laravel returns the entire record when you load the model.

What we've tried:

We are currently dynamically populating $visible[] based on permissions and we have a list of every field and what permission is required to read/write.

My Question:

Is there a better way to do this? Are we reinventing the wheel or does this seem like a reasonable approach?

Best Answer

Separate your entities from your API response model. This is a case of SRP (Single Responsibility Principle): the job of your entities is to make DB access convenient. Your are adding two more responsibilities to that, violating the SRP:

  • enforcing permissions
  • Serve as a public interface

That means that there are two parts to this answer.

having a public API

A public API must be stable and backwards compatible unless stated to your users in the most explicit way possible. That means any change in your codebase that changes the public API should be carefully checked for backwards compatibility before releasing it to production. With your current approach, you'll have a very hard time doing so: say you want to rename a property of your entity. Fine, you did so, your IDE caught all the usages and renamed them. But what about other code that uses your API? It will break because it is still looking for the old name. What you really want here is that you can change your entities and not affect the public API as long as all the information is still there. The solution: have separate classes for the API. For each Entity, have another class that (right now) mirrors the Entity. When receiving input from the API, convert the JSON into these intermediate instances. Do validation and stuff and only then cop data to your entities. When giving data out, first convert your entities to those intermediate classes and then convert those to JSON. Yes, that means more work. But it gives you flexibilities that you will 100% definitely need as soon as you have published your API. That work is now easier done than 30 features later.

enforcing permissions

You can now decide whether you want to include this responsibility in the API representation classes or whether you want it separately. You could also include this in the process of copying data from the entity to the representation class. It really depends on your codebase, team and preference. Just make sure that, wherever you place that code, the SRP is not violated.

performance

As has been said, you can go as far as to not even fetch fields from the DB that are not visible due to permissions. For regular fields the performance impact should be ignorable (except when we're talking about BLOB fields with several KB). But where this really matters is with related entities. If you can avoid three joins because they won't be used anyways, that's a definitive and huge speedup. See iipavlov's answer on that issue.