JSF inputFile Refused to display 'mypage.xhtml' in a frame because it set 'X-Frame-Options' to 'deny' - jsf

I was following this answer by BalusC to try and upload a file to the server. I am using his code as-is.
When using JSF 2.2, the #{bean.save} was never reached, and the file was never saved.
The server's console showed nothing. But the js console showed this error:
Refused to display 'http://localhost:8080/my_app/hello.xhtml' in a frame because it set 'X-Frame-Options' to 'deny'.
jsf.js.xhtml?ln=javax.faces:1 Uncaught DOMException: Blocked a frame with origin "http://localhost:8080" from accessing a cross-origin frame.
at FrameTransport.callback (http://localhost:8080/my_app/javax.faces.resource/jsf.js.xhtml?ln=javax.faces:1:5109)
at HTMLIFrameElement.<anonymous> (http://localhost:8080/my_app/javax.faces.resource/jsf.js.xhtml?ln=javax.faces:1:5759)
I saw this answer which suggested it was a bug in JSF 2.2. So I uploaded to 2.3.
With JSF 2.3 the #{bean.save} is reached, and the file is successfully saved. But the js error remains, and I can't upload a second file.
Any ideas?
EDIT in case it helps: I don't know why, but after selecting the file to upload in the dialog, an <iframe> is added to my page somehow.
EDIT 2
BalusC and Selaron suggested I try to change the X-Frame-Options header to not 'DENY'. I tried adding a #WebFilter and setting the header there, like this:
public void doFilter(...)
{
HttpServletResponse response = (HttpServletResponse) res;
response.addHeader("X-Frame-Options", "sameorigin");
response.setHeader("MyHeader", "whatever");
chain.doFilter(req, res);
}
I added a second header MyHeader with value "whatever" to check if the response contained that header when getting to the browser.
Turns out MyHeader gets to the browser correctly, but X-Frame-Options still remains as 'DENY'.
As I'm using Spring Security, I figured maybe there was some other filter messing with my response?
So, I have this:
#Configuration
#EnableWebSecurity
public class BasicConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.addFilterAfter(
new CustomFilter(), SwitchUserFilter.class);
...
}
}
My CustomFilter works as the previous one I showed: MyHeader remains, but X-Frame-Options does not.
I added it after SwitchUserFilter because the doc for HttpSecurity.addFilter says that is the last filter in the chain.
I am a bit lost now. My couple of questions:
Am I right to assume the X-Frame-Options header is getting overwritten by some other filter?
How could I ensure the X-Frame-Options I set remains? Or, how can I put my filter at the end of the chain?

I found this issue where the Mojarra team planned to Implement "ajax" file upload #2577 and the commit actually implementing it to the jsf javascript.
Madly the documentation on issue 2577 are not accessible anymore and thus it does not explain the background on why an iframe is needed here.
The first passage of this blog gives a brief explanation on why AJAX file upload is/was(?) not possible directly:
Ajax Style File Uploading using Hidden iFrame
by Viral Patel · November 18, 2008
File uploading using AJAX is not possible. AJAX doesn’t actually post
forms to the server, it sends selected data to the server in the form
of a POST or GET request. As javascript is not capable of grabbing the
file from the users machine and sending it to the server, it’s just
not possible with AJAX. You have to resort to regular old form submit.
If you have read/seen it somewhere, then it is not through AJAX. File
uploading occurs through an iframe in this case. You have to use a
iframe to upload the files. So, you can use iframe to asynchronous
upload (Like AJAX , but its not AJAX).
So finally your options are to either - as BalusC commented - relax your X-Frame-Options header setting or to change your upload to not use AJAX:
<h:form enctype="multipart/form-data">
<h:inputFile value="#{bean.file}" />
<h:commandButton value="upload" action="#{bean.save}"/>
</h:form>

Related

#PreDestroy method not called when leaving page of bean annotated with OmniFaces "ViewScoped"

