diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/TriggerCasGatewayException.java b/cas/src/main/java/org/springframework/security/cas/authentication/TriggerCasGatewayException.java
new file mode 100644
index 00000000000..e0dccbc3ef5
--- /dev/null
+++ b/cas/src/main/java/org/springframework/security/cas/authentication/TriggerCasGatewayException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013-2013 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * 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.springframework.security.cas.authentication;
+
+import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
+import org.springframework.security.core.AuthenticationException;
+
+/**
+ * Exception used to indicate the {@link CasAuthenticationEntryPoint} to make a CAS gateway authentication request.
+ *
+ * @author Michael Remond
+ *
+ */
+public class TriggerCasGatewayException extends AuthenticationException {
+
+
+ private static final long serialVersionUID = 4864856111227081697L;
+
+ //~ Constructors ===================================================================================================
+
+ /**
+ * Constructs an {@code InitiateCasGatewayAuthenticationException} with the specified message and no root cause.
+ *
+ * @param msg the detail message
+ */
+ public TriggerCasGatewayException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an {@code InitiateCasGatewayAuthenticationException} with the specified message and root cause.
+ *
+ * @param msg the detail message
+ * @param t the root cause
+ */
+ public TriggerCasGatewayException(String msg, Throwable t) {
+ super(msg, t);
+ }
+
+}
\ No newline at end of file
diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java
index 25221addf8c..5052b867c50 100644
--- a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java
+++ b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationEntryPoint.java
@@ -25,6 +25,7 @@
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.cas.ServiceProperties;
+import org.springframework.security.cas.authentication.TriggerCasGatewayException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.Assert;
@@ -39,6 +40,9 @@
* property. The service
is a HTTP URL belonging to the current application.
* The service
URL is monitored by the {@link CasAuthenticationFilter}, which
* will validate the CAS login was successful.
+ *
+ * If the raised exception is {@link TriggerCasGatewayException}, the parameter gateway
is set
+ * to true on the redirect url.
*
* @author Ben Alex
* @author Scott Battaglia
@@ -71,7 +75,7 @@ public void afterPropertiesSet() {
public final void commence(final HttpServletRequest servletRequest, HttpServletResponse response,
AuthenticationException authenticationException) throws IOException {
String urlEncodedService = createServiceUrl(servletRequest, response);
- String redirectUrl = createRedirectUrl(urlEncodedService);
+ String redirectUrl = createRedirectUrl(servletRequest, response, authenticationException, urlEncodedService);
preCommence(servletRequest, response);
response.sendRedirect(redirectUrl);
}
@@ -94,11 +98,26 @@ protected String createServiceUrl(HttpServletRequest request, HttpServletRespons
* @param serviceUrl the service url that should be included.
* @return the redirect url. CANNOT be NULL.
*/
+ @Deprecated
protected String createRedirectUrl(String serviceUrl) {
- return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl,
- this.serviceProperties.isSendRenew(), false);
+ return createRedirectUrl(null, null, null, serviceUrl);
}
+ /**
+ * Constructs the Url for Redirection to the CAS server. Default implementation relies on the CAS client to do the bulk of the work.
+ * Parameter gateway is infered from authenticationException class.
+ *
+ * @param request the HttpServletRequest
+ * @param response the HttpServletResponse
+ * @param authenticationException the authentication Exception that triggers CasAuthenticationEntryPoint
+ * @param serviceUrl the service url that should be included.
+ * @return the redirect url. CANNOT be NULL.
+ */
+ protected String createRedirectUrl(HttpServletRequest request, HttpServletResponse response, final AuthenticationException authenticationException, final String serviceUrl) {
+ boolean gateway = authenticationException instanceof TriggerCasGatewayException;
+ return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl, this.serviceProperties.isSendRenew(), gateway);
+ }
+
/**
* Template method for you to do your own pre-processing before the redirect occurs.
* @param request the HttpServletRequest
diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java
index 42339c0c9db..e233dfd990e 100644
--- a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java
+++ b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java
@@ -26,7 +26,6 @@
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.TicketValidator;
-
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
@@ -38,12 +37,18 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.DefaultRedirectStrategy;
+import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
+import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
+import org.springframework.security.web.savedrequest.RequestCache;
+import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
/**
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy
@@ -204,6 +209,10 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
+ private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+
+ private RequestCache requestCache = new HttpSessionRequestCache();
+
public CasAuthenticationFilter() {
super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
@@ -226,6 +235,25 @@ protected final void successfulAuthentication(HttpServletRequest request, HttpSe
chain.doFilter(request, response);
}
+ /**
+ * We override this method because we don't want to clear the rememberMe in case of an unsuccessful CAS gateway request.
+ */
+ @Override
+ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException failed) throws IOException, ServletException {
+
+ // if the request had a non empty artifact, we really encounter an unsuccessful authentication
+ // else it is probably an CAS gateway request with no SSO session, we redirect to the saved url
+ if (StringUtils.hasText(obtainArtifact(request))) {
+ super.unsuccessfulAuthentication(request, response, failed);
+ } else {
+ SavedRequest savedRequest = requestCache.getRequest(request, response);
+ if (savedRequest != null) {
+ redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());
+ }
+ }
+ }
+
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
@@ -302,6 +330,16 @@ public final void setServiceProperties(final ServiceProperties serviceProperties
this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();
}
+ public final void setRedirectStrategy(RedirectStrategy redirectStrategy) {
+ Assert.notNull(redirectStrategy, "redirectStrategy cannot be null");
+ this.redirectStrategy = redirectStrategy;
+ }
+
+ public final void setRequestCache(RequestCache requestCache) {
+ Assert.notNull(requestCache, "requestCache cannot be null");
+ this.requestCache = requestCache;
+ }
+
/**
* Indicates if the request is elgible to process a service ticket. This method exists
* for readability.
diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcher.java b/cas/src/main/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcher.java
new file mode 100644
index 00000000000..443c21110d6
--- /dev/null
+++ b/cas/src/main/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcher.java
@@ -0,0 +1,131 @@
+package org.springframework.security.cas.web;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
+import org.jasig.cas.client.authentication.GatewayResolver;
+import org.springframework.security.cas.ServiceProperties;
+import org.springframework.security.cas.authentication.CasAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Default RequestMatcher implementation for the {@link TriggerCasGatewayFilter}.
+ *
+ * This RequestMatcher returns true
iff :
+ *
+ * The request is marked as "gatewayed" using the configured {@link GatewayResolver} to avoid infinite loop.
+ *
+ * @author Michael Remond
+ *
+ */
+public class CasCookieGatewayRequestMatcher implements RequestMatcher {
+
+ // ~ Instance fields
+ // ================================================================================================
+
+ private ServiceProperties serviceProperties;
+
+ private String cookieName;
+
+ private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
+
+ // ~ Constructors
+ // ===================================================================================================
+
+ public CasCookieGatewayRequestMatcher(ServiceProperties serviceProperties, final String cookieName) {
+ Assert.notNull(serviceProperties, "serviceProperties cannot be null");
+ this.serviceProperties = serviceProperties;
+ this.cookieName = cookieName;
+ }
+
+ public final boolean matches(HttpServletRequest request) {
+
+ // Test if we are already authenticated
+ if (isAuthenticated(request)) {
+ return false;
+ }
+
+ // Test if the request was already gatewayed to avoid infinite loop
+ final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceProperties.getService());
+
+ if (wasGatewayed) {
+ return false;
+ }
+
+ // If request matches gateway criteria, we mark the request as gatewayed and return true to trigger a CAS
+ // gateway authentication
+ if (performGatewayAuthentication(request)) {
+ gatewayStorage.storeGatewayInformation(request, serviceProperties.getService());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Test if the user is authenticated in Spring Security. Default implementation test if the user is CAS
+ * authenticated.
+ *
+ * @param request
+ * @return true if the user is authenticated
+ */
+ protected boolean isAuthenticated(HttpServletRequest request) {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ return authentication instanceof CasAuthenticationToken;
+ }
+
+ /**
+ * Method that determines if the current request triggers a CAS gateway authentication.
+ * This implementation returns true
only if a {@link Cookie} with the configured
+ * name is present at the request
+ *
+ * @param request
+ * @return true if the request must trigger a CAS gateway authentication
+ */
+ protected boolean performGatewayAuthentication(HttpServletRequest request) {
+ if (!StringUtils.hasText(this.cookieName)) {
+ return true;
+ }
+
+ Cookie[] cookies = request.getCookies();
+ if (cookies == null || cookies.length == 0) {
+ return false;
+ }
+
+ for (Cookie cookie: cookies) {
+ // Check the cookie name. If it matches the configured cookie name, return true
+ if (this.cookieName.equalsIgnoreCase(cookie.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setGatewayStorage(GatewayResolver gatewayStorage) {
+ Assert.notNull(gatewayStorage, "gatewayStorage cannot be null");
+ this.gatewayStorage = gatewayStorage;
+ }
+
+ public String getCookieName() {
+ return cookieName;
+ }
+
+ public void setCookieName(String cookieName) {
+ this.cookieName = cookieName;
+ }
+}
\ No newline at end of file
diff --git a/cas/src/main/java/org/springframework/security/cas/web/TriggerCasGatewayFilter.java b/cas/src/main/java/org/springframework/security/cas/web/TriggerCasGatewayFilter.java
new file mode 100644
index 00000000000..4e459426df6
--- /dev/null
+++ b/cas/src/main/java/org/springframework/security/cas/web/TriggerCasGatewayFilter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013-2013 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * 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.springframework.security.cas.web;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.cas.authentication.TriggerCasGatewayException;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.util.Assert;
+import org.springframework.web.filter.GenericFilterBean;
+
+/**
+ * Triggers a CAS gateway authentication attempt.
+ *
+ * This filter must be placed after the ExceptionTranslationFilter
in the filter chain in order to start
+ * the authentication process. Throws a {@link TriggerCasGatewayException} when the current request matches against the
+ * configured {@link RequestMatcher}.
+ *
+ * The default implementation you can use is {@link DefaultCasGatewayRequestMatcher}. + * + * @author Michael Remond + */ +public class TriggerCasGatewayFilter extends GenericFilterBean { + + // ~ Instance fields + // ================================================================================================ + + private RequestMatcher requestMatcher; + + // ~ Constructors + // =================================================================================================== + + public TriggerCasGatewayFilter(RequestMatcher requestMatcher) { + Assert.notNull(requestMatcher, "requestMatcher cannot be null"); + this.requestMatcher = requestMatcher; + } + + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, + ServletException { + + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + if (requestMatcher.matches(request)) { + throw new TriggerCasGatewayException("Try a CAS gateway authentication"); + } else { + // Continue in the chain + chain.doFilter(request, response); + } + + } + + public void setRequestMatcher(RequestMatcher requestMatcher) { + this.requestMatcher = requestMatcher; + } + +} \ No newline at end of file diff --git a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java index c61d372944d..d91dda0fb5b 100644 --- a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java +++ b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationEntryPointTests.java @@ -16,16 +16,16 @@ package org.springframework.security.cas.web; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + import java.net.URLEncoder; import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.cas.ServiceProperties; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import org.springframework.security.cas.authentication.TriggerCasGatewayException; /** * Tests {@link CasAuthenticationEntryPoint}. @@ -95,4 +95,46 @@ public void testNormalOperationWithRenewTrue() throws Exception { .isEqualTo(response.getRedirectedUrl()); } + public void testNormalOperationWithRenewFalseAndGateway() throws Exception { + ServiceProperties sp = new ServiceProperties(); + sp.setSendRenew(false); + sp.setService("https://mycompany.com/bigWebApp/j_spring_cas_security_check"); + + CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); + ep.setLoginUrl("https://cas/login"); + ep.setServiceProperties(sp); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/some_path"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + + ep.afterPropertiesSet(); + ep.commence(request, response, new TriggerCasGatewayException("")); + assertThat("https://cas/login?service=" + + URLEncoder.encode("https://mycompany.com/bigWebApp/j_spring_cas_security_check", "UTF-8") + + "&gateway=true").isEqualTo(response.getRedirectedUrl()); + } + + public void testNormalOperationWithRenewTrueAndGateway() throws Exception { + ServiceProperties sp = new ServiceProperties(); + sp.setSendRenew(true); + sp.setService("https://mycompany.com/bigWebApp/j_spring_cas_security_check"); + + CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint(); + ep.setLoginUrl("https://cas/login"); + ep.setServiceProperties(sp); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/some_path"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + + ep.afterPropertiesSet(); + ep.commence(request, response, new TriggerCasGatewayException("")); + assertThat("https://cas/login?service=" + + URLEncoder.encode("https://mycompany.com/bigWebApp/j_spring_cas_security_check", "UTF-8") + + "&renew=true&gateway=true").isEqualTo(response.getRedirectedUrl()); + } + } diff --git a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java index f161e98473b..c1deb2b2672 100644 --- a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java +++ b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java @@ -16,12 +16,25 @@ package org.springframework.security.cas.web; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import java.io.IOException; + import javax.servlet.FilterChain; +import javax.servlet.ServletException; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; import org.junit.After; import org.junit.Test; - import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -34,15 +47,7 @@ import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; /** * Tests {@link CasAuthenticationFilter}. @@ -196,4 +201,32 @@ public void testChainNotInvokedForProxyReceptor() throws Exception { verifyZeroInteractions(chain); } + // SEC-977 + @Test + public void testCasGatewayWithNoSSOSession() throws IOException, ServletException { + HttpSessionRequestCache requestCache = new HttpSessionRequestCache(); + MockHttpServletRequest initialRequest = new MockHttpServletRequest("GET", "/some_path"); + requestCache.saveRequest(initialRequest, null); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + request.setServletPath("/login/cas"); + request.setSession(initialRequest.getSession()); + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain chain = mock(FilterChain.class); + + CasAuthenticationFilter filter = new CasAuthenticationFilter(); + filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class)); + filter.setAuthenticationManager(new AuthenticationManager() { + public Authentication authenticate(Authentication a) { + throw new BadCredentialsException("No ticket found"); + } + }); + + filter.doFilter(request,response,chain); + verify(chain, never()).doFilter(request, response); + assertEquals("Should redirect to saved url", "http://localhost/some_path", response.getRedirectedUrl()); + + } + } diff --git a/cas/src/test/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcherTests.java b/cas/src/test/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcherTests.java new file mode 100644 index 00000000000..06fabe610ca --- /dev/null +++ b/cas/src/test/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcherTests.java @@ -0,0 +1,97 @@ +package org.springframework.security.cas.web; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; + +public class CasCookieGatewayRequestMatcherTests { + + @Test + public void testNullServiceProperties() throws Exception { + try { + new CasCookieGatewayRequestMatcher(null, null); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertEquals("serviceProperties cannot be null", expected.getMessage()); + } + } + + @Test + public void testNormalOperationWithNoSSOSession() throws IOException, ServletException { + SecurityContextHolder.getContext().setAuthentication(null); + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setService("http://localhost/j_spring_cas_security_check"); + CasCookieGatewayRequestMatcher rm = new CasCookieGatewayRequestMatcher(serviceProperties, null); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/some_path"); + + // First request + assertTrue(rm.matches(request)); + assertNotNull(request.getSession(false).getAttribute(DefaultGatewayResolverImpl.CONST_CAS_GATEWAY)); + // Second request + assertFalse(rm.matches(request)); + assertNotNull(request.getSession(false).getAttribute(DefaultGatewayResolverImpl.CONST_CAS_GATEWAY)); + } + + + @Test + public void testGatewayWhenCasAuthenticated() throws IOException, ServletException { + SecurityContextHolder.getContext().setAuthentication(null); + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setService("http://localhost/j_spring_cas_security_check"); + CasCookieGatewayRequestMatcher rm = new CasCookieGatewayRequestMatcher(serviceProperties, "CAS_TGT_COOKIE_TEST_NAME"); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/some_path"); + request.setCookies(new Cookie("CAS_TGT_COOKIE_TEST_NAME", "casTGCookieValue")); + + assertTrue(rm.matches(request)); + + MockHttpServletRequest requestWithoutCasCookie = new MockHttpServletRequest("GET", "/some_path"); + requestWithoutCasCookie.setCookies(new Cookie("WRONG_CAS_TGT_COOKIE_TEST_NAME", "casTGCookieValue")); + + assertFalse(rm.matches(requestWithoutCasCookie)); + } + + @Test + public void testGatewayWhenAlreadySessionCreated() throws IOException, ServletException { + SecurityContextHolder.getContext().setAuthentication(mock(CasAuthenticationToken.class)); + + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setService("http://localhost/j_spring_cas_security_check"); + CasCookieGatewayRequestMatcher rm = new CasCookieGatewayRequestMatcher(serviceProperties, "CAS_TGT_COOKIE_TEST_NAME"); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/some_path"); + assertFalse(rm.matches(request)); + } + + + @Test + public void testGatewayWithNoMatchingRequest() throws IOException, ServletException { + SecurityContextHolder.getContext().setAuthentication(null); + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setService("http://localhost/j_spring_cas_security_check"); + CasCookieGatewayRequestMatcher rm = new CasCookieGatewayRequestMatcher(serviceProperties, "CAS_TGT_COOKIE_TEST_NAME") { + @Override + protected boolean performGatewayAuthentication(HttpServletRequest request) { + return false; + } + }; + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/some_path"); + + assertFalse(rm.matches(request)); + } + +} \ No newline at end of file