Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST GET endpoints returning different models based on user role

I'm currently dealing few endpoints returning different kinds of models based on permissions:

For example, our business object is getting converted to either an object of type Model or AdvancedModel.

public class Model
{
  public int Property1 {get; set;}
}

public class AdvancedModel : Model
{
  public int Property2 {get; set;}
}

public IActionResult Get()
{    
   (...)
   return User.IsAdmin 
      ? Mapper.Map<AdvancedModel>(Client);
      : Mapper.Map<Model>(Client);
}

Initially this was built so everyone could retrieve client's basic details (name...) BUT only admin could get access to "sensitive" information (preferred payment info, billing contacts).

This makes makes our API harder to understand as we need to identity what fields are returned based on the permission level... This works well but I'm afraid it will become funky as we scale up (new roles, endpoints).

We have considered introducing new endpoints but it will add a bunch of them to our already packed API.

/api/v1/admin/clients/1234
vs
/api/v1/clients/1234

I was just wondering what's the best practice to handle that kind of scenario?

Thx

like image 752
Seb Avatar asked Jul 17 '18 13:07

Seb


2 Answers

I've found that it can become confusing for a REST api to return different things depending on who's accessing the resource. This becomes even more complicated when you're accepting changes with PUT.

I don't think this a general advice, because different situations might require different solutions, but in your specific case I think it makes more sense to just have different resources.

It's fine for a 'piece of data' to be represented on more than 1 point in the API, however, another thing you could do is consider the following:

/clients/1234 <- could contain all the data
                 everyone may see.

/clients/1234/billing <- contains only the
                         billing information
                         admins can see.

I think this also plays well into the idea that it's good to avoid inheritance and use composition instead. You don't need a Model and an AdvancedModel. You need both Model and a Billing model.

like image 58
Evert Avatar answered Oct 15 '22 19:10

Evert


In addition to what was posted by Evert, you could use different media types for different responses. The media-type is also a good place to support versions in your api.

//fullrecord is only returned to admins
GET -H"accept=application/vnd.yourcompany.fullrecord.v1+json" /clients/1234
//fullrecord can also only be set by users with admin role
PUT -H"content-type=application/vnd.yourcompany.fullrecord.v1+json" /clients/1234

For all others

//this will just return public info
GET -H"accept=application/vnd.yourcompany.v1+json" /clients/1234

For the client

//this will just return infos for logged in the client
GET -H"accept=application/vnd.yourcompany.client.v1+json" /clients/1234

This would mean that a user with role admin can explicitly request a certain view by setting the appropriate accept header. It also leads to a cleaner URI design.

like image 44
jschnasse Avatar answered Oct 15 '22 19:10

jschnasse