I am trying to invoke a method annotated with #PreDestroy in a #ViewScoped bean when the user leaves the page associated with that bean in a rather large JSF powered web application.
After reading https://stackoverflow.com/a/15391453/5467214 and several other questions and answers on SO as well as https://showcase.omnifaces.org/cdi/ViewScoped, I came to the understanding that the OmniFaces ViewScoped annotation provides exactly that behavior by utilizing the unload page event as well as sendBeacon on modern browsers.
So I used the #ViewScoped annotation from OmniFaces in my bean:
import javax.annotation.PreDestroy;
import org.omnifaces.cdi.ViewScoped;
#Named("DesktopForm")
#ViewScoped
public class DesktopForm implements Serializable {
...
}
and annotated the method I want to invoke with the PreDestroy annotation:
#PreDestroy
public void close() {
System.out.println("Destroying view scoped desktop bean");
...
}
Unfortunately, this "close" method is not called when I click some link or leave the page
by entering an entirely new URL. Instead, the network analysis of my browser (a current Firefox) shows me that a POST request is send when leaving the page that returns with an 403 http error code:
As you can see in the screenshot, the "Initiator" of the POST request seems to be an unload.js.jsf script with a beacon mentioned in parentheses, which I assume is part of the OmniFaces library. So presumably the functionality described in the OmniFaces ViewScoped documentation is somehow triggered, but does not result in the expected behavior for me.
The browser still navigates to the new page, but the PreDestroy annotated method was not triggered. When I switch to the standard version of ViewScoped (javax.faces.view.ViewScoped instead of org.omnifaces.cdi.ViewScoped), naturally the method still does not get invoked, but there is also no POST method resulting in a 403 error status when leaving the page in the network analysis of my browser (because the standard ViewScoped annotation of Java does not try to invoke any bean side action on unload events, I guess)
I am using MyFaces 2.3.10 in combination with OmniFaces 2.7.18 (and PrimeFaces 8.0.5, I don't know if that is relevant), Spring Security 5.7.3 and Java 11.
Since "403" is the http status for "forbidden", could this have something to do with using "http" instead of "https" in my local development environment? Does this "send beacon" only work with secure connections?
Any help appreciated!
Edit: I also consulted the official documentation of the OmniFaces ViewScoped annotation under https://omnifaces.org/docs/javadoc/2.7/index.html?org/omnifaces/cdi/ViewScoped.html but could not find a reason for the problem I encounter.
With the help of BalusC's comment to my question above, I was able to solve my problem.
What it came down to was that unload events were not processed correctly by our filter chain. Specifically, they were denied access in the doFilter method of our class extending org.springframework.web.filter.GenericFilterBean.
Therefore I added
if (ViewScopeManager.isUnloadRequest(httpServletRequest)) {
chain.doFilter(request, response);
}
to the doFilter method of the mentioned class and then it worked.
On a side note, I had to update my OmniFaces library from 2.7.18 to 3.13.3, because the ViewScopeManager class of OmniFaces 2 only has one isUnloadRequest method that accepts an FacesContext as parameter, which I did not have available in the our GenericFilterBean extension. OmniFaces 3.1 on the other hand provides another method with the same name that works with an HttpServletRequest instance instead, which I had access to and therefore resolved the issue

Primefaces FileUpload: handle firewall error blocking upload

We have a JSF application that is currently working and live that uses Primefaces 7.0. The application allows users to upload files using the Primefaces FileUpload component which we then handle and process.
The firewall team is forcing a firewall to sit in front of the upload process which will block upload requests from hitting the application if it deems the file to not be suitable and return a 503 error to the browser. I am struggling to work out how we can handle the 503 error within the JSF application as the request never hits the server and I can't find a suitable attribute we can add to the FileUpload component that would listen for the 503 (tried using onerror as shown in code below but unfortunately it doesn't get called when the 503 error occurs). Without handling this error, nothing is presented to the user to suggest anything happened. Ideally we would present a growl message to the user asking them to try a different file type.
Below is the code currently for the FileUpload component however I'm not sure if it's any use as I'm looking more for advice than a code fix.
Anyone have any idea on how to potentially handle the 503?
Thanks
<p:fileUpload widgetVar="upload" id="upload"
disabled="#{user.filesRemaining eq 0}" label="SELECT FILES"
fileUploadListener="#{controller.handleFileUpload}"
styleclass="smallCommandButton" mode="advanced"
dragDropSupport="true" auto="true"
allowTypes="/(\.|\/)(jpeg|jpe|jpg|png|pdf)$/i" sizeLimit="10485760"
invalidSizeMessage="Image files must be under 10MB in size"
invalidFileMessage="Only JPG, PNG or PDF files can be uploaded"
onerror="#{controller.handleUploadError()}"
onstart="PF('uiBlocker').show()"
oncomplete="PF('uiBlocker').hide()">
</p:fileUpload>
Found a solution, I needed to add the below code to my fileUpload component, 'arguments' carries the information for the error:
onerror="handleUploadError(arguments)"
and then create the corresponding javascript function to handle the error e.g.
function handleUploadError(arguments) {
if(arguments[0].status === 503) {
**create message for user here**
}
}

