Does anybody know if there's play framework module that allows you to authorize pages dynamically in Java?
I'm using the play-authenticate/deadbolt modules (http://joscha.github.io/play-authenticate/ and https://github.com/schaloner/deadbolt-2) for the the authentication/authorization mechanism. However, deadbolt doesn't have a straight forward sample on how to implement authorization per page or resource. It does have the ability to create dynamic constrains but the docs are limited and don't tell you if you can create per page authorization using a model or something else.
I'm thinking of creating a model "Page" that allows you to keep track of all the permissions per page dynamically. Is there a better way ?
Thanks.
You could do this in Deadbolt by wrapping your page content in a dynamic tag. The name given to the tag maps to a DynamicResourceHandler, which can then do a lookup in the DB to see if the current user has access to the page.
For example, you view would look like this:
#(handler: my.app.MyDynamicResourceHandler)
#dynamic("handlerName", "pageKey", handler) {
Your page content goes here
}
And the handler's isAllowed method would be implemented along the lines of
public boolean isAllowed(String name,
String meta,
DeadboltHandler deadboltHandler,
Http.Context context)
{
Subject subject = deadboltHandler.getSubject();
result = // check your user's access to the page key (provided as the meta argument)
return result;
}
The documentation is being improved at the moment, and in the meantime you can see more examples at http://deadbolt-2-java.herokuapp.com/#template-dynamic
Related
I have a Spring Boot REST API that I'm building. Im slightly stuck on the correct way to design my API in a way that protects each individual users' data. For example, consider the following database relations:
User -> (Has Many) Projects -> (Has Many) Tasks. (A User has-many Projects, and a Project has-many tasks).
For example, if I design my endpoints in the following way:
GET /api/v1/projects/{projectId}
POST /api/v1/projects/{projectId}/tasks
Just as a simple example for the above, how can I make sure, when creating new tasks for a certain project, that the project belongs to the logged in user?
Currently, I am using JWT tokens via Spring Security as my authentication strategy, and included in the payload of the token I have my Users' id. So with every request I can retrieve the user, but surely that's incredibly inefficient to be making so many requests to the database and check if the user actually has a given project.
Some solution I was thinking about is to simply have endpoints designed like this:
/api/v1/users/{userId}/projects/{projectId}/tasks
And then I can use the user id in the JWT payload and compare it to the user id in the request parameter. But then that would mean with every new relation in my database, the length of the url is going to be massive :) Also I guess it would mean all the business logic would be inside the User service for the whole application, right? Which seems a little odd to me... but maybe I'm wrong.
Im not sure if thats an issue or not, but just trying to design the API to be as elegant as possible.
Thanks again!
Checking if the user has permissions to a project on every request is the correct solution. Consider cases when many other applications / users are calling your API. You want to make sure that your API is as secure as possible and cannot be manipulated from the frontend.
To make it more efficient you should have a way/query to check associations in your database like a simple query that returns true/false which should be quicker than retrieving all the data and comparing in Java code.
And when possible combine multiple database queries into one, like for one of your examples:
GET /api/v1/projects/{projectId}
in this case, don't run a query to get a user's information and a query for the requested project. Instead you could do a single query with a join between the user's table and the project table which should only return a project if the user is associated with it. The best way really depends on how your database is structured.
Adding a user id into the API URL is just redundant information. Just because the user id in the token matches the user id in the URL doesn't mean the user has any kind of permissions to any project.
Another solution to be avoided is to include the user's project ids in the JWT token which you can then compare without making a database request. This is bad for several reasons:
The token should only have required information for the user to access the API, it shouldn't have business logic
Depending on how much business logic you store in the token the token can become large in size. See this post for a discussion on size limits: What is the maximum size of JWT token?
If there is a way for the someone other than the user (like admin) to add/remove a user's association to a project then that change will not be reflected in the token until the token's data is refreshed
EDIT:
On the spring side I have used the #PreAuthorize annotation before to handle these types of method checks. Below is pseudo code as an example.
#RestController
public class MyController {
#PostMapping
#PreAuthorize("#mySecurityService.isAllowed(principal, #in)")
public SomeResponseType api1(SomeRequestType requestData) {
/* this is not reached unless mySecurityService.isAllowed
returns true, instead a user gets a 401/403 HTTP response
code (i don't remember the exact one) */
}
}
#Service
public class MySecurityService {
/*
Object principal - this is spring's UserDetails object that is
returned from the AuthenticationProvider. So basically a Java
representation of the JWT token which should have the
user's username.
SomeRequestType requestData - this is the request data that was
sent to the API. I'm sure there is a way to get the project ID
from the URL here as well.
*/
public boolean isAllowed(Object principal, SomeRequestType requestData) {
/*
take the user's username from the principal, take the
project ID from the request data and query the database
to check authorization, return true if authorized
make this check efficient
*/
return false;
}
}
The annotation and the security service can then be applied to multiple methods. You can have many different security services depending on what your are checking.
There are other ways available too https://www.baeldung.com/spring-security-method-security and this has to be enabled in spring's configuration (also explained in the link).
Hi so if I understood it correctly you want to automatically assign the task that is going to be created with "POST /api/v1/projects/{projectId}/tasks" to the current logged in user.
You could try to add a Parameter 'Principal principal' to your rest controller. The Principal is the user that is sending the request.
After you have your Prinicipal, you could write a simple convert method(for example: convertPrincipalToUser(Principal principal) which returns you the user. Finally you can add your user to the corresponding task)
Here is some more information about it:
https://www.baeldung.com/get-user-in-spring-security
Working with Spring / Spring security on a small project at the moment and having difficulty implementing this feature. Ideally I only want user1 to view the details of user1 and not those of 2,3 or 4 for example.
I've implemented Spring Security with Roles and understand that I can retrieve a UserDetails object or a principle, I'm not sure exactly but I know I can retrieve the details of the current logged in user using one of, what appears to be many methods.
This is what I'm currently using as a proof of concept when we go to the Admin/home page:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Gamer gamer = gamerService.findGamerByEmail(auth.getName());
System.out.println("Auth: " + auth.getName());
System.out.println("Gamer: " + gamer.getName() + gamer.getId() + gamer.getEmail());
The security config takes care of whether or not the current user can access because of the roles assigned to it.
I believe I should be able to go to the url of /mysite/viewUserDetails and have that page display information of the current user but I cannot find any examples of this, I've found plenty of example that prove a logged in user can view a page but none that specify checks in place to ensure user1 can only view user1's details.
On an older page I do this to display information for a particular user but I understand this to be bad practice-
<a th:href="#{/gamer/{gamerid}/games/excited (gamerid=${gamer.id}) }">
*Worth noting that this isn't using any form of login/registration to pull out this info, I'm simple using the id I pass in as part of the DB query.
It maps onto :
#RequestMapping("/gamer/{gamerid}/games/excited")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
So my question becomes, and I hope you can point in the right direction, How can I implement a solution where a user can only view his/her details and how should this be represented via the form and connecting controllers as passing ids in the url is kinda ugly (I could use a guid but...)
Thanks so much in advance.
It's actually quite an easy choice. Either you have an entry point like:
#RequestMapping("/gamer/{gamerid}/games/excited")
and you manually check that the user in session can access the requested resource, or you have something like
#RequestMapping("/my-games")
that automatically reads the user id from the security context.
More than a security choice, I'd pick one depending on code reuse and future use-cases (for example the same page/set of pages can be seen by more than one user).
Have a look at #PreAuthorize annotation. It is possbile to annotate given endpoint with it and create custom logic in a bean. Then you can use custom method to allow or disallow the endpoint to proceed :
#Controller
public class HomeController {
#Autowired
private AuthenticationService authenticationService;
#RequestMapping("/gamer/{gamerid}/games/excited")
#PreAuthorize("#authenticationService.hasAccess(#gamerid)")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
}
Service class :
#Service
public class AuthenticationService {
public boolean hasAccess(String tgamerid) {
//implement logic here
return true;
}
}
Method hasAccess in the AuthenticationService should return boolean. #PreAuthorize will be launched before controller handler method is invoked. The controller above is just an example. You can pass Authentication object in SPeL expression in #PreAuthorize annotation to service method or get it from security context inside service class, to implement logic which fits your needs. More information can be found here and in Spring Docs.
For some subscription process, there is a requirement as follows :
User visits our website
For any subscription process, user click the relevant section that should redirect the view to specified external URL having POST parameters.
External party handles the subscription
It could be solved using <form/> defining the method=POST and putting all the required parameters in hidden fields.
But, we have CMS in place so changing the already existing view to support <form/> would be cumbersome.
I was hoping that there could be some solution that will allow in the following way:
User visits the page
User clicks the relevant section and sends the request to our internal mapping (along with some ID).
Using that ID, will prepare the relevant payload and will redirect the view to external url along with that payload (using POST).
Please let me know for more information.
As documented in the Spring Documentation you can return s.th. like redirect:http://myhost.com/some/arbitrary/path in your controller. To do it dynamically, use the following
#RequestMapping("/redirect")
protected String redirect(#RequestParam("url") String url)
{
return "redirect:" + redirectUrl;
}
Of course you can implement a special RedirectService on your own, but that should be simple.
It is not possible to send payload to another endpoint using a redirect. A redirect is a HTTP-Response with response code of 301,302,303,307,308 - each has a different intention (see Wikipedia Statuscode for additional info). It is the responsibility of the client to decide what to do next - e.g. calling the new location with the same payload.
I figured it out the way to perform the required use case.
In order to redirect the view with POST parameters, please refer the steps below
1) User clicks on the link.
2) Firstly redirect to user to internal mapping.(It would be an intermediate GET request before redirection to external endpoint).
Append all the required parameters to this endpoint.
3) Then transfer the view to intermittent form page populating all the fields in hidden tags.
4) Upon loading the page, submit this form to required destination using POST method.
In this way, we could hack this. I believe it's not a clean way, but if we have to do this, it could be done in this way.
When developing a Web Application in Java, launching in Tomcat, I need to be able to create (dynamically) a new static address (link,URL) in the server that will be used to view the information of a new item, let's call it new_item_001, which have been just created by one user.
Say I want to create a new address
www.domain.com/webapp/items/new_item_001
which can be used to render a view of the contents of new_item_001.
Which is the best approach to do this?
Should I dynamically create a new servlet class for this view?
Should I dynamically create the folder items and one html file new_item_001 for this item inside of it?
Should I edit the server address mapping rules to create this static address and map it to a central servlet which somehow knows which item to display?
I understand the question is ill posed, and that I am far from even understanding the issue, so I would like some guidelines on what to look for.
None of the above.
You should simply have a servlet mapped to /items/*. When a request come to this servlet, analyze the actual path of the request, extract the part after /items/ to know the actual value (new_item_001) in your example, get the data corresponding to this item from the database, and send it to the browser.
Using a true MVC framework like Spring MVC would make that much easier. You could simply map a method of a controller using
#RequestMapping("/items/{itemId}")
public Item getItem(#PathVariable("itemId") String itemId) {
...
}
and let the framework do all the URL parsing for you.
I would like to tackle this in a simple way. Creating a servlet for each created item would be overkill and become quite cumbersome to manage after a successful run of the application for some time.
Changing/editing server mapping URL looks very naive approach and is not scaling too. Let configuration be there and change them only when you actually need to change them.
My suggestion is to create one servlet that handles all these requests. For example, you may save item information on a datastore or on file system(i.e images uploaded by user etc..). Next time a GET request is received by the application to fetch saved information of an item, servlet should be able to reference the item on database associated with the item id on the URL. If you don't wish to expose item id/surrogate key in the database, you can also have a simple mapping between them by implementing your own logic. Frameworks like Spring MVC do a good job in mapping URLs to resources like this should you wish to use a framework.
Additionally to minimize the number of requests to the same item, you can also implement an HTTP caching strategy(i.e. ETAG, If-Modified-Since) by instructing your web server at the time of first GET request from a user.
I working on Spring MVC app. The app funcionality is accessible through ReST API which jsp containing ajax logic consume. I am using spring security with defined roles (USER, COMPANY, ADMIN). Methods use requestMapping with responseBody such as:
www.app.com/auth/{userId}/request/{requestId}
It, of course, support GET for obtaining resource and POST for its creating or updating.
The problem is that after succesful login with, for example, userId = 1 I want GET request with requestId = 99. But when I run WebDev client for Chrome, I can also access another resource with easy request in format
www.app.com/auth/5/request/{requestId}
So basically, I can access resources, which I am not allowed to see. I hope you got the idea, where I am heading.
My question is - What is the best approach to secure this?
I was thinking about storing logged user Id (Integer) in session and comparing it everytime request for resource is made, but it seems to me that I am pulling the wrong end of rope :)
Thank you for any advice
You should have a look into the Expression-Based Access Control section of the spring security documentation.
Example copied from the documentation:
#PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact) {
..
}
This would check if name of the contact is equal to the name of the currently logged in user.
Using this this feature you can build much more sophisticated access rules than you could do with simple roles. However, this does not exclude using roles. You can still keep roles for basic security checks.