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.
Related
I'm trying to set up my website to allow location additions to the urls.
EG: mysite.com/us/ca/sanfrancisco/home
While also still allowing mysite.com/home and everything in between.
Spring boot parent so you know what version of spring I'm using:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
If there's another piece of versioning you need, let me know.
I get that I can add in regex variables to the request mapping, but how would I go about persisting those urls across more requests?
So right now for an example, the testing error page I have:
#RequestMapping({"/error/501", "/{state:[a-z]{2}}/error/501", "/{state:[a-z]{2}}/{city:[a-z]+}/error/501"})
public ModelAndView testingLocations(ModelMap model, #PathVariable(value="state", required = false) String state,
#PathVariable(value="city", required=false) String city){
getLogger().info("State: {}", state);
model.addAttribute("stateTest",state+":"+city);
model.addAttribute("view", "error");
return new ModelAndView("error/501", model);
}
But when I'm on my testing page, and I click the home button, it takes me back to mysite.com/home
So My Questions
Is there a way for me to persist it so that if they're currently on a location based url, it will apply that base to the future navigations? (unless they manually enter the url to not include them)
Then as a follow-up, is there a way for me to globally apply these request variables without requiring me to add the #PathVariable to every request mapping method? I get that I can just add the request mapping variable strings themselves to the controller class, so that I don't need those on every method. But is there a way for me to utilize those without needing the #PathVariable annotations?
Finally, is there a way for me to make this not as hardcoded, like a way for me to say /{*location}/error to cover as deep as the locations will allow? While still having the verification on the location formatting, so verifying that 1 we support the locations given, 2 the format is correct (/ca/sanfrancisco vs /anything/anything
The last one I can live with, if I need to have the /state/city/municipality/actualtarget
As far as verifying that we support the locations given, I understand that's on my end, which I'll probably just have a small database to keep track of where we do and do not support for the given variables.
Is there a best practice for building this system? I tried to find something on this, but googling "spring boot location url" is not the best at giving me what I need, since "location" can apply to a pretty wide range of topics. I've gotten to where I am from searching, but I can't seem to pin down these last few steps.
Any help/advice/suggestions is appreciated. If upgrading versions is required, I'm not sure how viable that is at the moment, I'd have to look into it. Preferably I'd like the solution to be able to be done on the current spring version I'm running.
The best way here is:
#RequestMapping("/some/{foo}/{baz}")
public String hi(CompositeObject compositeObject) {
return "hi";
}
#Data
public class CompositeObject {
private String foo;
private String baz;
}
Spring provides functionality for request path and request parameters to collect it into a composite object. It doesn' work either with body or headers.
If you have something optional like state, then just keep it null at the controller and handle later
I am developing an API REST but I faced the following problem: in some cases, the ID of my resources are URLs.
The API is used for both, a software where the IDs are in UUID format, and a FileSystem where the only way of identifying resources is by using their path.
So, to retrieve a resource, I use a GET statement like that:
- http://localhost/files/{id}
When I am retrieving a document stored in the database of the software, {id} is something like "2ab89005-b938-46c8-9d22-3a12a3c40672", but when the document is in a FileSystem, the {id} can be something like "/documents/2018/april/myfile.pdf".
How can I manage this situation? Until now, I am doing a GET request over the URL "http://localhost/files/" (without {id}) and including the required information in the body in JSON format:
{
"id": {id}
}
Of course, I know this is not a good practice, but I don't know how can I implement REST architecture in this situation.
I am using Java + Spring Boot.
Thank you.
SOLUTION IMPLEMENTED:
At the end, I have created the following endpoint: http://localhost/files/?id={id}.
The reson of doing this is because the application that is calling me doesn't know if it is asking for a resource stored in a FileSystem or in my database.
If the application knows it, I think it is better to implement two endpoints: http://localhost/files/?id={id} (for FileSystem) and http://localhost/files/{id} (for database).
It's realy bad idea to take ability for user chose file in FS because of Path Manipulation Vulnerability.
But if you want to do that, you cat set String as a path variable, and decode your request string:
#RestController
#RequestMapping("test")
public class MainController {
#GetMapping("/{id}")
public Mono<String> main(#PathVariable("id") String id){
return Mono.just(id);
}
}
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.
I'm creating an API that exposes something similar to group memberships. In effect, I have a GroupMembership resource that is exposed at /groupmembership.
Now when I want to create a new association between a user and a group, I will POST to /groupmembership. What I'm curious about is how I should reference the User and Group resource instances. Do I do it through the URI or do I do it through their UUID? That is, which of these two payloads is valid?
POST /groupmembership
{
user: "http://localhost:8080/user/abcd-def-ghij",
group: "http://localhost:8080/group/1a2-b3c-4d5"
}
or
POST /groupmembership
{
user: "abcd-def-ghij",
group: "1a2-b3c-4d5"
}
I am using Spring HATEOAS and as far as I can tell, there is no way to dereference a link to an entity id, which makes the first approach somewhat problematic. Basically, given a link I want to be able to figure out the UUID that references the entity. But I also don't want to parse the URI since they are supposed to be opaque anyway. So can Spring HATEOAS do that?
With the second approach, I can simply look it up, but I wanted to know which approach makes more sense. One thing that bothers me is that the first thing has the distinct flavor of something that should be handled by the client; i.e., it's the client that follows the URI. It seems like the server should simply be able to handle the UUIDs? But on the other hand, the server fully-controls the structure of the URI, and so it seems like it should know how to dereference the URI to the appropriate entity/resource id.
Hi you do not need a payload. Publisch the URL of the created Ressource through the Location header with status code 201.
To answer your question, use the URI.
The client shouldn't compose URIs on his own to get the resources.
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.