Skip to content

Commit

Permalink
try to workaround mojarra encodeRedirectURL bug
Browse files Browse the repository at this point in the history
  • Loading branch information
tandraschko committed Feb 28, 2024
1 parent 80a0995 commit 5c762b6
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@
*/
package org.apache.deltaspike.jsf.impl.clientwindow;

import java.io.IOException;
import java.io.OutputStream;
import jakarta.faces.FacesException;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.Map;
import org.apache.deltaspike.jsf.impl.util.ClientWindowHelper;
import org.apache.deltaspike.jsf.impl.util.JsfUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Map;

public class ClientSideClientWindow extends DeltaSpikeClientWindow
{
/**
Expand Down Expand Up @@ -81,7 +82,7 @@ else if (isNoscriptRequest(facesContext.getExternalContext()))
if (id == null || newWindowIdRequested)
{
// GET request without windowId - send windowhandlerfilter.html to get the windowId
sendWindowHandlerHtml(facesContext.getExternalContext(), id);
sendWindowHandlerHtml(facesContext, facesContext.getExternalContext(), id);
facesContext.responseComplete();
}
}
Expand All @@ -96,7 +97,7 @@ protected boolean isNoscriptRequest(ExternalContext externalContext)
return (noscript != null && "true".equals(noscript));
}

protected void sendWindowHandlerHtml(ExternalContext externalContext, String windowId)
protected void sendWindowHandlerHtml(FacesContext facesContext, ExternalContext externalContext, String windowId)
{
HttpServletResponse httpResponse = (HttpServletResponse) externalContext.getResponse();

Expand All @@ -119,7 +120,7 @@ protected void sendWindowHandlerHtml(ExternalContext externalContext, String win
// could be a different when using forwards
windowHandlerHtml = windowHandlerHtml.replace(REQUEST_URL_REPLACE_PATTERN,
org.owasp.encoder.Encode.forJavaScriptBlock(
ClientWindowHelper.constructRequestUrl(externalContext)));
ClientWindowHelper.constructRequestUrl(facesContext, externalContext)));
// set the noscript-URL for users with no JavaScript
windowHandlerHtml =
windowHandlerHtml.replace(NOSCRIPT_URL_REPLACE_PATTERN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,27 @@
*/
package org.apache.deltaspike.jsf.impl.util;

import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.enterprise.inject.Typed;
import jakarta.faces.FacesException;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import jakarta.faces.lifecycle.ClientWindow;
import jakarta.faces.render.ResponseStateManager;

import jakarta.servlet.http.HttpServletResponse;
import org.apache.deltaspike.jsf.api.config.base.JsfBaseConfig;
import org.apache.myfaces.core.api.shared.lang.Assert;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

@Typed()
public abstract class ClientWindowHelper
Expand All @@ -48,7 +58,7 @@ public abstract class Cookies
public static final String REQUEST_WINDOW_ID_PREFIX = "dsrwid-";
}

public static String constructRequestUrl(ExternalContext externalContext)
public static String constructRequestUrl(FacesContext facesContext, ExternalContext externalContext)
{
String url = externalContext.getRequestContextPath()
+ externalContext.getRequestServletPath();
Expand All @@ -61,11 +71,156 @@ public static String constructRequestUrl(ExternalContext externalContext)
url = JsfUtils.addRequestParameters(externalContext, url, true);
// always remove jfwid to force adding new jfwid as JSF impl otherwise just ignores it
url = JsfUtils.removeUrlParameter(url, ResponseStateManager.CLIENT_WINDOW_URL_PARAM);
url = externalContext.encodeRedirectURL(url, null);

// TODO currently this is broken in Mojarra and will be fixed in 4.0.6
// url = externalContext.encodeRedirectURL(url, null);

// let's reuse the logic from MyFaces instead
HttpServletResponse servletResponse = (HttpServletResponse) externalContext.getResponse();
servletResponse.encodeRedirectURL(encodeURL(url, facesContext, servletResponse.getCharacterEncoding()));

return url;
}


