parent
590a79c6a6
commit
47f825871e
|
|
@ -21,7 +21,11 @@ package org.jasig.cas.client.session;
|
|||
import java.io.IOException;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jasig.cas.client.util.AbstractConfigurationFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Implements the Single Sign Out protocol. It handles registering the session and destroying the session.
|
||||
|
|
@ -34,15 +38,25 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter {
|
|||
|
||||
private static final SingleSignOutHandler handler = new SingleSignOutHandler();
|
||||
|
||||
/** The prefix url of the CAS server */
|
||||
private String casServerUrlPrefix;
|
||||
|
||||
/** Parameter name that stores the state of the CAS server webflow for the callback */
|
||||
private String relayStateParameterName = SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME;
|
||||
|
||||
public void init(final FilterConfig filterConfig) throws ServletException {
|
||||
if (!isIgnoreInitConfiguration()) {
|
||||
handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
|
||||
handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName",
|
||||
SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME));
|
||||
handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName",
|
||||
"logoutRequest"));
|
||||
SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME));
|
||||
setRelayStateParameterName(getPropertyFromInitParams(filterConfig, "relayStateParameterName",
|
||||
SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME));
|
||||
handler.setArtifactParameterOverPost(parseBoolean(getPropertyFromInitParams(filterConfig,
|
||||
"artifactParameterOverPost", "false")));
|
||||
handler.setEagerlyCreateSessions(parseBoolean(getPropertyFromInitParams(filterConfig,
|
||||
"eagerlyCreateSessions", "true")));
|
||||
setCasServerUrlPrefix(getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null));
|
||||
}
|
||||
handler.init();
|
||||
}
|
||||
|
|
@ -55,20 +69,51 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter {
|
|||
handler.setLogoutParameterName(name);
|
||||
}
|
||||
|
||||
public void setRelayStateParameterName(final String name) {
|
||||
this.relayStateParameterName = name;
|
||||
handler.setRelayStateParameterName(name);
|
||||
}
|
||||
|
||||
public void setSessionMappingStorage(final SessionMappingStorage storage) {
|
||||
handler.setSessionMappingStorage(storage);
|
||||
}
|
||||
|
||||
public void setCasServerUrlPrefix(final String casServerUrlPrefix) {
|
||||
CommonUtils.assertNotNull(casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
|
||||
this.casServerUrlPrefix = casServerUrlPrefix;
|
||||
}
|
||||
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
|
||||
final FilterChain filterChain) throws IOException, ServletException {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
|
||||
if (handler.isTokenRequest(request)) {
|
||||
handler.recordSession(request);
|
||||
} else if (handler.isLogoutRequest(request)) {
|
||||
} else if (handler.isBackChannelLogoutRequest(request)) {
|
||||
handler.destroySession(request);
|
||||
// Do not continue up filter chain
|
||||
return;
|
||||
} else if (handler.isFrontChannelLogoutRequest(request)) {
|
||||
handler.destroySession(request);
|
||||
// relay state value
|
||||
final String relayStateValue = CommonUtils.safeGetParameter(request, this.relayStateParameterName);
|
||||
// if we have a state value -> redirect to the CAS server to continue the logout process
|
||||
if (StringUtils.isNotBlank(relayStateValue)) {
|
||||
final StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(casServerUrlPrefix);
|
||||
if (!this.casServerUrlPrefix.endsWith("/")) {
|
||||
buffer.append("/");
|
||||
}
|
||||
buffer.append("logout?_eventId=next&");
|
||||
buffer.append(this.relayStateParameterName);
|
||||
buffer.append("=");
|
||||
buffer.append(CommonUtils.urlEncode(relayStateValue));
|
||||
final String redirectUrl = buffer.toString();
|
||||
logger.debug("Redirecting back to the CAS server: {}", redirectUrl);
|
||||
CommonUtils.sendRedirect(response, redirectUrl);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
logger.trace("Ignoring URI {}", request.getRequestURI());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,14 +18,18 @@
|
|||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -40,6 +44,10 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
public final class SingleSignOutHandler {
|
||||
|
||||
public final static String DEFAULT_ARTIFACT_PARAMETER_NAME = "ticket";
|
||||
public final static String DEFAULT_LOGOUT_PARAMETER_NAME = "logoutRequest";
|
||||
public final static String DEFAULT_RELAY_STATE_PARAMETER_NAME = "RelayState";
|
||||
|
||||
/** Logger instance */
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
|
|
@ -47,11 +55,14 @@ public final class SingleSignOutHandler {
|
|||
private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
|
||||
|
||||
/** The name of the artifact parameter. This is used to capture the session identifier. */
|
||||
private String artifactParameterName = "ticket";
|
||||
private String artifactParameterName = DEFAULT_ARTIFACT_PARAMETER_NAME;
|
||||
|
||||
/** Parameter name that stores logout request */
|
||||
private String logoutParameterName = "logoutRequest";
|
||||
private String logoutParameterName = DEFAULT_LOGOUT_PARAMETER_NAME;
|
||||
|
||||
/** Parameter name that stores the state of the CAS server webflow for the callback */
|
||||
private String relayStateParameterName = DEFAULT_RELAY_STATE_PARAMETER_NAME;
|
||||
|
||||
private boolean artifactParameterOverPost = false;
|
||||
|
||||
private boolean eagerlyCreateSessions = true;
|
||||
|
|
@ -84,6 +95,13 @@ public final class SingleSignOutHandler {
|
|||
this.logoutParameterName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Name of parameter containing the state of the CAS server webflow.
|
||||
*/
|
||||
public void setRelayStateParameterName(final String name) {
|
||||
this.relayStateParameterName = name;
|
||||
}
|
||||
|
||||
public void setEagerlyCreateSessions(final boolean eagerlyCreateSessions) {
|
||||
this.eagerlyCreateSessions = eagerlyCreateSessions;
|
||||
}
|
||||
|
|
@ -95,6 +113,7 @@ public final class SingleSignOutHandler {
|
|||
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
|
||||
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
|
||||
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannot be null.");
|
||||
CommonUtils.assertNotNull(this.relayStateParameterName, "relayStateParameterName cannot be null.");
|
||||
|
||||
if (this.artifactParameterOverPost) {
|
||||
this.safeParameters = Arrays.asList(this.logoutParameterName, this.artifactParameterName);
|
||||
|
|
@ -116,19 +135,31 @@ public final class SingleSignOutHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given request is a CAS logout request.
|
||||
* Determines whether the given request is a CAS back channel logout request.
|
||||
*
|
||||
* @param request HTTP request.
|
||||
*
|
||||
* @return True if request is logout request, false otherwise.
|
||||
*/
|
||||
public boolean isLogoutRequest(final HttpServletRequest request) {
|
||||
public boolean isBackChannelLogoutRequest(final HttpServletRequest request) {
|
||||
return "POST".equals(request.getMethod())
|
||||
&& !isMultipartRequest(request)
|
||||
&& CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName,
|
||||
this.safeParameters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given request is a CAS front channel logout request.
|
||||
*
|
||||
* @param request HTTP request.
|
||||
*
|
||||
* @return True if request is logout request, false otherwise.
|
||||
*/
|
||||
public boolean isFrontChannelLogoutRequest(final HttpServletRequest request) {
|
||||
return "GET".equals(request.getMethod())
|
||||
&& CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a token request with the current HTTP session by recording the mapping
|
||||
* in the the configured {@link SessionMappingStorage} container.
|
||||
|
|
@ -154,14 +185,47 @@ public final class SingleSignOutHandler {
|
|||
sessionMappingStorage.addSessionById(token, session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncompress a logout message (base64 + deflate).
|
||||
*
|
||||
* @param originalMessage the original logout message.
|
||||
* @return the uncompressed logout message.
|
||||
*/
|
||||
private String uncompressLogoutMessage(final String originalMessage) {
|
||||
// base64 decode
|
||||
final byte[] binaryMessage = Base64.decodeBase64(originalMessage);
|
||||
|
||||
try {
|
||||
// decompress the bytes
|
||||
final Inflater decompresser = new Inflater();
|
||||
decompresser.setInput(binaryMessage);
|
||||
byte[] result = new byte[binaryMessage.length * 10];
|
||||
int resultLength = decompresser.inflate(result);
|
||||
decompresser.end();
|
||||
|
||||
// decode the bytes into a String
|
||||
return new String(result, 0, resultLength, "UTF-8");
|
||||
} catch (DataFormatException e) {
|
||||
logger.error("Unable to decompress logout message", e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.error("Unable to decompress logout message", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the current HTTP session for the given CAS logout request.
|
||||
*
|
||||
* @param request HTTP request containing a CAS logout message.
|
||||
*/
|
||||
public void destroySession(final HttpServletRequest request) {
|
||||
final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName,
|
||||
String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName,
|
||||
this.safeParameters);
|
||||
// front channel request -> the message needs to be base64 decoded + decompressed
|
||||
if ("GET".equals(request.getMethod())) {
|
||||
logoutMessage = uncompressLogoutMessage(logoutMessage);
|
||||
}
|
||||
logger.trace("Logout request:\n{}", logoutMessage);
|
||||
|
||||
final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
|
||||
|
|
|
|||
|
|
@ -160,10 +160,19 @@ public final class CommonUtils {
|
|||
*/
|
||||
public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName,
|
||||
final String serviceUrl, final boolean renew, final boolean gateway) {
|
||||
return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "="
|
||||
+ urlEncode(serviceUrl) + (renew ? "&renew=true" : "") + (gateway ? "&gateway=true" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Url encode a value using UTF-8 encoding.
|
||||
*
|
||||
* @param value the value to encode.
|
||||
* @return the encoded value.
|
||||
*/
|
||||
public static String urlEncode(String value) {
|
||||
try {
|
||||
return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "="
|
||||
+ URLEncoder.encode(serviceUrl, "UTF-8") + (renew ? "&renew=true" : "")
|
||||
+ (gateway ? "&gateway=true" : "");
|
||||
return URLEncoder.encode(value, "UTF-8");
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package org.jasig.cas.client.session;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* Logout message generator to perform tests on Single Sign Out feature.
|
||||
* Greatly inspired by the source code in the CAS server itself.
|
||||
*
|
||||
* @author Jerome Leleu
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public final class LogoutMessageGenerator {
|
||||
|
||||
private static final String LOGOUT_REQUEST_TEMPLATE =
|
||||
"<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"00000001\" Version=\"2.0\" "
|
||||
+ "IssueInstant=\"\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">@NOT_USED@"
|
||||
+ "</saml:NameID><samlp:SessionIndex>%s</samlp:SessionIndex></samlp:LogoutRequest>";
|
||||
|
||||
public static String generateLogoutMessage(String sessionIndex) {
|
||||
return String.format(LOGOUT_REQUEST_TEMPLATE, sessionIndex);
|
||||
}
|
||||
|
||||
public static String generateCompressedLogoutMessage(String sessionIndex) {
|
||||
final String logoutMessage = generateLogoutMessage(sessionIndex);
|
||||
final Deflater deflater = new Deflater();
|
||||
deflater.setInput(logoutMessage.getBytes(Charset.forName("ASCII")));
|
||||
deflater.finish();
|
||||
final byte[] buffer = new byte[logoutMessage.length()];
|
||||
final int resultSize = deflater.deflate(buffer);
|
||||
final byte[] output = new byte[resultSize];
|
||||
System.arraycopy(buffer, 0, output, 0, resultSize);
|
||||
return Base64.encodeBase64String(output);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Licensed to Jasig under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Jasig licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockFilterConfig;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
/**
|
||||
* Tests {@link SingleSignOutFilter}.
|
||||
*
|
||||
* @author Jerome Leleu
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public class SingleSignOutFilterTests {
|
||||
|
||||
private final static String CAS_SERVER_URL_PREFIX = "http://myhost.com/mycasserver";
|
||||
private final static String TICKET = "ST-yyyyy";
|
||||
private final static String RELAY_STATE = "e1s1";
|
||||
|
||||
private SingleSignOutFilter filter = new SingleSignOutFilter();
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
private MockFilterChain filterChain;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
filter = new SingleSignOutFilter();
|
||||
filter.setCasServerUrlPrefix(CAS_SERVER_URL_PREFIX);
|
||||
filter.setIgnoreInitConfiguration(true);
|
||||
filter.init(new MockFilterConfig());
|
||||
request = new MockHttpServletRequest();
|
||||
response = new MockHttpServletResponse();
|
||||
filterChain = new MockFilterChain();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void initWithoutCasServerUrlPrefix() throws ServletException {
|
||||
filter = new SingleSignOutFilter();
|
||||
filter.init(new MockFilterConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tokenRequest() throws IOException, ServletException {
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME, TICKET);
|
||||
request.setQueryString(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME + "=" + TICKET);
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
request.setSession(session);
|
||||
filter.doFilter(request, response, filterChain);
|
||||
assertEquals(session, SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void backChannelRequest() throws IOException, ServletException {
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME, LogoutMessageGenerator.generateLogoutMessage(TICKET));
|
||||
request.setMethod("POST");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
filter.doFilter(request, response, filterChain);
|
||||
assertNull(SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void frontChannelRequest() throws IOException, ServletException {
|
||||
final String logoutMessage = LogoutMessageGenerator.generateCompressedLogoutMessage(TICKET);
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME, logoutMessage);
|
||||
request.setQueryString(SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage);
|
||||
request.setMethod("GET");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
filter.doFilter(request, response, filterChain);
|
||||
assertNull(SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET));
|
||||
assertNull(response.getRedirectedUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void frontChannelRequestRelayState() throws IOException, ServletException {
|
||||
final String logoutMessage = LogoutMessageGenerator.generateCompressedLogoutMessage(TICKET);
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME, logoutMessage);
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME, RELAY_STATE);
|
||||
request.setQueryString(SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage + "&" +
|
||||
SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME + "=" + RELAY_STATE);
|
||||
request.setMethod("GET");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
filter.doFilter(request, response, filterChain);
|
||||
assertNull(SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET));
|
||||
assertEquals(CAS_SERVER_URL_PREFIX + "/logout?_eventId=next&" +
|
||||
SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME + "=" + RELAY_STATE, response.getRedirectedUrl());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Licensed to Jasig under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Jasig licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
/**
|
||||
* @author Matt Brown <matt.brown@citrix.com>
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public final class SingleSignOutHandlerTests {
|
||||
|
||||
private final static String ANOTHER_PARAMETER = "anotherParameter";
|
||||
private final static String TICKET = "ST-xxxxxxxx";
|
||||
|
||||
private SingleSignOutHandler handler;
|
||||
private MockHttpServletRequest request;
|
||||
private final static String logoutParameterName = "logoutRequest";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
handler = new SingleSignOutHandler();
|
||||
handler.setLogoutParameterName(logoutParameterName);
|
||||
handler.init();
|
||||
request = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isBackChannelLogoutRequest() throws Exception {
|
||||
request.setParameter(logoutParameterName, TICKET);
|
||||
request.setMethod("POST");
|
||||
|
||||
assertTrue(handler.isBackChannelLogoutRequest(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a multipart request is not considered logoutRequest. Verifies issue CASC-147.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void isBackChannelLogoutRequestMultipart() throws Exception {
|
||||
request.setParameter(logoutParameterName, TICKET);
|
||||
request.setMethod("POST");
|
||||
request.setContentType("multipart/form-data");
|
||||
|
||||
assertFalse(handler.isBackChannelLogoutRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isFrontChannelLogoutRequest() {
|
||||
request.setParameter(logoutParameterName, TICKET);
|
||||
request.setMethod("GET");
|
||||
request.setQueryString(logoutParameterName + "=" + TICKET);
|
||||
|
||||
assertTrue(handler.isFrontChannelLogoutRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isFrontChannelLogoutRequestKO() {
|
||||
request.setParameter(ANOTHER_PARAMETER, TICKET);
|
||||
request.setMethod("GET");
|
||||
request.setQueryString(ANOTHER_PARAMETER + "=" + TICKET);
|
||||
|
||||
assertFalse(handler.isFrontChannelLogoutRequest(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordSessionKOIfNoSession() {
|
||||
handler.setEagerlyCreateSessions(false);
|
||||
request.setSession(null);
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME, TICKET);
|
||||
request.setQueryString(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME + "=" + TICKET);
|
||||
handler.recordSession(request);
|
||||
final SessionMappingStorage storage = handler.getSessionMappingStorage();
|
||||
assertNull(storage.removeSessionByMappingId(TICKET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordSessionOK() {
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
request.setSession(session);
|
||||
request.setParameter(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME, TICKET);
|
||||
request.setQueryString(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME + "=" + TICKET);
|
||||
handler.recordSession(request);
|
||||
final SessionMappingStorage storage = handler.getSessionMappingStorage();
|
||||
assertEquals(session, storage.removeSessionByMappingId(TICKET));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destorySessionPOSTKONoSessionIndex() {
|
||||
final String logoutMessage = LogoutMessageGenerator.generateLogoutMessage("");
|
||||
request.setParameter(logoutParameterName, logoutMessage);
|
||||
request.setMethod("POST");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
handler.getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
handler.destroySession(request);
|
||||
assertFalse(session.isInvalid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destorySessionPOST() {
|
||||
final String logoutMessage = LogoutMessageGenerator.generateLogoutMessage(TICKET);
|
||||
request.setParameter(logoutParameterName, logoutMessage);
|
||||
request.setMethod("POST");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
handler.getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
handler.destroySession(request);
|
||||
assertTrue(session.isInvalid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destorySessionGETNoSessionIndex() {
|
||||
final String logoutMessage = LogoutMessageGenerator.generateCompressedLogoutMessage("");
|
||||
request.setParameter(logoutParameterName, logoutMessage);
|
||||
request.setQueryString(logoutParameterName + "=" + logoutMessage);
|
||||
request.setMethod("GET");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
handler.getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
handler.destroySession(request);
|
||||
assertFalse(session.isInvalid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void destorySessionGET() {
|
||||
final String logoutMessage = LogoutMessageGenerator.generateCompressedLogoutMessage(TICKET);
|
||||
request.setParameter(logoutParameterName, logoutMessage);
|
||||
request.setQueryString(logoutParameterName + "=" + logoutMessage);
|
||||
request.setMethod("GET");
|
||||
final MockHttpSession session = new MockHttpSession();
|
||||
handler.getSessionMappingStorage().addSessionById(TICKET, session);
|
||||
handler.destroySession(request);
|
||||
assertTrue(session.isInvalid());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Licensed to Jasig under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Jasig licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a
|
||||
* copy of the License at the following location:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.jasig.cas.client.session;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author Matt Brown <matt.brown@citrix.com>
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public final class SingleSignoutHandlerTests {
|
||||
|
||||
private SingleSignOutHandler handler;
|
||||
private MockHttpServletRequest request;
|
||||
private final static String logoutParameterName = "logoutRequest";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
handler = new SingleSignOutHandler();
|
||||
handler.setLogoutParameterName(logoutParameterName);
|
||||
handler.init();
|
||||
request = new MockHttpServletRequest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLogoutRequest() throws Exception {
|
||||
request.setParameter(logoutParameterName, "true");
|
||||
request.setMethod("POST");
|
||||
|
||||
assertTrue(handler.isLogoutRequest(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a multipart request is not considered logoutRequest. Verifies issue CASC-147.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void isLogoutRequestMultipart() throws Exception {
|
||||
request.setParameter(logoutParameterName, "true");
|
||||
request.setMethod("POST");
|
||||
request.setContentType("multipart/form-data");
|
||||
|
||||
assertFalse(handler.isLogoutRequest(request));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -167,4 +167,8 @@ public final class CommonUtilsTests extends TestCase {
|
|||
final String responsedContent = CommonUtils.getResponseFromServer(new URL("http://localhost:8090"), new HttpsURLConnectionFactory(), null);
|
||||
assertEquals(RESPONSE, responsedContent);
|
||||
}
|
||||
|
||||
public void testUrlEncode() {
|
||||
assertEquals("this+is+a+very+special+parameter+with+%3D%25%2F", CommonUtils.urlEncode("this is a very special parameter with =%/"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue