I have trouble migrating from Vaadin 14 to Vaadin 23. I have a long running backgroundtask with a progress dialog. With Vaadin 14 after finishing the task i could navigate to a result-view with the following code:
UI ui = UI.getCurrent();
final SecurityContext context = SecurityContextHolder.getContext();
ui.access(() -> {
SecurityContextHolder.setContext(context);
UI.navigate("work-finished-view");
});
With Vaadin 23 i get this warning
(ViewAccessChecker.java:147) Preventing navigation to com.vaadin.flow.router.RouteNotFoundError because no HTTP request is available for checking access.
This is the code in com.vaadin.flow.server.auth.ViewAccessChecker
VaadinServletRequest vaadinServletRequest = VaadinServletRequest.getCurrent();
if (vaadinServletRequest == null) {
// This is in a background thread and we cannot access the request
// to check access
getLogger().warn("Preventing navigation to " + targetView.getName()
+ " because no HTTP request is available for checking access.");
beforeEnterEvent.rerouteToError(NotFoundException.class);
return;
}
What is the right way to navigate to another view from a background thread?
You would need create a fake request that uses the values from the security context holder and set that request using CurrentInstance.set(VaadinRequest.class, request);. Completely doable but not very nice.
Instead of writing a long and hacky example here for how it could be done, I spent the time on creating a pull request to make this work out of the box in the future: https://github.com/vaadin/flow/pull/13675
If you are using the VaadinWebSecurityConfigurerAdapter the SecurityContext should automatically be available in the UI thread. Unfortunately, the ViewAccessChecker will not make use of it either way. The way it's implemented (i.e. in a Spring independent way) it simply requires a HttpServletRequest. So I currently don't see a right way to do this (with vanilla Vaadin, anyway). You should probably create a ticket on Github because this seems like a pretty important use case.
Maybe you could redirect to an unprotected view that requires the user to click a button to navigate to the protected one.
Page#setLocation(...) would also create a new request for the security check, but that will not navigate within Vaadin and basically recreate your UI.
But both options are pretty far down on the list of acceptable workarounds, honestly.
If no one else can provide an actually reasonable workaround, you could check out my add-on: Spring Boot Security for Vaadin. It does use Spring's SecurityContext so it would work in your case.
Related
I'm using Vert.x for my web service, where a part of it required authorization. I've set an AuthenticationHandler (using the OAuth2 implementation from vertx-auth-oath2) to listen on the protected paths (lets say "/*") and it is correct called, sends a redirect to the authentication provider, which redirects back and then correctly to the real handler. This works fine.
But the next time we call the protected endpoint - it does the whole thing again. I see that in the abstract AuthenticationHandlerImpl class it checks if the context already has a user() and if so - will not run the actual auth handler, which is the behavior I need - but it obviously doesn't happen because every call is a new request with a new RoutingContext.
What is the "correct" way to retain the User object across requests, so that the auth handler will be happy?
I'm guessing it has something to do with session storage but I've never used that - up until now I was using a custom "API key" style solution, and I'm trying to do this "The Right Way(tm)" in this new project.
I'm using the latest Vert.x 4.3.5.
You will need CookieHandler and SessionHandler to store and handle session with user. This will work out of the box with provided vertx-auth-oath2.
Here is a simple example to get you started:
https://github.com/vert-x3/vertx-examples/blob/master/web-examples/src/main/java/io/vertx/example/web/auth/Server.java
Expectation:
When i attempt booking, booking has been failed due to price change and throwing below price change exception.
Then i need to capture that errors in event and redirect the request to previous page and display the error message on top of the page.
As per my below logic i can able to redirect the request to previous page, while throwing price change exception but the issue is i can't able to
display my error message (Your booking price has been changed, please choose another) on previousPage.
Anybody can help me out. Thanks in advance.
BookAction.java
public class BookAction {
private static final String PRICE_CHANGED_ERROR = "Your booking price has been changed, please choose another";
public Event book(RequestContext context) {
try {
// Booking attempt
// Consider booking attempt failed and throwing price change exception
}catch (PriceChangeException priceChangeException) {
return handlePriceChange(context);
}
}
public Event handlePriceChange(RequestContext context) {
Errors pageErrors = getPageErrors(context);
if (true) { // Consider if its true and returning new Event "searchPage"
pageErrors.reject(PRICE_CHANGED_ERROR);
return new Event(this, "searchPage");
}
return error();
}
}
booking.xml
<webflow:flow>
<webflow:action-state id="book">
<webflow:evaluate expression="bookAction.book" />
<webflow:transition on="success" to="confirm" />
<webflow:transition on="error" to="handleFrd" />
<webflow:transition on="searchPage" to="searchPage" />
</webflow:action-state>
<webflow:end-state id="searchPage" view="externalRedirect:#{flowScope.bookState.searchResultURI}" />
</webflow:flow>
Unfortunately, this is a poorly documented part of SWF. What you are looking for is to use the flashScope. flashScope was specifically designed for this purpose after a variable in the flashScope is read once it is automatically deleted.
But... the design problem in SWF is that the flashScope only works between a Parent flow and an embedded child flow inside of it. It will not hold save your variables if you use an "externalRedirect" and try to access the error using "flashScope.error"... because technically the first flow ended and a new flow has begun. So how to solve this problem?
I was only able to solve it with 2 ways and both are hacks.
Option 1.
if the message is simple then in the 'externalRedirect:#{flowScope.bookState.searchResultURI}' append a HTTP GET param such as
?msg=An Error Occurred.
Although this will look ugly in the url bar, it is simpliest and the least painful solution.
Option 2.
is a bit more painful but provides the most flexibility if you want complex error msgs. Basically you have to merge the flashScope of Spring Web Flow and the flashMap of Spring MVC and send your externalRedirect to a spring mvc controller first.
You will need to create and register a FlowExecutionListenerAdapter
to merge (hack) the flashScope and the flashMap see the link directly below for step by step instructions (ignore the stuff about sessionMap)
Spring web flow how to add flash attribute when exiting from flow to external redirect
Then once you achieve that you can easily access your flash variable error msg using Spring MVC like so:
How to read flash attributes after redirection in Spring MVC 3.1?
Note: the variable will already be in the flashMap of spring mvc. So all you have to do is render a view in spring mvc and access the variable from the view.
My 2 cents: because of limitations like this I've personally stopped using webflow and only use Spring MVC exclusively now. I think Spring Web flow is still useful for very simple use cases (flow A -> flow B -> flow C) but if you're trying to to do something more complex or are trying to learn SWF from scratch right now... my advice to you is to just use Spring MVC... it will save you a lot of time in the long run. I think they are working on a major revision for SWF (3.0) maybe then all these limitations will get worked out
Indeed Spring Web Flow has a number of scopes where variables can be stored but when a flow ends all scopes end as well, unless the flow is a child flow delegating control back to the parent flow in which case you can use flash scope in addition to any parent flow scoped variables.
Spring Web Flow also knows how to interact with the Spring MVC flash scope. So when a top-level flow ends and the redirect is to a Spring MVC endpoint, there is a way to indicate that output variables should be put in Spring MVC flash scope, which would make them available after the redirect.
This is mentioned in the reference documentation http://docs.spring.io/autorepo/docs/webflow/current/reference/html/spring-mvc.html#spring-mvc-flash-output.
in booking.xml, add below changes
<webflow:transition on="searchPage" to="searchPage">
<set name="flowScope.error" value="'text u wanna show in next page'" type="string" />
</webflow:transition
And now in the redirected page try reading that text. hope it would work!
Use flash scope:
In xml
<set name="flashScope.flashScopeAttribute" value="'f'" />
In JSP
flash scope: #{flashScopeAttribute}
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.
I'm working to develop a multi-tenant Play Framework 2.1 application. I intend to override the onRequest method of the GlobalSettings class to load and set a custom configuration based on the subdomain of the request. Problem is, I don't see how this would be possible in Play 2.x.
I can override system properties at the command line when starting the server, but how can I do this programmatically in Java code for each request?
The code would look something like this (I assume):
#Override
public play.mvc.Action onRequest(Request request, Method actionMethod) {
//Look up configuration settings in Cache based on request subdomain
//(i.e. Cache.get("subdomain.conf"))
//if not in cache:
//load appropriate configuration file for this subdomain (java.io.File)
//set new configuration from file for this request
//cache the configuration for future use in a new thread
//else
//set configuration from cache for this request
return super.onRequest(request, actionMethod);
}
}
Looking up the URL and getting/setting the cache is easy, but I cannot figure out how to SET a new configuration programmatically for Play Framework 2.1 and the documentation is a little light on things like this.
Any thoughts? Anyone know a better, more efficient way to do this?
So, in a sort of roundabout way, I created the basis for a multi-tenant Play application using a Scala Global. There may be a more efficient way to implement this using a filter, but I'm finding this seems to work so far. This does not appear to be as easily implemented in Java.
Instead of using the configuration file, I'm using the database. I assume it would be far more efficient to use a key-value cache, but this seems to work for now.
In Global.scala:
object Global extends GlobalSettings {
override def onRouteRequest(request: RequestHeader): Option[Handler] = {
if (request.session.get("site").isEmpty){
val id = models.Site.getSiteIDFromURL(request.host)
request.session.+("site" -> id)
}
super.onRouteRequest(request)
}
}
And then, obviously, you have to create a database model to query the site based on the request domain and/or the session value set in the request. If anyone knows a better way I'd love to hear it.
I'm using JSF (Mojarra 1.2) with Richfaces (3.3.2) within some facelets which are used as portlets (I'm using the Jboss Portlet Bridge 2.0 here). Now I'm facing something strange: I've got an actionlistener on my <h:commandButton> which is triggered, when the button is clicked but when I simply reload the page, the action is executed everytime I load the page again. This happens only if I already triggered the action before. Is this behaviour normal?
I should notice that Spring 2.5 is used to manage my beans, the mentioned beans are session-scope beans, maybe this is a interessting point?!
Yes, reloading a HTTP POST request will execute the HTTP POST request again and thus trigger all associated server-side actions again. This issue affects all webapplications in general and is not per se related to JSF.
A well known fix to this is the POST-Redirect-GET (PRG) pattern. Basically you need to redirect the POST request to a GET request immediately after processing the action, so that the result page will be delivered by a HTTP GET request. Refreshing this HTTP GET request won't execute the initial HTTP POST request anymore.
This pattern has however one caveat: since it concerns a brand new request, all request scoped beans are garbaged and renewed in the new request. So if you'd like to retain the data in the new request, you would need to either pass them as GET parameters or to store it in the session scope. Usually just reloading the data in bean's constructor is sufficient. But since you mention to use session scoped beans only (which is however not the best practice, but this aside), this shouldn't be a big concern for you.
Turning on PRG in JSF is relatively easy, just add the following entry to the associated <navigation-case>:
<redirect />
Or if you prefer to fire it programmatically, then make use of ExternalContext#redirect() in the bean's action method:
public void submit(ActionEvent event) {
// ...
FacesContext.getCurrentInstance().getExternalContext().redirect(someURL);
}