Ignore or not API endpoint parameters based on access level - api

I am working on an API endpoint that returns a list of products:
"api/products"
The endpoint accepts the following parameters:
page_size
page_number
Each product has a boolean property named IsApproved.
In the web application used by common users I always want to return only the Approved products ... On the web ADMIN application used by administrators I want to return all products, Approved or Not ...
My idea would be to add a new parameter (enumeration) named:
ApprovedStatus
And the values would be Approved, NotApproved and All.
On each API call I would check the user permissions ... If is admin I will consider the value on this parameter. If not then I will always return only approved products.
Another solution would be to have different endpoints ...
Any advice on which approach to take or is there other options?

The approval status is part of the product, therefore, in a perfect REST world, you don't want a different endpoint at all since you're accessing the same resource.
Then, for filtering a resource based on a property value, I think the convention is that if you specify that property as a query parameter it will only return those matching the value, and if not, it will return all of them, so I don't see the need to define a special ApprovedStatus parameter with some special values. Just query by isApproved!
Finally, about how to handle authorization. This, I think, should be handled at a completely separate layer**. If authorization is involved, you should have an explicit authorization layer that decides, for a specific resource and user, wether access is granted or not. This means the query would be triggered and if one of the resources generated by the query fails to be authorized for the user that triggered the query, it's taken out of the results. This accomplishes the behaviour you want without having any code that is checking specific users against specific query parameters, which is good because if tomorrow you have another endpoint that exposes this objects you won't have to implement the same authorization policy twice. Pundit is a perfect example on how to do this with Ruby elegantly.
**Of course, this approach retrieves data from the database unnecessarily which could matter to you, and also opens your endpoint up to timing attacks. Even then, I would consider tackling these problems premature optimizations and should be ignored unless you have a very good reason.

You're right about your ideas:
You can create a new endpoint just for admins, that will return all products
You can use a kind of authorization (e.g. Authorization Header) in order to check if the API is being called through admin or normal user. Then you can route internally to get all products or just IsApproved products.
You can add a proxy in front of your API to route to the right action, but it can also be achieved directly in the API but I think the second solution is easier.

Adding one more property is a bad idea.
In my opinion, adding another end point is very good. Because it will increase the protection in the admin end point.
Otherwise, since it is a web application, Simply set a cookie and a session to identify and separate the admin and user.

Going with the principle of least astonishment, I'd be in favour of adding a second endpoint for admin users. Such that you'll have:
GET /api/products (for regular users)
GET /api/admin/products (for admins)
This allows your code and API documentation to be nicely separated, and all of the admin-specific authentication details can live under the "admin" namespace.
The intention behind each API call is also clearer this way, which helps developers; and means that you can differentiate between admin vs regular usage in any usage stats that you track.
With ApprovedStatus, I think the specifics here don't matter much, but - considering what a developer using the API might reasonably expect / assume - it would be good to:
Ensure the ApprovalStatus parameter name matches the property name for "approval" that you return with each product object
Defaults to "approved" if it is not specified
Alert the user when an invalid value is specified, or one that they don't have access to
Bottom line: to answer your headline question - I think it's bad practice to ignore user input... sometimes. Design your API such that distinctions around when input can be passed in is very clear; and always alert the user if you receive input values that are technically acceptable, but not in the way that the user has requested, or for their access level. Ignoring values that are plain wrong (e.g. an argument that doesn't exist) is another story, and can be useful for future proofing or backwards compatibility.

Related

REST API optional path elements

