Skip to content

Commit

Permalink
Merge pull request #2611 from dburdine/formlogout-OL-2
Browse files Browse the repository at this point in the history
Formlogout ol 2
  • Loading branch information
garypicher committed Mar 9, 2018
2 parents 127d9b5 + 07c2c48 commit 6767ba4
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ private String removeFirstSlash(String exitPage) {
protected boolean verifyLogoutURL(HttpServletRequest req, String exitPage) {
boolean acceptURL = false;
boolean allowLogoutPageRedirectToAnyHost = webAppSecurityConfig.getAllowLogoutPageRedirectToAnyHost();
if (!exitPage.equals("logon.jsp") && !allowLogoutPageRedirectToAnyHost) {
if (exitPage != null && !exitPage.equals("logon.jsp") && !allowLogoutPageRedirectToAnyHost) {
String logoutURLHost = null;
try {
InetAddress thisHost = InetAddress.getLocalHost();
Expand All @@ -243,8 +243,31 @@ protected boolean verifyLogoutURL(HttpServletRequest req, String exitPage) {
if (tc.isDebugEnabled())
Tr.debug(tc, "domain for exitPage url: " + logoutURLHost);
if (!logoutURL.isAbsolute()) {
//always accept relative URLs for redirect.
acceptURL = true;
/*
* If exitPage starts with "//" , it is a NetworkPath. We need to valid its target host.
* While the specification for network paths is preceded by exactly 2 slashes("//"), browsers will
* generally redirect network paths with 2 or more preceding slashes. Thus, we need to try to obtain the hostname
* utilizing only 2 preceding slashes.
*/
if (exitPage.startsWith("//")) {
if (tc.isDebugEnabled())
Tr.debug(tc, "URI " + exitPage + " will be processed as a Network-Path." +
" sendRedirect() defines a Network-Path as starting with // ");
char[] exitPageCharArr = exitPage.toCharArray();
for (int i = 0; i < exitPageCharArr.length; i++) {
if (exitPageCharArr[i] != '/') {
URI uri = new URI(exitPage.substring(i - 2));
//Set hostname for further checks.
logoutURLHost = uri.getHost();
if (tc.isDebugEnabled())
Tr.debug(tc, "SDK indicates " + exitPage + " Network-Path does not contain a valid hostname.");
break;
}
}
} else {
//accept a relative URIs that are not Network-Path's
acceptURL = true;
}
}
} catch (URISyntaxException urise) {
//The URI is invalid and will not be redirected to.
Expand All @@ -267,7 +290,9 @@ protected boolean verifyLogoutURL(HttpServletRequest req, String exitPage) {
if (!acceptURL && logoutURLHost != null)
acceptURL = isRequestURLEqualsExitPageHost(req, logoutURLHost);
} else {
acceptURL = true;
if (exitPage != null) {
acceptURL = true;
}
}
if (!acceptURL) {
Tr.error(tc, "SEC_FORM_LOGOUTEXITPAGE_INVALID", new Object[] { req.getRequestURL(), req.getParameter("logoutExitPage") });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,28 +153,125 @@ public void testLogoutReqUrlHost_NotEqualsExitPageHost() throws Exception {
mock.assertIsSatisfied();
}

/*
* Test variations of URLs
*/
@Test
public void testIsLogoutPageURLValid() throws Exception {
//List<String> logoutPageRedirectDomainList = new ArrayList<String>();
//logoutPageRedirectDomainList.add("austin.ibm.com");
final String otherHostLogoutURL = "http://localhost:9080 2 3 Location: http://localhost:9080";
public void testIsLogoutPageURLValid_invalidURL() throws Exception {

final String otherHostLogoutURL1 = "http://localhost:9080 2 3 Location: http://localhost:9080";
final String otherHostLogoutURL2 = "mailto:java-net@java.sun.com";
final String otherHostLogoutURL3 = "news:comp.lang.java";
final String otherHostLogoutURL4 = "urn:isbn:096139210x";
final String otherHostLogoutURL5 = "http://www.ibm.com/j2se/1.3/";
final String otherHostLogoutURL6 = "docs/guide/collections/designfaq.html#28";
final String otherHostLogoutURL7 = "../../../demo/jfc/SwingSet2/src/SwingSet2.java";
final String otherHostLogoutURL8 = "file://www.ibm.com/~/calendar";
final String otherHostLogoutURL9 = null;
final String otherHostLogoutURL10 = ""; //JDK considers "" a valid URI, so we do then as well.

final java.io.PrintWriter pw = mock.mock(java.io.PrintWriter.class);

mock.checking(new Expectations() {
{
one(req).getParameter("logoutExitPage");
will(returnValue(otherHostLogoutURL));
one(req).getRequestURL();
allowing(req).getParameter("logoutExitPage"); //This is a trace record call. Value returned is irrelevant.
will(returnValue(otherHostLogoutURL1));
allowing(req).getRequestURL();
will(returnValue(new StringBuffer("http://localhost:9080/snoop")));
one(resp).getWriter();
allowing(resp).getWriter();
will(returnValue(pw));
one(pw).println(FormLogoutExtensionProcessor.DEFAULT_LOGOUT_MSG);
one(wasc).getLogoutPageRedirectDomainList();
allowing(pw).println(FormLogoutExtensionProcessor.DEFAULT_LOGOUT_MSG);
allowing(wasc).getLogoutPageRedirectDomainList();
}
});

FormLogoutExtensionProcessor processorDouble = new FormLogoutExtensionProcessor(ctx, wasc, authApi);
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL2));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL3));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL4));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL5));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL6));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL7));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL8));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL9));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL10));
}

