8

Let's say I'm creating a RESTful service to handle orders for my warehouse over the web.

  • I want to allow customers to create accounts
  • I want a customer admin to be able to create accounts for other users in their office
  • I want to allow customer users to create orders
  • I want a site administrator to be able to create and manage all customer accounts
  • I want a site administrator to be able to create and manage all users
  • I want a site administrator to be able to create and manage all orders

Given these requirements. My initial thoughts are to design endpoints in this manner.

# to request a new customer account
/customers/request {POST}
# create and view customers - limited to admins
/customers {GET, POST}
# view customer info, update a customer
/customers/{customer_id} {GET, PATCH}
# create and view orders for a customer
/customers/{customer_id}/orders {GET, POST}
# view and update order for a customer
/customers/{customer_id}/orders/{order_id} {GET, PATCH}

I feel pretty confident that those path's make sense and follow the general restful ideas. However, I'm not sure how to handle the users endpoint. The problem is, I want customer admins to be able to create users that can use their customer account to create orders. Where do customer admins POST to to accomplish this? I had a couple of ideas. Following this answer, I thought about this.

# creation of users always done through this endpoint no matter what the
# authenticated user's role is
/users { GET, POST }
# associate user with customer
/customers/{customer_id}/user_memberships { GET, POST }

The problem with this approach is how does the admin of the customer account get the ID of the user to associate with the customer account. Any GET request on /users would be filtered by retrieving only users who are part of their customer account. However, because the user would be created before the membership, they would never be able to view the user. I also though about just having two endpoints to create users.

# create a user for a customer account
/customers/{customer_id}/users {GET, POST}
# root users endpoint only accessible to admins
/users {GET, POST}
# return same user
/users/1
/customers/{customer_id}/users/1

It essentially boils down to using the customer url prefix as a means of authorization. It seems a little strange to have two endpoints invalidating the other. What if the root endpoints were only views of the subresource endpoints?

# view all users in system - admin only
/users {GET}
# create & view admin users
/admin/users {GET, POST}
# create internal office users
/locations/{location_id}/users { GET, POST }
# create customer users
/customers/{customer_id}/users { GET, POST }

In this case, we could still cache GET responses on the sub resources as they would not change unless there was a POST or PATCH/DELETE on the specific id of a subresource.

This style also seems to make sense for orders. Admins can view all orders even though they technically belong to a customer.

# admin can view all orders
/orders?customer_id=1234
/orders

I kind of like the idea of the root resource being a view of subresources allowing for easier authorization based on the url.

So, I guess after all of that, my real question is:

Is having multiple endpoints representing the same resource a problem even if one of them is just an aggregate view of the subresources and does not permit the creation of a resource through that endpoint?

Community
  • 1
  • 1
chrislbs
  • 83
  • 1
  • 4
  • According to my understanding, for ex. u want that for particular customerId you want that this customer only view its users not will be able to create its user which will only be created by admin, so this can be done using spring security as well and this definitely creates the problem so u have to categorize the customer according to your requirement. – Girish Jan 03 '14 at 18:49

2 Answers2

6

You shouldn't mix the design of your API, REST principles, and the need for authorization. You should design your API in a way that makes it:

  • easy to use
  • easy to maintain
  • easy to understand

A RESTful approach to API design tries to address these different concerns. A RESTful approach is about identifying the objects you have, their state, and their possible transition.

And that's where it stops. Now, you wonder about authorization. You want to be able to control what a user can do on given records depending on who the user is (an administrator, a customer,...) and what the targeted resource is (a customer record...).

What you need to do is deploy an authorization framework on top of your REST API in a loosely-coupled way. In other words, you want to externalize authorization. You definitely not want to build authorization straight into your API. Imagine that suddenly you have new authorization rules / constraints: you would have to recode your API. In doing so you'd break all the clients. That would lead to poor user experience.

So, we've identified you need to externalize authorization. Great. What are the different ways to do so? This depends on the language and framework you use.

You can use:

  • Spring Security in Java
  • Yii in PHP
  • CanCan in Ruby
  • ... and many more

You could also implement your own filters, for instance a Servlet filter in Java in front of your REST endpoints.

Lastly, you can turn to a full-blown attribute-based authorization model based on XACML. There are several open-source and vendor alternatives. If you are not familiar with attribute-based access control or XACML, have a look at the following links:

With XACML, you define policies centrally e.g:

  • Administrators can view all customer accounts
  • Administrators can modify a customer account he/she is assigned to
  • Customers can view and edit their own account only

The policies are then evaluated in an authorization service (in XACML that's known as a policy decision point). The authorization service exposes a binary authorization API which your API can call out to: can user Alice view record foo?.

Using externalized authorization based on policies and using XACML, you achieve a loose coupling between your business logic (your business API) and the authorization logic which you can more easily maintain and update.

David Brossard
  • 13,584
  • 6
  • 55
  • 88
  • I see your point about authorization being decoupled from the API design. However, I'm not really trying to design the API for authorization's sake. My main issue is such: you cannot have an order that doesn't belong to a customer (cusutomers/cust_id/orders). However, I want to also be able to query all orders (/orders). I'm mostly concerned with having two endpoints potentially refer to the same resources. (/orders?cust_id=1234) === (/customers/1234/orders). Do you see that as a problem? – chrislbs Jan 06 '14 at 19:26
  • I don't see that as a problem. If you think about it, it's like saying you have an API to manage orders and an API to manage customers. As it happens, you can determine a customer record from an order as much as you can determine an order record from a customer record. – David Brossard Jan 07 '14 at 13:03
0

According to my understanding, for ex. u want that for particular customerId you want that this customer only view its users not will be able to create its user which will only be created by admin, so this can be done using spring security as well and this definitely creates the problem so u have to categorize the customer according to your requirement.

Girish
  • 1,717
  • 1
  • 18
  • 30
  • 1
    Consider 3 roles for the service: Administrators, Customer Administrators, and Customer Users. Administrators can create and view anything. Customer Administrators can create additional users to access the Customer Account. Customer Users can do things on behalf of the customer account. e.g. Owner of Company A wants access to the system. They POST to customers/request, get a customer account and become admin user of the customer account. He can then create an account for Employee X by posting to /customers/customer_id/users, who can create orders by POSTing to /customer/customer_id/orders. – chrislbs Jan 03 '14 at 19:44
  • @chrislbs Ya , my friend you can do this with the help of spring security by providing the access of particular URL to particular member and you can apply the constraints on these as well. – Girish Jan 03 '14 at 19:49
  • @chrislbs Is this you want, then please click on my answer so that this will be helpful for others as well – Girish Jan 03 '14 at 19:53
  • Your comment doesn't address my question. I know how I'm going to do authorization with my URL scheme. My question is whether or not having multiple ways to access a resource is bad restful design. I add the component of authorization to clarify a potential use case. – chrislbs Jan 03 '14 at 20:01
  • @chrislbs My friend, this not affects your restful design u can map multiple URIs to single resource, rest architecture very swiftly supports this design but this design can decrease your performance of code. – Girish Jan 03 '14 at 20:37
  • @chrislbs have you get something from my answer – Girish Jan 03 '14 at 20:44
  • @chrislbs are you still thinking, or you get what I told – Girish Jan 04 '14 at 07:51