Permissions on a rest API implementing HATEOAS
Asked Answered
G

3

6

I'm trying to figure out the right way to handle permissions in a single page app that talks directly to several RESTful APIs, that implement HATEOAS.

As an example:

"As a user of my application I can view, start and pause jobs but not stop them."

The underlying rest API has the following resource:

/jobs/{id} Which accepts GET and PUT. The GET returns a job model and the PUT accepts a job model as a request body in the form:

{
 "_links" : {
     "self" : "/jobs/12345678"
 }
 "id" : 12345678,
 "description" : "foo job",
 "state" : "STOPPED"
}

Accepted job states can be: dormant | running | paused | stopped.

The requirement says that on the UI I must have the buttons:

START, PAUSE, STOP

... and only display based on the logged in user's permissions.

From the API perspective everything works as the underlying logic on the server makes sure that the user cannot update the state to a STOPPED state when a request is made (a 401 is returned maybe).

What is the best way to inform the app / UI of the user's permissions, so it can hide any buttons that the user has no permission to action?

Should the API provide a list of permissions, maybe something like :

{
 "_links" : {
     "self" : "/permissions",
     "jobs" : "/jobs"
 }
 "permissions" : { 
     "job" : ["UPDATE", "DELETE"], 
     "job-updates" : ["START", "PAUSE"] 
  }
}

OR should the API change so that the permissions are reflected in the HATEOS links maybe something like :

{
 "_links" : {
     "self" : "/jobs/12345678",
     "start" : "/jobs/12345678/state?to=RUNNING", 
     "pause" : "/jobs/12345678/state?to=PAUSED", 
 }
 "id" : 12345678,
 "description" : "foo job",
 "state" : "DORMANT"
}

Or should it be done in a completely different way?

UPDATE

I've found the following article which suggests an answer: https://softwareengineering.stackexchange.com/questions/215975/how-to-handle-fine-grained-field-based-acl-permissions-in-a-restful-service

Guipure answered 9/7, 2014 at 23:55 Comment(0)
D
6

I would go with the latter: Imply permissions based on which links are present.

If the link isn't there, the user can't access the resource/perform the action. If it is, they can. That's what I'd do, because it's simple and clean and leaves little to the discretion of the front-end code. Decoupling, yo.

Alternatively, if you do want to include all the links in each response but explicitly specify which are allowed and which aren't, if you use a format such as HAL to write your links, you could extend it with a flag on each link like so:

{
    "_links" : {
        "self" : {
            "href":"/jobs/12345678",
            "allowed":false
        },
        "start" : {
            "href":"/jobs/12345678/state?to=RUNNING",
            "allowed":false
        },
        "pause" : {
            "href":"/jobs/12345678/state?to=PAUSED",
            "allowed":false
        }
    },
    "id" : 12345678,
    "description" : "foo job",
    "state" : "DORMANT"
}
Discalced answered 10/7, 2014 at 14:5 Comment(2)
not a fan of the flag, but def agree with answer. Giving links to indicate available actions is one of the key benefits of HATEOAS. Just like on a web page, there's no delete button if i cannot delete an entrySundstrom
I am not a fan of the flag either. I think that in most cases, the flag is not appropriate, but I can see why in some cases it might be possible. Sometimes an action is not possible because of a processing state, for example. However, there are other ways to indicate this kind of thing while including the link (such as an attribute in the body), and returning the appropriate status code to any requests if the link is actually used.Discalced
A
2

I would go with the latter. The reason I don't like the former is because you are creating extra work for the client by requiring it to figure out the mapping between permissions and the resources they permit access to. If you use hateoas and check for the presence of relation types, this mapping is done for you by the server. It also means the uris can change without breaking the client.

I recently wrote a blog post on this area:

https://www.opencredo.com/2015/08/12/designing-rest-api-fine-grained-resources-hateoas-hal/

Arrangement answered 13/8, 2015 at 19:14 Comment(1)
How do you define in that approach the allowed operation e.g. PUT etc?Hightension
B
0

You should be using forms, not links, to provide state transition hypermedia. If you cannot provide forms in your media type, provide links to URIs which use another media type that supports forms, such as XHTML.

IANA has link relations for create-form, edit-form and delete-form for this purpose.

Also, please do not use start and pause as real link relations. If you define them yourself, they must be URIs (preferably HTTP URLs, but any URI under your control will suffice). start has a completely different meaning to what you're using it for, and pause is not defined.

Backbencher answered 21/2, 2017 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.