JSF 2.0 Multiple requests generated per page

I have implemented a Filter for checking if a user is logged in or not by checking the session for a #SessionScoped bean. When I started testing it, I however, noticed that whenever I accessed one of my pages the Filter would be invoked multiple times.
I have figured out that I needed to ignore AJAX requests which reduced the number of times my filter would be invoked but the number of requests triggered each time I loaded the page was still more than one.
By trial and error, I have figured out that the requests would be generated by the following XHTML tags (both embedded in the <h:body> tag):
<h:outputStylesheet name="styles/userbar.css" target="head"/>
<o:commandScript name="updateMessages" render="custom_messages"/>
The second tag being part of OmniFaces library.
Any ideas why I get multiple requests or maybe if there is a way to ignore the requests generated by these tags?
Any help would be appreciated.
That can happen if you mapped the filter on a generic URL pattern like #WebFilter("/*"), or directly on the faces servlet like #WebFilter(servletNames="facesServlet"). The requests you're referring to are just coming from (auto-included) CSS/JS/image resources. If you track the browser's builtin HTTP traffic monitor (press F12, Network) or debug the request URI in filter, then that should have become clear quickly.
As to covering JSF resource requests, if changing the filter to listen on a more specific URL pattern like #WebFilter("/app/*") is not possible for some reason, then you'd need to add an additional check on the request URI. Given that you're using OmniFaces, you can use the Servlets utility class to check in a filter if the current request is a JSF ajax request or a JSF resource request:
#WebFilter("/*")
public class YourFilter extends HttpFilter {
#Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws IOException, ServletException {
if (Servlets.isFacesAjaxRequest(request) || Servlets.isFacesResourceRequest(request)) {
chain.doFilter(request, response);
return;
}
// ...
}
}
See also:
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same (contains a "plain vanilla" Servlet example for the case you aren't using OmniFaces)

ViewExpiredException with tracking mode URL in Glassfish3

our customer doesn't want to have session handling with cookies and it also will cause problems with an Apache/mod_rewrite gateway, so i tried to use
<tracking-mode>URL</tracking-mode>
in our web.xml. That should be all with Glassfish3/Servlet 3.0. However now i get ViewExpiredExceptions when trying to log in(it's not an AJAX request):
<p:commandButton id="submit"
value="${msg['Login.submit.label']}"
action="#{loginBean.login}"
ajax="false"/>
I also tried to save the session on the client side, than i can see the JSESSIONID in the URL but that throws NotSerializableExceptions for my #EJBs. Any ideas? Do i miss something? It used to work fine with the cookies.
UPDATE: LoginBean.login returns "Home.xhtml?faces-redirect=true", expected behaviour when clicking the commandButton: POST on Login.xhtml, my login page, redirect and GET on Home.xhtml.
SECOND UPDATE:
Looks like my action never gets called, i'm directly getting the ViewExpiredException and a HTTP 500 error code.
THIRD UPDATE:
Looks like the HttpSession is always null with tracking mode set to URL, with cookies the HttpSession is correctly created. Shouldn't the FacesServlet create a session and append the JSESSIONID in the URL if there is no session?
ANOTHER UPDATE:
With
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
the session will be created on postback. But than i'm running into
java.io.NotSerializableException
.
The other option is to set restore view compability to true.
Edit your web.xml and add following code and try.
<context-param>
<param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
<param-value>true</param-value>
</context-param>
Updated:
Reference
com.sun.faces.enableRestoreView11Compatibility is a JSF 1.2 setting that tells JSF 1.2 to behave like JSF 1.1.
com.sun.faces.enableRestoreView11Compatibility == true means "do not throw a ViewExpiredException; instead, just create a new view if the old one has expired."
The IBM notes on the JSF 1.1 behaviour say:
This can have adverse behaviors because it is a new view, and items that are usually in the view, such as state, are no longer be there.
The default JSF 1.2 behaviour is defined in the spec as this:
If the request is a postback, call ViewHandler.restoreView(), passing the FacesContext instance for the current request and the view identifier, and returning a UIViewRoot for the restored view. If the return from ViewHandler.restoreView() is null, throw a ViewExpiredException with an appropriate error message. javax.faces.application.ViewExpiredException is a FacesException` that must be thrown to signal to the application that the expected view was not returned for the view identifier. An application may choose to perform some action based on this exception.
To have a ViewExpiredException thrown when the view expires, remove the com.sun.faces.enableRestoreView11Compatibility parameter or set it to false.
The com.sun namespace suggests that the parameter is a Sun/Mojarra and derived implementation-specific setting, so it probably will not work with all JSF implementations.
Fixed by updating Mojarra. My Glassfish 3.1.2.2 came with Mojarra 2.1.6 and this bug:
https://java.net/jira/browse/JAVASERVERFACES-2143
Updated to 2.1.22 and everything works.

Using Post when doing a sendRedirect

I have a requirement that a jsf page needs to be launched from a custom thin client in user browser (thin client does a http post). Since the jsf page may be invoked multiple times, I use a jsp to redirect to the jsf page with params on url. In jsp I do a session invalidation. The jsp code is below:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%#page session="true" %>
<%
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
session.invalidate();
String outcome = request.getParameter("outcome");
String queryString = "outcome=" + outcome ;
response.sendRedirect("./faces/MyPage.jspx?" + queryString);
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"></meta>
<title>title here</title>
</head>
<body></body>
</html>
My jsf page uses mutiple drop down items with autoSubmit enabled. I set the params on session when the form first inits and then use it from there when an action button is ultimately clicked by user. Following is the code I use to get the param in jsf backing bean constructor:
FacesContext ctx = getFacesContext();
Map sessionState = ctx.getExternalContext().getSessionMap();
outcome = (String)sessionState.get("outcome");
if(outcome == null) //if not on session, get from url
outcome = (String)JSFUtils.getManagedBeanValue("param.outcome");
This way I can get the param even after multiple autoSubmits of drop downs.
My problem is that I cannot have parameters show up on browser address bar. I need a way so that the parameters can be passed to the jsp page. I cannot post direct to jsp from thin client since I need the jsf page to have a new session each time the client launches user browser. This is imp due to the above code snippet on how I use params in the jsf page.
Is there nay way to use a post when doing a sendRedirect so that I do not have to pass params on url? I cannot use forward since when an autoSubmit fires on jsf page, it causes the browser to refresh the jsp page instead.
Is there any better way to handle the jsf page itself so that I don't have to rely on storing params on session between successive autoSubmit events?
Since you invalidate the session, no, you cannot.
The only way would be putting it in the session scope. Why are you by the way invalidating the session on first request? This makes really no sense.
Unrelated to your actual problem, doing sendRedirect() in a scriptlet instead of a Filter and having a bunch of HTML in the same JSP page is receipt for big trouble. Do not write raw Java code in JSP files, you don't want to have that. Java code belongs in Java classes. Use taglibs/EL in JSP only.
No. Because sendRedirect send the web-browser/client a 302 with the new location and according to the following it will usually be a GET, no matter what the original request was.
According to HTTP/1.1 RFC 2616 http://www.ietf.org/rfc/rfc2616.txt:
If the 302 status code is received in response to a request other
than GET or HEAD, **the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user**, since this might
change the conditions under which the request was issued.
Note: RFC 1945 and RFC 2068 specify that the client is not allowed
to change the method on the redirected request. However, ***most
existing user agent implementations treat 302 as if it were a 303
response, performing a GET on the Location field-value regardless
of the original request method***. The status codes 303 and 307 have
been added for servers that wish to make unambiguously clear which
kind of reaction is expected of the client.
Maybe playing carefully with ajax can get you something.
Update: Then again, as user: BalusC pointed out, you can redirect by manually setting the headers, as in:
response.setStatus(307);
response.setHeader("Location", "http://google.com");

Resources