RESTfully Routing API Based on Custom Roles

I am developing an API using Flask-RESTful and my application has three roles.

  • site_admin
  • department_admin
  • Main

For any given resource, the returned JSON object has a different set of keys based on each role.

For example, if you click / order as "site_admin", the result might look like this:

{
  "orders": [
    {"id": 1, "user": "foo", "paid": True,  "department": "A", "code": 456},
    {"id": 2, "user": "bar", "paid": False, "department": "A", "code": 567},
    {"id": 3, "user": "meh", "paid": False, "department": "B", "code": 678}
  ]
}

      

However, if you click / order as "department_admin", the result might look like this:

{
  "orders": [
    {"id": 3, "user": "meh", "paid": False}
  ]
}

      

And if you click / order as "basic" it will be a very minimal JSON response like this:

{
  "orders": [
    {"id": 2, "paid": True}
  ]
}

      

What is the RESTful way to implement this?

I can think of three ways to do this.

(1) using the request argument and filtering:

class Orders(restful.Resource):
  def get(self):
    if request.args['role'] == 'site_admin':
      return admin_JSON_response()
    elif request.args['role'] == 'department_admin':
      return dept_admin_JSON_response()
    else:
      return basic_JSON_response()

api.add_resource(Orders, '/orders')

      

(2) filtering the session object:

class Orders(restful.Resource):
  def get(self):
    if session['role'] == 'site_admin':
      return admin_JSON_response()
    elif session['role'] == 'department_admin':
      return dept_admin_JSON_response()
    else:
      return basic_JSON_response()

api.add_resource(Orders, '/orders')

      

(3) having a different route for each role:

class OrdersSiteAdmin(restful.Resource):
  def get(self):
    return admin_JSON_response()
api.add_resource(OrdersSiteAdmin, '/orders_site_admin')

class OrdersDeptAdmin(restful.Resource):
  def get(self):
    return dept_admin_JSON_response()
api.add_resource(OrdersDeptAdmin, '/orders_dept_admin')

class OrdersBasic(restful.Resource):
  def get(self):
      return basic_JSON_response()
api.add_resource(OrdersBasic, '/orders_basic')

      

... Is there a consensus on which is the preferred RESTfully way?

Many thanks!

+3


source to share


1 answer


Your option # 2 violates the stateless constraint, using custom sessions is not a good idea in a REST API, and instead you should require your clients to provide authentication with every request.

Let's say you fixed # 2 and instead of the user session there is now a variable current_user

that gets populated during authentication. Then you can rewrite this example like this:

class Orders(restful.Resource):
  def get(self):
    if current_user.role == 'site_admin':
      return admin_JSON_response()
    elif current_user.role == 'department_admin':
      return dept_admin_JSON_response()
    else:
      return basic_JSON_response()

api.add_resource(Orders, '/orders')

      



Look at your three options one by one:

  • (1) defines a role in a query string that allows any user to request any view by simply passing the required role. But why put a role in the query string? I am assuming that you will be authenticating your users, so knowing your user you also know this role. This seems unnecessary and will give you additional verification.

  • (3) creates different resources for each role. Once again, you have to make sure that the "base" user does not have access to the two URLs that are of higher roles, so you have some validation functionality as well.

  • (2) assumes that the user database stores the role of each user, so once the user is authenticated, the correct representation for his / her role is returned based on the assigned role. This, I think, is the best option, since users really have no way to hack into data that they are not allowed to see.

Speaking of RESTful, I'll also look at your views that could be improved. Consider using links to other resources instead of providing IDs to conform to HATEOAS .

+4


source







All Articles