Since you guys have been very helpful in my early steps into the Play Framework (thanks for that), here it goes again:
We have a working registration controller, that POSTS all credentials to the database.
But then, we want to make it possible to be immeadiately logged in afterwards. Below is the code that makes this work:
public static void doRegistration(#Valid User user) {
//registering the user
try{
SecureController.authenticate(user.username, user.password, false, "MainController.index");
}catch(Throwable ex){
MainController.index();
}
This works fine, but it is not very safe because it GETs all the credentials to the server. I know I have to edit my routes file somehow, but I can't see how.
The routes file:
* /account SecureController.login
POST /account/register RegistrationController.doRegistration
GET /account/register SecureController.login
Somewhere should be the action SecureController.authenticate, but what do I have to put in the column after the POST... It can't be /account/register, because that fails...
Thank you beforehand!
I am not sure I understand your issue. The routes file is just a way to configure your URLs to be pretty URLs. If you don't specify them, then it falls back on default {controller}/{method} syntax.
The issue you are having, is that when you call another controller Play performs a redirect to that controller's method, which involves sending a request back to your browser telling it to redirect (this ensures that the state of the application is reflected in the URL within the browser). A redirect needs therefore to send a GET request, and included in the GET request will be your parameters.
what you are trying to do, as you said, is not safe. What you should do (not the only option, only one possibility) is:
Maintain your current doRegistration action for the user
Create a service class (that does not inherit Controller). It can be static or require instantiation (with static methods should be enough though).
Add a #Before method to a common controller that will be executed always. One way is to create a controller with a #Before method and add this controller to all other controllers via the #With annotation, so that #Before will be executed always for all controllers. It requires you to add a #With to each new controller, but I believe it keeps the code quite clean.
The idea would be that the controller calls the authenticate method from the service class. It's a simple static This method checks the user (if it's enabled, has proper license, whatever) and sets some parameters in the session (via Session object).
To help with this you may want to create another authenticate method in the user that returns the attributes to set (for example in a Map, if it contains an "error" key the user can't be authenticated for some reason). How to do this step can change according to your requirements.
Once the Session has been set, you redirect to the page of your election (main, profile, etc). As you have the common #Before method, this will be executed. This method should verify the credentials in the session (user authenticated, license type, etc) and act accordingly. You have an example in the Secure controller of Play, but you could create your own.
With this, you could user the authenticate method of the service from any controller, allowing authentication via multiple methods, and using a common point to verify the session.
Related
Here a solution is described to handle redirects to a custom URL based on a condition via use of AccessStrategy.
This however is part of the unauthorized login logical flow therefore results into a still not-logged in user arriving at the end url we redirect to. (via getUnauthorizedUrl)
If we want to redirect the user based on a condition, say via injecting an action to the webflow, how can we manipulate the return URL to be changed into a custom one?
WebUtils.getService(requestContext) include getters of the source/originalUrl but no obvious way to set/manipulate said value through an action bean.
p.s. Currently using CAS version 5.3.x
Responses for normal web applications from CAS are built using WebApplicationServiceResponseBuilder.
If you examine this block you will find that the final response is built using WebApplicationServiceResponseBuilder bean. It is only created conditionally, if an existing bean is not already found in the context by the same name. So to provide your own, you just need to register a bean with the same name using your own #Configuration class.
#Bean
public ResponseBuilder<WebApplicationService> webApplicationServiceResponseBuilder() {
return new MyOwnWebApplicationServiceResponseBuilder(...);
}
...and then proceed to design your own MyOwnWebApplicationServiceResponseBuilder, perhaps even by extending WebApplicationServiceResponseBuilder and overriding what you need where necessary to build the final redirect logic conditionally.
To learn about how #Configuration classes work in general, you can:
Review this post
or this post
or consult the documentation for Spring and/or Spring Boot.
I've a requirement to build a Java based web application where a resource should be available only when all the authorized users of that resource are logged in. Also, if any authorized user logs out, the resource should no longer be available to any of them.
The resource could be of any type(html pages, pdf documents, spread sheets etc.,)
Is there any existing authentication standards/protocols that supports this type of requirement or I've to build this from scratch?
the resource should be available only when all the authorized users of that resource are logged in. Also, if any authorized user logs out, the resource should no longer be available to any of them.
Once you have given access to the resource to an user, this user will be able to download / take screenshots / save / record the resource, no matter if it's a PDF document, an image, an audio file. I don't know the context and the goal of what you're trying to build, but you should know that it will be insecure in any case.
Even putting this consideration aside, you'll need a real-time solution. Once the user has loaded the page containing the resource, you need to be able to hide or deny modification rights to him. This means you have to use something like WebSockets or Ajax Polling on the client side to have the frontend know when your server considers that not all the required users are online, and that the access to the resource should be "denied". But once more since this is client-side code it can easily be changed or altered, the requests it is sending can easily be blocked by the user, so it is once again inherently insecure.
I'd suggest giving a little bit of context here and describing what is the problem you're trying to solve, because most likely there's a more reasonable solution to solve it.
If what you need to do is to deny modification rights if not all the "resource owners" are online, it is more easily doable since the modifications will happen on the server side. In this case, a solution using WebSockets could quite easily be implemented but I don't know a library or framework that does such a thing. Most likely you will have to build it yourself.
If you're not constrained to use a specific web framework, feel free to try the following filter based implementation for jersey. Note that you still need to add a fair amount of custom code for handling the logic of "Collective authentication" as jersey only provides the basic tools required for this, and it doesn't explicitly implement the whole concept. Here's how you could do it, on a high level:
class AuthorizationProvider {
public void authenticate(ContainerRequestContext requestContext) {
// Here you would need to query your database to get the Collection of Users belonging
// to the "Collective" Role. You would then check if they are all logged in.
// A really abstract version would look like this, assuming you've already queried the DB
// and have a reference to the above mentioned Collection.
if (collectiveUsers.size == collectiveUsers.stream().filter(User::isLoggedIn).count()) {
return true;
}
return false;
}
}
class AuthorizationRequestFilter implements ContainerRequestFilter {
private final AuthorizationProvider authorizationProvider;
#Override
public void filter(ContainerRequestContext requestContext) {
if (authorizationProvider.authenticate(requestContext)) {
// serve whatever it is you want to serve if all required users are logged in
} else {
// otherwise reject the request
requestContext.abortWith(Response
.status(Response.Status.UNAUTHORIZED)
.entity("Resource available only after collective login")
.build());
}
}
}
#ApplicationPath("/")
class MyApplication extends ResourceConfig {
public MyApplication() {
// Register the filter
register(AuthorizationRequestFilter.class);
}
}
Apart from this, you would also need to handle the Login part.
You would assign these specific users the Collective role, and you would mark them as logged in, whenever they successfully pass through login authentication.
If all the above conditions are met, you should be able to successfully serve your "Collective only" page, only when all "Collective" users are logged in.
This also covers the part where if either one of these users logs out, you store the state in your database (mark the Collective user with isLoggedIn = false). So from this point on, whenever somebody requests the page, it will return Unauthorized.
Conversely, you can also attempt to implement SSE (Server sent events) to actively update the frontend part, if somebody logs out. With this, the page will actively be disabled even if somebody has already managed to get it previously.
Container request filter source and example, for reference, jersey docs
I have an Spring-mvc application and in each controller I add a form to SessionAttributes to preserve properties when save, delete or do another get request. Main problem becomes when I try to open some link in another browser tab and try to submit the first one. I tried this solution but when I do a redirect (in controller I only have 1 return for view and the other methods do a redirect) it creates a new conversation and can't find previous one.
I have another question about this triying to use spring-session, question It's here but I don't know if this will work too.
Did you look into Spring's RedirectAttributes? I haven't used it myself but it sounds like it should do what you would like. RedirectAttributes is typically used for GET/redirect/POST patterns and at least one user seems to think passing session attributes this way is bad practice, however they go on to mention there doesn't seem to be a better solution. Anyway, the example shown in the documentation:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return "accounts/new";
}
// Save account ...
redirectAttrs.addAttribute("id", account.getId()).addFlashAttribute("message", "Account created!");
return "redirect:/accounts/{id}";
}
would add the "message" attribute to a RedirectModel, and if your controller redirects, then whatever method handles the redirect can access that data like so:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handleRedirect(Model model) {
String message = (String) model.asMap().get("message");
return new ModelAndView();
}
So adding session attributes should be possible in the same way. Another reference here.
EDIT
I was looking through the Spring documentation and they also mention this annotation #SessionAttributes. From the documentation:
The type-level #SessionAttributes annotation declares session attributes used by a specific handler. This will typically list the names of model attributes or types of model attributes which should be transparently stored in the session or some conversational storage, serving as form-backing beans between subsequent requests.
Could this be what you need?
And also a link to documentation on flash attributes.
This is the solution we have come up with, nothing to do with Spring:
On each html form of your application you will have to include a hidden field. Let's name this field CSRF_TOKEN. This field should have a randomly generated value. This value is placed both in the session and the hidden field. The name of the session attribute is SESSION_CSRF_TOKEN
When the form is submitted to the server, you check whether the value in the session (SESSION_CSRF_TOKEN) equals the value sent in the HTTP request parameter CSRF_TOKEN. If not, you show some kind of error message and you stop processing. If they are equal, proceed.
If the user opens a new tab or duplicates a tab, the server will re-render the page and a new CSRF_TOKEN will be generated. So the user will only be able to submit the form from the newly opened tab , and not from the original.
This solution offers an additional bonus: It protects from CSRF attacks.
I have a web app that uses a Java Bean for login functions right now and all of the JSP pages check the Bean to make sure the user is logged in. I am also introducing some AJAX functionality now with servlets and I see that of course those exchanges don't check authentication. I'm wondering how I should handle this. For example, I don't want someone to be able to logout, hit back button, then submit something with the AJAX functions successfully.
I can't access the bean from the servlet to check the login (totally wrong context and static vs non-static). I guess I could set a flag with the user entry in the database table denoting logged in or not. Then I can detect timeout logoffs and update the flag as well. But that way would require extra database accesses every time something is done. It would duplicate functionality in some way, but I guess I could perhaps use that just for the AJAX stuff. One difference with that would be the user would not be able to be logged in on multiple places at once as currently.
How is this kind of thing normally done?
Thanks for any help!
You could use session to store that flag instead of the database, and when the user logs out you should remove that flag and destroy the session. In login method
HttpSession session = req.getSession(true);
session.setAttribute("loggedIn",true)
And in your AJAX code
if(eq.getSession(true).getAttribute("loggedIn")==true)
doWork();
else
error("not logged in");
The webcontainer will handle timeouts for you, keep track of each user and his session, and so on.
But I would recommend that you use a standard for managing authntication
Basically the problem is this:
There is a stored database procedure that takes a username as an argument and produces some XML data depending on it. It is called by a method with no arguments in an unsecured web service (let's call that web service WSA). There is also another web service (let's call it WSB) which is supposed to call WSA. In this setup, WSA should only ever be called by WSB and never by anyone else. WSB is what users call and it is the way they get the required XML data. The web services are deployed on OC4J, and they have security enabled on them. WSB is secured by OC4J and is accessed by providing the username and password of an OC4J user.
When testing a web service, OC4J provides you with a form where you can enter login information prior to invoking a web service. If you select to include security info in the header and preview the message before invoking the service, the username and password are in the message.
My problem is that I can't get the security information (or at least the username) to reach the endpoint implementation and invocation of the stored procedure.
So far I have created WSA, made a web service proxy that refers to it, and created WSB based on the proxy.
What I have tried so far to get the username (and why it doesn't work):
Had WSA implement javax.xml.rpc.server.ServiceLifecycle. This provides WSA with an instance of javax.xml.rpc.server.ServletEndpointContext, which provides me with a java.security.Principal. However, that Principal is null if I call WSB (which in turn calls WSA). If I secure WSA and call it directly, the Pricipal is not null and contains the user (but it doesn't solve the problem, because I need to call WSB, not WSA).
Created handlers (extending javax.xml.rpc.handler.GenericHandler) for both services, which were supposed to be able to process the message. One thing really baffled me here. The handler methods get called correctly - the WSB handler handles the request, then the WSA handler handles the request, then the WSA handler handles the response and finally the WSB handler handles the response. But when I tried printing the messages to a file on each step, I found out that even at the first step (when WSB handles the request) there is no security information in the message. No username, no nothing. The message is in fact quite different from what is shown on the invocation page when previewing the request message before invoking the service.
Tried injecting an instance of WebServiceContext by using the #Resource annotation, but apparently OC4J doesn't support this.
If anyone can shed some light on where I might be doing something wrong, I would be very thankful.
The problem is that "WSA is called by a method with no arguments in an unsecured web service". So, there is no security context for WSA to pick-up the user id from...
The simplest fix might be to change the WSA API to accept a user id in the request parameters.
HTH
Tom