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 94c783e3a2..a8500bf05b 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
@@ -22,6 +22,7 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
import org.apereo.cas.client.util.WebUtils;
import org.apereo.cas.client.validation.TicketValidator;
@@ -39,11 +40,16 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
+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.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
+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;
@@ -199,6 +205,10 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
+ private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
+
+ private RequestCache requestCache = new HttpSessionRequestCache();
+
public CasAuthenticationFilter() {
super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
@@ -238,8 +248,24 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
String serviceTicket = obtainArtifact(request);
if (serviceTicket == null) {
- this.logger.debug("Failed to obtain an artifact (cas ticket)");
- serviceTicket = "";
+ boolean gateway = false;
+ HttpSession session = request.getSession(false);
+ if (session != null) {
+ gateway = session.getAttribute(TriggerCasGatewayFilter.TRIGGER_CAS_GATEWAY_AUTHENTICATION) != null;
+ session.removeAttribute(TriggerCasGatewayFilter.TRIGGER_CAS_GATEWAY_AUTHENTICATION);
+ }
+ if (gateway) {
+ this.logger.debug("Failed authentication response from CAS gateway request");
+ SavedRequest savedRequest = this.requestCache.getRequest(request, response);
+ if (savedRequest != null) {
+ this.redirectStrategy.sendRedirect(request, response, savedRequest.getRedirectUrl());
+ }
+ return null;
+ }
+ else {
+ this.logger.debug("Failed to obtain an artifact (cas ticket)");
+ serviceTicket = "";
+ }
}
boolean serviceTicketRequest = serviceTicketRequest(request, response);
CasServiceTicketAuthenticationToken authRequest = serviceTicketRequest
@@ -303,6 +329,16 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
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 0000000000..973503efc0
--- /dev/null
+++ b/cas/src/main/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcher.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002-2023 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
+ *
+ * https://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 jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import org.apereo.cas.client.authentication.DefaultGatewayResolverImpl;
+import org.apereo.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 if:
+ *
+ * The request is marked as "gatewayed" using the configured {@link GatewayResolver} to
+ * avoid infinite loop.
+ *
+ * @author Michael Remond
+ *
+ */
+public class CasCookieGatewayRequestMatcher implements RequestMatcher {
+
+ private ServiceProperties serviceProperties;
+
+ private String cookieName;
+
+ private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
+
+ 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,
+ this.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)) {
+ this.gatewayStorage.storeGatewayInformation(request, this.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 this.cookieName;
+ }
+
+ public void setCookieName(String cookieName) {
+ this.cookieName = cookieName;
+ }
+
+}
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 0000000000..4a29ce0087
--- /dev/null
+++ b/cas/src/main/java/org/springframework/security/cas/web/TriggerCasGatewayFilter.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002-2023 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
+ *
+ * https://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 jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
+import org.apereo.cas.client.util.CommonUtils;
+import org.apereo.cas.client.util.WebUtils;
+
+import org.springframework.security.cas.ServiceProperties;
+import org.springframework.security.web.DefaultRedirectStrategy;
+import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
+import org.springframework.security.web.savedrequest.RequestCache;
+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 requires a web session to work. + *
+ * This filter must be placed after the {@link CasAuthenticationFilter} if it is defined. + *
+ * The default implementation is {@link CasCookieGatewayRequestMatcher}. + * + * @author Michael Remond + * @author Jerome LELEU + */ +public class TriggerCasGatewayFilter extends GenericFilterBean { + + public static final String TRIGGER_CAS_GATEWAY_AUTHENTICATION = "triggerCasGatewayAuthentication"; + + private final String loginUrl; + + private final ServiceProperties serviceProperties; + + private RequestMatcher requestMatcher; + + private RequestCache requestCache = new HttpSessionRequestCache(); + + public TriggerCasGatewayFilter(String loginUrl, ServiceProperties serviceProperties) { + this.loginUrl = loginUrl; + this.serviceProperties = serviceProperties; + this.requestMatcher = new CasCookieGatewayRequestMatcher(this.serviceProperties, null); + } + + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + if (this.requestMatcher.matches(request)) { + // Try a CAS gateway authentication + this.requestCache.saveRequest(request, response); + HttpSession session = request.getSession(false); + if (session != null) { + session.setAttribute(TRIGGER_CAS_GATEWAY_AUTHENTICATION, true); + } + String urlEncodedService = WebUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), + null, this.serviceProperties.getArtifactParameter(), true); + String redirectUrl = CommonUtils.constructRedirectUrl(this.loginUrl, + this.serviceProperties.getServiceParameter(), urlEncodedService, + this.serviceProperties.isSendRenew(), true); + new DefaultRedirectStrategy().sendRedirect(request, response, redirectUrl); + } + else { + // Continue in the chain + chain.doFilter(request, response); + } + + } + + public String getLoginUrl() { + return this.loginUrl; + } + + public ServiceProperties getServiceProperties() { + return this.serviceProperties; + } + + public RequestMatcher getRequestMatcher() { + return this.requestMatcher; + } + + public RequestCache getRequestCache() { + return this.requestCache; + } + + public void setRequestMatcher(RequestMatcher requestMatcher) { + Assert.notNull(requestMatcher, "requestMatcher cannot be null"); + this.requestMatcher = requestMatcher; + } + + public final void setRequestCache(RequestCache requestCache) { + Assert.notNull(requestCache, "requestCache cannot be null"); + this.requestCache = requestCache; + } + +} 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 46689b42e9..a2e0a3e2b7 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 @@ -36,6 +36,7 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -219,4 +220,19 @@ public class CasAuthenticationFilterTests { verify(securityContextRepository).saveContext(any(SecurityContext.class), eq(request), eq(response)); } + @Test + public void testNullServiceButGateway() throws Exception { + CasAuthenticationFilter filter = new CasAuthenticationFilter(); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + request.getSession(true).setAttribute(TriggerCasGatewayFilter.TRIGGER_CAS_GATEWAY_AUTHENTICATION, true); + + new HttpSessionRequestCache().saveRequest(request, response); + + Authentication authn = filter.attemptAuthentication(request, response); + assertThat(authn).isNull(); + assertThat(response.getStatus()).isEqualTo(302); + assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost?continue"); + } + } 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 0000000000..522303aea2 --- /dev/null +++ b/cas/src/test/java/org/springframework/security/cas/web/CasCookieGatewayRequestMatcherTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2023 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 + * + * https://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 jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.apereo.cas.client.authentication.DefaultGatewayResolverImpl; +import org.junit.jupiter.api.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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mock; + +/** + * Tests {@link CasCookieGatewayRequestMatche}. + * + * @author Michael Remond + */ +public class CasCookieGatewayRequestMatcherTests { + + @Test + public void testNullServiceProperties() throws Exception { + try { + new CasCookieGatewayRequestMatcher(null, null); + fail("Should have thrown IllegalArgumentException"); + } + catch (IllegalArgumentException expected) { + assertThat(expected.getMessage()).isEqualTo("serviceProperties cannot be null"); + } + } + + @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 + assertThat(rm.matches(request)).isTrue(); + assertThat(request.getSession(false).getAttribute(DefaultGatewayResolverImpl.CONST_CAS_GATEWAY)).isNotNull(); + // Second request + assertThat(rm.matches(request)).isFalse(); + assertThat(request.getSession(false).getAttribute(DefaultGatewayResolverImpl.CONST_CAS_GATEWAY)).isNotNull(); + } + + @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")); + + assertThat(rm.matches(request)).isTrue(); + + MockHttpServletRequest requestWithoutCasCookie = new MockHttpServletRequest("GET", "/some_path"); + requestWithoutCasCookie.setCookies(new Cookie("WRONG_CAS_TGT_COOKIE_TEST_NAME", "casTGCookieValue")); + + assertThat(rm.matches(requestWithoutCasCookie)).isFalse(); + } + + @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"); + assertThat(rm.matches(request)).isFalse(); + } + + @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"); + + assertThat(rm.matches(request)).isFalse(); + } + +} diff --git a/cas/src/test/java/org/springframework/security/cas/web/TriggerCasGatewayFilterTests.java b/cas/src/test/java/org/springframework/security/cas/web/TriggerCasGatewayFilterTests.java new file mode 100644 index 0000000000..3b19771390 --- /dev/null +++ b/cas/src/test/java/org/springframework/security/cas/web/TriggerCasGatewayFilterTests.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2023 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 + * + * https://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 jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; +import org.springframework.security.web.savedrequest.RequestCache; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Tests {@link TriggerCasGatewayFilter}. + * + * @author Jerome LELEU + */ +public class TriggerCasGatewayFilterTests { + + private static final String CAS_LOGIN_URL = "http://mycasserver/login"; + + @AfterEach + public void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + public void testGettersSetters() { + ServiceProperties sp = new ServiceProperties(); + TriggerCasGatewayFilter filter = new TriggerCasGatewayFilter(CAS_LOGIN_URL, sp); + assertThat(filter.getLoginUrl()).isEqualTo(CAS_LOGIN_URL); + assertThat(filter.getServiceProperties()).isEqualTo(sp); + assertThat(filter.getRequestMatcher().getClass()).isEqualTo(CasCookieGatewayRequestMatcher.class); + assertThat(filter.getRequestCache().getClass()).isEqualTo(HttpSessionRequestCache.class); + RequestMatcher requestMatcher = mock(RequestMatcher.class); + filter.setRequestMatcher(requestMatcher); + assertThat(filter.getRequestMatcher()).isEqualTo(requestMatcher); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> filter.setRequestMatcher(null)); + RequestCache requestCache = mock(RequestCache.class); + filter.setRequestCache(requestCache); + assertThat(filter.getRequestCache()).isEqualTo(requestCache); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> filter.setRequestCache(null)); + } + + @Test + public void testOperation() throws IOException, ServletException { + ServiceProperties sp = new ServiceProperties(); + sp.setService("http://myservice"); + TriggerCasGatewayFilter filter = new TriggerCasGatewayFilter(CAS_LOGIN_URL, sp); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain chain = mock(FilterChain.class); + + filter.doFilter(request, response, chain); + assertThat(filter.getRequestCache().getRequest(request, response)).isNotNull(); + assertThat(request.getSession(false).getAttribute(TriggerCasGatewayFilter.TRIGGER_CAS_GATEWAY_AUTHENTICATION)) + .isEqualTo(true); + assertThat(response.getStatus()).isEqualTo(302); + assertThat(response.getRedirectedUrl()) + .isEqualTo(CAS_LOGIN_URL + "?service=http%3A%2F%2Fmyservice&gateway=true"); + verify(chain, never()).doFilter(request, response); + + filter.doFilter(request, response, chain); + verify(chain, times(1)).doFilter(request, response); + } + +}