I am in the process of designing a new API and am trying to make it as simple as possible. For the intended system most API consumers will be referencing objects that belong to them alone however a few other accounts will "own" objects in other peoples accounts. The questions is whether account becomes a required part of the path or an optional inclusion for these special super-accounts.
Here is an example spec and works fine in the single user context (Account ID "basic1"):
# return person "s4t4s2" for account "basic1"
GET /v1/person/s4t4s2
# return team "g3a35a" for account "basic1"
GET /v1/team/g3a35a
For super-accounts they have their own objects where the above implementation works, however they also require access to the properties of accounts they effectively own (Account ID "super1"):
# return person "s4t4s2" for account "super1"
GET /v1/person/s4t4s2
# get team "g399a2" for account "super1"
GET /v1/team/g399a2
# return person "s4t4s2" for account "basic1"
GET /v1/accounts/basic1/person/s4t4s2
Because most of my consumers will be dealing with the single account view is it best practice to use the second format for all accounts or is it entirely valid to use both formats with automatic scoping via authentication credientials when the account is omitted?
If I understand correctly, those are the same "person" resources, but they have multiple URIs? I would probably prefer having one URI for a single instance of a resource. Even if it has different "views". Different views can still be solved by either having different media-types, or just filling out fields differently on the same media-type depending on the user's permissions.
The advantage of having a single URI for a single instance is that it can be bookmarked, cached, etc. For example if a user's team/account view changes, it can't reuse its links to persons because the URIs change. I think that is not a good design.
If my understanding is wrong, and /v1/accounts/basic1/person/s4t4s2 is not the same person as /v1/person/s4t4s2 then disregard my comment. :)

Add instruction 'how' to perform a REST API request by adding header or body param?

Imagine a simple REST API that allows to create a user account, by sending a JSON resource to POST /users as in the following. By default it sends out a confirmation email to the user.
{
"username": "john#appleseed.com",
"password": "secret"
}
However sometimes there are good reasons for not sending out a confirmation based on the use case, e.g. another API client, or admins signing up users on their behalf.
Since it doesn't have any implications on the created resource but is more of an instruction how to create the user, should it be separate from the request body? What's the best way to do this?
Specify a custom header Confirmation: no-confirmation
Add a query param ?confirmation=false
Add a send_confirmation field to the request body
Let's take the options in order:
Adding a header value to indicate some semantic difference should be generally avoided. The API should be "browseable", meaning it should be discoverable following links only.
Adding a query parameter is, from REST perspective completely equal to creating another URI. It does not really matter how you expose it, the point is that the client needs to follow some links from the previous "state" it was in. This is actually ok, as long as the links to these resources indicate the different semantics you described: like creating users by admin, users creating themselves, etc.
Also note, that the API should not necessarily expose whether a confirmation is sent. The API should expose the "purpose", the server then can decide whether the use-case warrants a confirmation email.
Putting a send_confirmation in the JSON representation itself. This is ok, if this is a functionality available for the user. For example I can ask for a confirmation email. If I can't, and it is only used for differentiating different use-cases, then I would rather prefer option 2.
Summary: For the case you are describing I would pick option 2: different resources for admins and normal users.

REST API design for lists relevant to a user

