Dynamic instance-level authorization with Jersey (based on id) - java

Apologies as I am fairly new to Jersey. I've been trying to find a way to have instance-level access authorization using Jersey resources, but the most granularity I'm seeing is Role or static instance-level permissions. I'm a little puzzled because it
To describe better what I mean: suppose an User owns a group of Post resources - presumably this user has the role Author. I don't want every User who is an Author to be able to modify every Post, though. What is the easiest way to control this?
Is this the kind of authorization that's dealt with within the resource class method? Should I be creating a custom Authorization filter? If so, are there any examples of such a thing out there? I'm a little puzzled as it seems like such a common use case.
Thanks!

The reason there isn't much out there in terms of examples is that it's probably down to your own data model as to how you handle this.
Taking a simple example, if each Post has an owner then your resource would probably look something like this:
#PUT
#Path("{id: [A-Fa-f0-9]+}")
#Consumes(MediaType.APPLICATION_JSON)
public T update(#Context HttpServletRequest request, final T item, #PathParam("id") final String id)
{
final Post post = getPostbyId(id);
if (!post.allowedToUpdate(request.getUserPrincipal())
{
throw new UnauthorizedException();
}
// Authorized, carry on
}
There are no end of variations on this theme, but if you're doing resource-level authorization you probably want to do it in something like this way, where you obtain the resource given its ID and then decide if the user is authorized to carry out the requested operation.

Related

Rest Resources Separation

I've been trying to start a REST api with Spring Boot and I'm a bit strugling with the separation of my resources and which endpoint should be in which file.
Let's say we have an api enpoint to deal with a user and achievements from this user:
/user/{id} GET - to fetch user by id
/achievement/{id} GET - to fetch by achievement
Which are both in their separates resources file:
UserResource
#RestController
public class UserResource {
public UserResource() {...}
#GetMapping("/users/{id}")
public UserDTO getUser(String id) {
log.debug("REST request to get User : {}", login);
return userService.getUserWithAuthoritiesById(id).map(AdminUserDTO::new));
}
And AchievementResource
#RestController
public class AchievementResource {
public AchievementResource(...) {...}
#GetMapping("/achievements/{id}")
public ResponseEntity<Achievement> getAchievement(#PathVariable Long id) {
return achievementRepository.findById(id);
}
}
So far so good, pretty simple. My problem comes when I must get all achievements from a User. Naming covention says I should have an endpoint such as:
/user/{id}/achievements GET
But where should this endpoint be? I feel like both Resources could be good since for the UserResource, the root of the endpoint is the user, but the AchievementResource could be logical too since we are returning achievements.
Easy answer: you have the wrong problem
But where should this endpoint be?
The definition of the resource should be in your machine readable api definition. You produce the class files you need by feeding your definition into a code generator for your choice of language. The generator will put the classes it creates in files somewhere, and you leave them in this default arrangement until some point in the future when you have a compelling reason to arrange them differently (at which point, you fork the code generator and make your preferred design the default).
That said, when designing by hand there's nothing particularly special about "REST endpoints". The guidelines for where resource classes belong is no different from any other classes in Java....
That said, I find that the literature around file layout heuristics rather disappointing. There doesn't seem to be a lot of material discussing the trade offs of different designs, or contexts in which one choice might be more compelling than another.
For your specific situation, I would advise putting the new resource into a file of its own. The argument here being that your UserResource has User dependencies, and your AchievementsResource has achievements dependencies, but your new thing has both, and as a matter of (hand waves) principle, we should avoid bringing unneeded achievements dependencies into the namespace of the UserResource (and vice versa).
In other words, if we find ourselves adding imports to an existing file to implement a new thing, that's a hint that the new thing may be better placed somewhere else.
Using separate files also has nice mechanical advantages - it reduces merge collisions, each file will have its own source control history (meaning that the history of Users isn't cluttered with a bunch of commits that are exclusively about new thing). See Adam Tornhill's work over at CodeScene, for example.
As you separated the controllers, it is not wrong, you should classify the methods by their general entity, "if I need to recover the user's achievements", it is related to both, however, where does she get this data from? of the Achievements knowing that each achievement must have a relationship in the database with the user, you can very well look it up in the achievement controller with a List returnAchievementsByUser (Integer Id) method.
It depends on your point of view and the business behind the scene. You can use just one endpoint in many cases; if "users" are the main resources who have achievements, then "/users/{user-id}" and {users/{user-id}/achievements/{achievement-id} get the user by Id and special achievement of the user
#RestController
#RequestMapping("users")
public class UsersRestController{
#GetMapping("/{user-id}")
public UserDTO getUser(#PathVariable("user-id") String id) {
code...
}
#GetMapping("/{user-id}/achievements/{achievement-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code...
}
}
And if locating "achievements" on top of "users" in their entity hierarchy has meaning to you and your business, then /achievements/{achievement-id}/users/{user-id} can be a rest presentation:
#RestController
#RequestMapping("achievements")
public class AchievementsRestController{
#GetMapping("/{achievement-id}")
public UserDTO getAchievement(#PathVariable("achievements-id") String id) {
code
}
#GetMapping("/{achievements-id}/users/{user-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code
}
}
finally ,whenever they are not in an entity hierarchy, you can pass userId to
"/achievements/{achievements-id}" (or achievement-id to "/users/{user-id}") as a RequestParam.

RESTful shiro permissions based on data

I'm creating a RESTful service with Jersey (2.28) and use Apache Shiro for permission handling. So I used the buildin HttpMethodPermissionFilter which creates permissions like resource:read or resource:write. Now I have the problem that a user may only be allowed to read or write a specific resource and that I would need something like resource:write:<id> or resource:write:<name> or what ever as identifier.
I thought about extending the filter but at that point - even while I could access the body or the url - I have no idea how the data looks like.
Solutions I thought about:
Always pass a query parameter in the url, like /api/resource?id=xxx and if given apply that parameter for the permission string. But there is no way to tell if the parameter is required or not if both resource:read and resource:read:<id> exist. The filter might create a wrong permission for the given url. I could apply the filter only to urls where I know it must be the case, but seems all a bit wonky and error prone.
Remove the filter and ask for the permissions inside of the requested method.
#GET
#Path("/resource/{id}")
public Response getResource(#PathParam("id") String id) {
if(AuthorizationHandler.hasPermission("resource:read:" + id) {
return Response.status(Status.OK).entity("Resource GET works").build();
}
// return 403 or handle exception or ...
}
Somewhat like that, but it will leave me with exception handling in every method which also seems not much preferable. Maybe I could use an ExceptionMapper to handle responses... haven't tried that.
Does maybe someone else have another idea how to solve this efficently or maybe point me to an already existing solution? I'd prefere to use the #RequiresPermissions("resource:read") annotation (or a custom one), but could also define the urls / filters in the shiro.ini file /api/resource/** = noSessionCreation, jwtf, rest[resource] or I fallback to solution 2 if that's recommended.

Best practice determine passed parameter type in Restful API?

I am doing my first JSON RESTful API in Java.
The front end will be AngularJS. For the Web Service, I'm using Tomcat 7, JAX-RS, Jackson, Jersey, etc...
I would like to keep the API as simple as possible.
e.g. I would query a user by ID as well as UUID. So I would like to use e.g. /rest/users/{id} and /rest/users/{uuid}. I would ideally like the back-end to figure out which is which rather than create separate API branches.
Since it's all coming from JSON, I assume I can not just let Java figure it out based on the type. So I have come up with:
#GET
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getJSONUser(#PathParam("id") String id) {
User user;
if(id.contains("-")){
user = userDao.getUserByHandle(id);
}else{
user = userDao.getUserById(Long.parseLong(id));
}
return user;
}
I notice that as an example, the Facebook API does this all over the place. e.g. /{user-id}/albums and /{page-id}/albums.
Is there some trick I should be aware of that makes detecting these parameters easier? Or is FB encoding something in the ID itself? i.e. am I on the right track already. It's pretty cool how Jersey is able to figure out the calls based on MediaType. I thought there might be something equally as cool going on inside to deal with this...
Thanks in advance
Peter
I assume that they are encoding something in the ID itself.
From a purely REST-point-of-view, /{user-id}/albums and {page-id}/albums are the same endpoint.

jax-rs : For a same #Path method returning Json, how to serialize differently per device type

In short:
I'd like to return different JSONs, with say less attributes, when a request comes from a phone than when it comes from a desktop pc.
I want to build a REST service.
The service will serve data based on JPA entities.
The service is declared with #Path.
Depending on the User-Agent header, I want to serve a richer JSON for desktop than for mobile devices. Selection to be done serverside.
Is there a better way than to build a second serializer and use a condition (if(request useragent ) to call them (in every method) and be forced to return some String instead of any Object (making #Produces annotation unused).
Thank you
One way it to add a PathParam or QueryParam to the Path to tell the device type in the request, so the service can be able to understand the type of device, from which the request is and create the appropriate JSON.
Please check the most voted SO answer to find out whether the request is from mobile or desktop and add the parameter accordingly
You can use jax-rs resource selector, which will use different sub-resource depending on user-agent string.
#Path("api")
public UserResource getResourceByUserAgent() {
//the if statement will be more sophisticated obviously :-)
if(userAgent.contains("GT-I9505") {
return new HighEndUserResource();
} else {
return new LowEndUserResource();
}
}
interface UserResource {User doSomeProcessing()}
class HighEndUserResource implements UserResource {
#Path("process")
public User doSomeProcessing() {
//serve
}
}
class LowEndUserResource implements UserResource {
#Path("process")
public User doSomeProcessing() {
//serve content for low end
}
}
By invoking "/api/process" resource the response will depend on userAgent. You can also easily extend the solution for other devices, and implement MiddleEndUserResource for example.
You can read more information about sub-resources here:

Google Cloud Endpoints call one endpoint from another

Let's say I have the following Google Cloud Endpoints:
#ApiMethod(name = "account.insert")
public Account insertAccount(Account account, #Named("userId") Long userId)
#ApiMethod(name = "user.get")
public User getUser(#Named("id") Long id)
Let's assume Account has a reference to User, which shall be created when the account is inserted. So what I need to do is look up the user by userId first to make sure it exists, then set the reference in Account.
To avoid redundancy, I decided it would be good to call getUser(userId) as this functionality exists as another endpoint.
This would work fine as long as both methods/endpoints are in the same class/file. As soon as I cross the class boundaries, I receive the following message:
"non-static method (...) cannot be referenced from a static content"
First of all, I don't understand why the content is static if it isn't marked as static in the method signature, but maybe the #ApiMethod annotation does its job here, because it is understandable that the API is exposed as a static method.
Second, I would probably be able to get around this by using a delegate function, but then I would like to know if it is considered good practice to call into one of my own endpoints from another endpoint.
Let me know what you think, please.

Categories