Including/Excluding model attributes based on user permissions


#1

I’m implementing the json-api spec for an internal application API with a pretty granular rights/permissions system and my team has been debating various way to handle the case when some users don’t have access to read certain fields from an endpoint.

Here’s a quick example:

  • A store has a name, net revenue, and employees.
  • An employee of the store can see the name and other employees, but not the net revenue.
  • A manager of the store can see name, employees, and net revenue (via a read_store_net_revenue permission)

When an employee accesses the /api/stores/1 endpoint, we don’t want to disclose the value of the net revenue (and honestly, probably don’t even want to expose the presence of that field). We weren’t able to identify anything in the spec addressing the delivery of conditional attributes, so wanted see if we either a) missed it, or b) its purposely not defined and thus curious if anyone has implemented something similar.

After some debate, we’ve arrived at two approaches:

  1. Simply don’t return the net revenue field inside of the attributes when employees without the read_net_revenue permission requests api/stores/1. The con here is that knowing what is and isn’t restricted is somewhat of a mystery in the schema without adding additional information not defined in the spec.
  2. Make the inclusion of the net revenue field always opt-in, e.g. /api/stores/1?include=net_revenue. We’d use include and treat restricted attributes as relations to leverage the existing work that’s happening there, to raise a proper error messages if a field was requested to which the user doesn’t have access, and to make it explicit that the requested attribute isn’t always available. The major con here is endpoint bloat, as we could end up with 10-15+ end points per model (eg store, store-net-revenue, store-net-profit, etc…).

Thanks for your help!


#2

I can’t find anything in the spec that says either of those is not allowed. If you take the latter route, remember that a future version of the spec may define an include parameter as well. It also may be more difficult to get client-side libraries to conform with your extra requirements. I would personally go with the first option. If employees are not supposed to see certain fields, then their client shouldn’t be expecting them anyway.

Since your permissions are very granular, a possible implementation is to loop through all of the resources and remove attributes that the client shouldn’t see, right before the document is serialized. I find that easier than figuring out which attributes to include when they are being read.


#3

The spec already defines an include parameter, for controlling which relationships to embed.

There is a general rule for picking your own parameters that will not conflict with future versions of the spec: they must contain at least one character that is not a lower case letter (a-z). The recommendation is to include a minus, underscore, or upper-case letter.