I am using Primefaces DialogFramework with
My problem is, that if the user knows the location of my dialog, he is able to access it directly via the URL. I do not want that to be possible, so I thought it would be able to put the dialog in WEB-INF folder of my web-app, but now, if I want to open the dialog, I get a FileNotFound-Exception.
If my dialog is located in some regular folder, it works fine
RequestContext.getCurrentInstance().openDialog("/myfolder/mydialog");
// this works as expected
but if it is located in WEB-INF, it does not work any longer
RequestContext.getCurrentInstance().openDialog("/WEB-INF/mydialog",options,null);
// this is causing a fileNotFoundException
I also tried to set up a navigation rule for this in faces-config
but again with no success
<navigation-case>
<from-outcome>mydialog</from-outcome>
<to-view-id>/WEB-INF/mydialog.xhtml</to-view-id>
<redirect />
</navigation-case>
How may I open dialogs located in WEB-INF folder, or is it not possible at all? Thanks in advance
Unfortunately, putting PrimeFaces Dialog Framework dialogs in /WEB-INF
in order to prevent direct access is indeed not going to work. The dialogs are loaded entirely client side. On the POST request which opens the dialog, JSF/PrimeFaces returns an oncomplete
script with the (public!) URL of the dialog to JavaScript/jQuery, which in turn shows a basic dialog template with an <iframe>
whose URL is set to the dialog URL, which in turn loads the content. In effects, 2 requests are being sent, the first to get the dialog's URL and the second to get the dialog's content based on that URL in the <iframe>
.
There's no way to keep the dialog in /WEB-INF
without falling back to the "traditional" dialog approach via <p:dialog>
and conditional display via JS/CSS. There's also no way in the server side to verify based on some headers if the request is coming from an <iframe>
, so that all others could simply be blocked. Your closest bet is the referer
header, but this can be spoofed.
One way to minimize abuse is checking the presence of pfdlgcid
request parameter (identified by Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM
) when a dialog is being requested. PrimeFaces namely appends this request parameter representing "conversation ID" to the dialog URL. Presuming that all dialogs are stored in a folder /dialogs
, then you could do the job with a simple servlet filter. Here's a kickoff example which sends a HTTP 400 error when /dialogs/*
is being requested without the pfdlgcid
request parameter.
@WebFilter("/dialogs/*")
public class DialogFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String id = request.getParameter(Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM);
if (id != null) {
chain.doFilter(req, res); // Okay, just continue request.
}
else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST); // 400 error.
}
}
// ...
}
However, the abuser might not be that stupid and discover the pfdlgcid
request parameter during the normal flow and still be able to open the dialog individually when supplying that parameter, even with a random value. I thought of comparing the actual pfdlgcid
value to the known ones. I checked the PrimeFaces DialogNavigationHandler
source code, but unfortunately, PrimeFaces doesn't store this value anywhere in the session. You'd need to provide a custom DialogNavigationHandler
implementation wherein you store the pfdlgcid
value in the session map which in turn is also compared in the servlet filter.
First add the following method to the DialogFilter
:
public static Set<String> getIds(HttpServletRequest request) {
HttpSession session = request.getSession();
Set<String> ids = (Set<String>) session.getAttribute(getClass().getName());
if (ids == null) {
ids = new HashSet<>();
session.setAttribute(getClass().getName(), ids);
}
return ids;
}
Then copypaste the PrimeFaces DialogNavigationHandler
source code into your own package and add the following line after line 62:
DialogFilter.getIds((HttpServletRequest) context.getExternalContext().getRequest()).add(pfdlgcid);
Replace the <navigation-handler>
in faces-config.xml
with the customized one.
Finally, alter the if
condition in the DialogFilter#doFilter()
method as follows:
if (getIds(request).contains(id)) {
// ...
}
Now, this prevents the abuser from attempting to open the dialog with a random ID. This however doesn't prevent the abuser from attempting to open the dialog by copypasting the exact <iframe>
URL immediately after opening it. Given the way how the PrimeFaces dialog framework works, there's no way to prevent that. You could at most remove the pfdlgcid
value from the session when the dialog is about to returns to the parent. However, when the dialog is closed by pure JS means, then this is also bypassed.
All in all, if you really, really, want to avoid the enduser being able to open the dialog individually, then you can't go around the "traditional" <p:dialog>
approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With