@Test
public void testIsLogoutPageURLValid_networkPath_listMatch() throws Exception {
final List<String> logoutPageRedirectDomainList = new ArrayList<String>();
logoutPageRedirectDomainList.add("austin.ibm.com");
logoutPageRedirectDomainList.add("myserver.com");

final String otherHostLogoutURL1a = "/////myserver.com";
final String otherHostLogoutURL1b = "////myserver.com";
final String otherHostLogoutURL1c = "///myserver.com";
final String otherHostLogoutURL1d = "//myserver.com";
//Considered relative URLs by WAS. So redirect performed and no hostname list comparision performed.
final String otherHostLogoutURL1e = "/myserver.com";
final String otherHostLogoutURL1f = "myserver.com";
final java.io.PrintWriter pw = mock.mock(java.io.PrintWriter.class);

mock.checking(new Expectations() {
{
allowing(req).getParameter("logoutExitPage");
//will(returnValue(otherHostLogoutURL));
allowing(req).getRequestURL();
will(returnValue(new StringBuffer("http://localhost:9080/snoop")));
allowing(resp).getWriter();
will(returnValue(pw));
allowing(pw).println(FormLogoutExtensionProcessor.DEFAULT_LOGOUT_MSG);
allowing(wasc).getLogoutPageRedirectDomainList();
will(returnValue(logoutPageRedirectDomainList));
}
});

FormLogoutExtensionProcessor processorDouble = new FormLogoutExtensionProcessor(ctx, wasc, authApi);
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1a));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1b));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1c));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1d));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1e));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1f));
}

@Test
public void testIsLogoutPageURLValid_networkPath_noListMatch() throws Exception {
final List<String> logoutPageRedirectDomainList = new ArrayList<String>();
logoutPageRedirectDomainList.add("austin.ibm.com");
logoutPageRedirectDomainList.add("yourserver.com");

final String otherHostLogoutURL1a = "/////myserver.com";
final String otherHostLogoutURL1b = "////myserver.com";
final String otherHostLogoutURL1c = "///myserver.com";
final String otherHostLogoutURL1d = "//myserver.com";
//Considered relative URLs by WAS. So redirect performed and no hostname list comparision performed.
final String otherHostLogoutURL1e = "/myserver.com";
final String otherHostLogoutURL1f = "myserver.com";
final java.io.PrintWriter pw = mock.mock(java.io.PrintWriter.class);

mock.checking(new Expectations() {
{
allowing(req).getParameter("logoutExitPage");
//will(returnValue(otherHostLogoutURL));
allowing(req).getRequestURL();
will(returnValue(new StringBuffer("http://localhost:9080/snoop")));
allowing(resp).getWriter();
will(returnValue(pw));
allowing(pw).println(FormLogoutExtensionProcessor.DEFAULT_LOGOUT_MSG);
allowing(wasc).getLogoutPageRedirectDomainList();
will(returnValue(logoutPageRedirectDomainList));
}
});

FormLogoutExtensionProcessor processorDouble = new FormLogoutExtensionProcessor(ctx, wasc, authApi);
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1a));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1b));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1c));
assertFalse(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1d));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1e));
assertTrue(processorDouble.verifyLogoutURL(req, otherHostLogoutURL1f));
}

// Note that we cannot simulate code flow through if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) blocks
Expand Down

0 comments on commit 6767ba4

Please sign in to comment.