Has anyone found a good way to model permissions on a resource in JSON API?
I want to describe a Campaign resource that a User can or cannot take action on, such as applying to it.
Note that in our JSON API implementation, we’ve chosen to make all resources always include all of their attributes as a standard representation of that resource.
I can see a few different approaches: meta properties, related resource, other resource+user resource, and response header Allow values.
Meta Properties
This approach uses the meta member to describe the permissions the authenticated user has on the returned Campaign resource. The description for meta in the spec says that it can be used to include non-standard meta-information. These permission properties are not part of the standard representation of the resource, in my opinion. So, it makes sense that they could go in meta.
However, we then have to invent a permission structure that lives in meta. One approach may be the following:
GET /campaigns/1
{
"id": 1,
"type": "campaigns",
"attributes": {
// ...
},
"meta": {
"permissions": {
"apply": true
}
}
}
The client would then look in meta.permissions to determine if the current user could or could not take specific actions.
Related Resource
This approach creates a related resource to describe the permissions the authenticated user has on the returned Campaign resource. This other resource wouldn’t be something that could be directly CRUDed and would need some fake id property. Much like the meta approach, this provides a separate place to contain the non-standard information about a Campaign.
GET /campaigns/1
{
"id": 1,
"type": "campaigns",
"attributes": {
// ...
},
"relationships": {
"campaign-user-permission": {
"data": {
"type": "campaign-user-permissions",
"id": 2
}
}
},
"included": [
{
"type": "campaign-user-permissions",
"id": 2,
"attributes": {
"canApply": true
}
}
]
}
This approach doesn’t seem to buy us much over the meta approach and has several problems.
Resource+User Resource
Maybe what we need is just a new resource that represents the combinations of a Campaign and the User requesting it. That could look like:
GET /user-campaigns/1
{
"id": 1,
"type": "user-campaigns",
"attributes": {
// ...
canApply: true
}
}
This approach puts the permissions in attributes of a different resource. It’s not the same resource as Campaign and therefore can have a different standard representation.
This feels a bit messy, though. Are we going to CRUD this resource? Possibly. If we want to say that a specific user can see a specific campaign, we may want to create a UserCampaign resource for that.
Response Header Allow
This approach uses the Allow response header (from the HTTP spec) to describe the permissions the authenticated user has on the returned Campaign resource. It can only list HTTP Method names, but the HTTP spec mentions that you can use non-standard method names in here.
GET /campaigns/1
Allow: APPLY
{
"id": 1,
"type": "campaigns",
"attributes": {
// ...
}
}
This approach isn’t as flexible as others. It has to apply to the entire response. If there are ever degrees of parmission on an action, it would be hard to describe those with this method.