I'm kinda surprised I don't see this in more REST discussions, but I'm debating how to best provide a list of things to a user, based on who the user is, or what they have authorization to see.
For example, let's say that we have an API for a bookstore. I might have a resource URL of /books which would list all books. But if my application logic is such that some books are viewable to users who are anonymous, but others are only viewable to those who are logged in, I'm a bit more uncertain the best way to go.
Obviously, programmatically I could filter based on the identity of the user (gleaned from their API key or whatever creds I'm using) but I feel like that's a bit 'off' from the standpoint of REST design.
Feels more right to have two endpoints; let's say one for /books/public and one for /books/restricted and we can say that the latter returns a 401 if the user isn't logged in. Easy enough.
But that pattern breaks a bit if the books in question are just a list of books that the user has read or has bought or have some other relation to the user. I've seen some API's that would do something like /my/books in that case, but that (again) feels off since the /my/books isn't a unique URL per se, it changes based (again) on a header value (API key, etc).
This leaves me to think that maybe the "best" approach is to do something like /books/users/1235 or /users/1234/books to get the books that 'belong' to user 1234, and then return a 401 if someone not authenticated tries to hit that URL, or a 403 if they're authenticated but not authorized to view that resource.
I guess that's a lot of background to my main question: What's the best practice for REST API URL design when the resource data is dependent on user identity?
Rest is not a standard so there is technically no right or wrong way to accomplish what you're after. Having said that, I'll give you my opinion.
I propose you simply use /books unless there is a very specific need for a service or user to see which are unique to them and those that are public. In which case I suggest doing all three. The fact that there could be more or less content at the /books endpoint based on the user's privileges doesn't alter the intent, it enhances it.
But that pattern breaks a bit if the books in question are just a list of books that the user has read or has bought or have some other relation to the user. I've seen some API's that would do something like /my/books in that case, but that (again) feels off since the /my/books isn't a unique URL per se, it changes based (again) on a header value (API key, etc).
I believe providing a /my/books endpoint is ideal if the content is intended to be private. If your reading list IS private and specific to your account then having an endpoint without that is masked significantly dramatically reduces the risk of having user-identifying information leak via the url.
In the event that reading lists can be shared then you would want there to be a a discrete action. If a user wishes to share their reading list or a subset therein they would compose the list and a new endpoint would be created specifically for it like /book-lists/xxxx-xxxx-xxxx. If your user's reading list is public by nature then /my/books would simply redirect to /booklists/xxxx-xxxx-xxxx
This leaves me to think that maybe the "best" approach is to do something like /books/users/1235 or /users/1234/books to get the books that 'belong' to user 1234, and then return a 401 if someone not authenticated tries to hit that URL, or a 403 if they're authenticated but not authorized to view that resource.
I'm not sure if you are referring to the reading list or the first concept of filtered results by some form of credentials.
/books/users/1234 would suggest a correlation to users from the book's perspective. This would be more applicable for authors or publishers, people that are actually affiliated to books in some deeper connection other than your examples above elude to. The same logical connection, for me at least, applies to /users/3493/books but to a lesser extent.
I think as a general approach, you should scratch both of these out if you have any desire for the public links to be shared. You are potentially making a very convoluted structure that could ultimately result in people getting really confused why their friend can't see a list of novels because she sent her search results with a url had been "personalized" and thus secured.
Obviously, programmatically I could filter based on the identity of
the user (gleaned from their API key or whatever creds I'm using) but
I feel like that's a bit 'off' from the standpoint of REST design.
No, it is not off, because the credentials are part of every request and the response can be dependent on request parameters...
The url depends on whether you want to share personal data with others:
if you want to share the url
/users/{userId}/favouriteBooks/
/books/favouriteOf:{userId}/
if you don't want to share the url
/books/favourite/
Keep in mind that you always can change the url structure as long as your service applies the HATEOAS principle...

RESTful way of having a single resource based on authentication

I have an API that provides an Account resource based on the authentication (login) that is supplied. As a user can only have one account, and can only see it's own account and not those of others, this API will basically be a single resource API in all cases.
So to keep things simple, I have this resource under the url accounts/ and when you access accounts/?username=dude&password=veryhard you'll get your account data (if you dohn't supply authentication you'll get a 403).
Now I wonder if this is RESTful. Also, you should be able to update your account info, and I wonder if PUT would be appropriate. In my knowledge, PUT should be done on a unique URI for the resource. Well, is this a unique URI for the resource? Generally a URI for an account would look like accounts/3515/ where 3515 is the account id. However, users don't know their account id. Also, there should be more ways to log in, instead of a username + password you should also be able to use a token (like accounts/?token=d3r90jfhda139hg). So then we got 2 URL's that point to the same resource, which also isn't really beautiful for a RESTful URI, is it?
So, what would be the most RESTful solution? Or should I not do this RESTful?
REST purists will consider that use of /accounts/ to obtain a single account is bad practice as it should specify a collection. Instead consider a key which cannot be mistaken for an ID, for example if your IDs are UUIDs then use a token such as 'me' so your URL is /accounts/me. This has the advantage that if later on you wish to obtain different account information, say for example you need to list users or you have an administration system using the same API, then you can expand it easily.
Putting username and password in the URL is also not pure REST. The query parameters should be directly related to the resource you are obtaining; commonly filtering and limiting the resources returned. Instead you should seriously consider using something like HTTP Basic authentication over an encrypted (HTTPS) connection so that you separate out your authentication/authorisation and resource systems. If you prefer to use a token system then take a look at oauth or hawk.
Finally, yes if you use PUT you should supply a full resource identifier. Given that it is very common for systems to read data before updating it the lack of ID won't be a problem as that will come back as part of the prior GET.
Yes accounts/?username=dude&password=veryhard is a correct REST URL.
PUT is used with an id if it used to update a resource, if you use it to create you must supply an ID. otherwise you use post to create a resource without id

OOP Design: Where should reusable validation be?

I've got a wizard which needs to validate if a user is logged in and then at the end validate that all the details they have entered are correct.
The issue is I'm not sure where to put the validation logic.
At the moment I have a BuyMembership class which has validation on the Buy() method.
However this won't be called at the beginning of the wizard where I need to validate whether the user is unique and is eligible for buying a membership.
For this validation I've created a BuyMembershipValidation class which validates if the user is eligible.
The issue now is that I have to pass a different parameter object to the BuyMembershipValidation and the BuyMembership classes. This means the data is split.
Is there a better way of doing it. Should I only load part of the information into the BuyMembership class for the initial validation and then load the rest after?
Update:
I need to validate when they enter the wizard (check if they are logged in already) and if they aren't then they will register as a new user otherwise I have to check if they have the right settings to buy membership as all users can't buy a membership. That's why I need two sets of validation. One for whether they're eligible and another for the actual data they enter to register. However I would like to recheck that they are eligible when they make the final transaction just in case they somehow made it past the first wizard step or if the web service (where I'm doing the logic) is called from somewhere else at a later point.
Another Update:
I've added an answer with my decision.
TIA,
Jonathan.
You're actually talking about three very different things here, and like Allain said, you need to think along the lines of single-responsibility to do this right, OOP-like.
Authentication: making sure a user is known
Authorization: making sure a user can access certain logic in your application
Validation: making sure the data a user inputs is valid/safe/what's expected or needed
A validator is typically tasked with validating user supplied information, not authorization. The single responsibility principle dictates that in your case they should definitely be handed by two objects.
Passing different parameter objects is fine, but but I think the authorization one shouldn't really be passed in to the BuyMembership object, it sounds like something that should be handled externally to it.
My 2 cents.
User authentication is a broad topic. It really depends on what type of application you are building. If it is a web application, I would recommend using one of the standard methods for authentication: NTLM / kerberos
As to when to validate, I don't see any problem with holding off on the validation until the actual Buy method is called. As long as during the steps in the wizard, you are not releasing any information to them and are only collecting data from a form. The main time when you need to be concerned about authentication is when you are actually placing an order or releasing information to the user that should be locked away in a database somewhere.
If you provide more details to what you are trying to do, I will refine my answer to help you a bit more. It's a rather broad question though, so this is the best I can come up with for now.
I've decided to go with just the one class. The reason is that there is common logic / behaviour in the IsEligible and Buy methods. One determines if the inputs meet the eligibility requirements and the other validates that all the details needed to buy the membership are valid. I don't really see this as authentication related. The authentication is done in a separate web control and I just check the Identity.IsAuthenticated and Username to see if they are logged in. I then check their username and if they are eligible to buy then I let them continue, otherwise show an error message. In terms of input validation I already have validation controls on my webpage but the validation I'm concerned with here is server side business logic validation. For that reason I think it should be in the business logic class. Not some validation class separate from the logic that performs the payment. This may just be my way of looking at it but I think this fits well with the BDD ideas of keeping validation inside the entity that holds the data.