// copied from MyFaces
private static String encodeURL(String baseUrl, FacesContext facesContext, String encoding)
{
Assert.notNull(baseUrl, "url");

String fragment = null;
String queryString = null;
Map<String, List<String>> paramMap = null;

//extract any URL fragment
int index = baseUrl.indexOf('#');
if (index != -1)
{
fragment = baseUrl.substring(index + 1);
baseUrl = baseUrl.substring(0, index);
}

//extract the current query string and add the params to the paramMap
index = baseUrl.indexOf('?');
if (index != -1)
{
queryString = baseUrl.substring(index + 1);
baseUrl = baseUrl.substring(0, index);
String[] nameValuePairs = queryString.split("&");
for (int i = 0; i < nameValuePairs.length; i++)
{
String[] currentPair = nameValuePairs[i].split("=");
String currentName = currentPair[0];

if (paramMap == null)
{
paramMap = new HashMap<>(5, 1f);
}

List<String> values = paramMap.get(currentName);
if (values == null)
{
values = new ArrayList<>(1);
paramMap.put(currentName, values);
}

try
{
values.add(currentPair.length > 1
? URLDecoder.decode(currentPair[1], encoding)
: "");
}
catch (UnsupportedEncodingException e)
{
//shouldn't ever get here
throw new UnsupportedOperationException("Encoding type=" + encoding
+ " not supported", e);
}
}
}

ClientWindow window = facesContext.getExternalContext().getClientWindow();
if (window != null && window.isClientWindowRenderModeEnabled(facesContext))
{
if (paramMap == null)
{
paramMap = new HashMap<>(5, 1f);
}

if (!paramMap.containsKey(ResponseStateManager.CLIENT_WINDOW_URL_PARAM))
{
paramMap.put(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, Arrays.asList(window.getId()));
}

Map<String, String> additionalQueryURLParameters = window.getQueryURLParameters(facesContext);
if (additionalQueryURLParameters != null)
{
for (Map.Entry<String , String> entry : additionalQueryURLParameters.entrySet())
{
paramMap.put(entry.getKey(), Arrays.asList(entry.getValue()));
}
}
}

boolean hasParams = paramMap != null && !paramMap.isEmpty();

if (!hasParams && fragment == null)
{
return baseUrl;
}

// start building the new URL
StringBuilder newUrl = new StringBuilder(baseUrl.length() + 10);
newUrl.append(baseUrl);

//now add the updated param list onto the url
if (hasParams)
{
boolean isFirstPair = true;
for (Map.Entry<String, List<String>> pair : paramMap.entrySet())
{
for (int i = 0; i < pair.getValue().size(); i++)
{
String value = pair.getValue().get(i);

if (!isFirstPair)
{
newUrl.append('&');
}
else
{
newUrl.append('?');
isFirstPair = false;
}

newUrl.append(pair.getKey());
newUrl.append('=');
if (value != null)
{
try
{
newUrl.append(URLEncoder.encode(value, encoding));
}
catch (UnsupportedEncodingException e)
{
//shouldn't ever get here
throw new UnsupportedOperationException("Encoding type=" + encoding
+ " not supported", e);
}
}
}
}
}

//add the fragment back on (if any)
if (fragment != null)
{
newUrl.append('#');
newUrl.append(fragment);
}

return newUrl.toString();
}

/**
* Handles the initial redirect for the LAZY mode, if no windowId is available in the current request URL.
*
Expand All @@ -80,7 +235,7 @@ public static void handleInitialRedirect(FacesContext facesContext, String newWi

ExternalContext externalContext = facesContext.getExternalContext();

String url = constructRequestUrl(externalContext);
String url = constructRequestUrl(facesContext, externalContext);

// remember the initial redirect windowId till the next request - see #729
addRequestWindowIdCookie(facesContext, newWindowId, newWindowId);
Expand Down

0 comments on commit 5c762b6

Please sign in to comment.