diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java
index b35199c5828..92f7c6e517e 100644
--- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java
+++ b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java
@@ -56,6 +56,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
+ public static final String DEFAULT_REMEMBERME_ATTRIBUTE_NAME = "longTermAuthenticationRequestTokenUsed";
//~ Instance fields ================================================================================================
@@ -68,6 +69,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private TicketValidator ticketValidator;
private ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
+ private String rememberMeAttributeName = DEFAULT_REMEMBERME_ATTRIBUTE_NAME;
//~ Methods ========================================================================================================
@@ -141,7 +143,7 @@ private CasAuthenticationToken authenticateNow(final Authentication authenticati
final UserDetails userDetails = loadUserByAssertion(assertion);
userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
- authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
+ authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion, rememberMeAttributeName);
} catch (final TicketValidationException e) {
throw new BadCredentialsException(e.getMessage(), e);
}
@@ -234,6 +236,14 @@ public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
+ public String getRememberMeAttributeName() {
+ return rememberMeAttributeName;
+ }
+
+ public void setRememberMeAttributeName(String rememberMeAttributeName) {
+ this.rememberMeAttributeName = rememberMeAttributeName;
+ }
+
public boolean supports(final Class> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) ||
(CasAuthenticationToken.class.isAssignableFrom(authentication)) ||
diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java
index ffa78978096..ee0c972e90b 100644
--- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java
+++ b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java
@@ -20,6 +20,7 @@
import org.jasig.cas.client.validation.Assertion;
import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.RememberMeAware;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.UserDetails;
@@ -30,7 +31,7 @@
* @author Ben Alex
* @author Scott Battaglia
*/
-public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
+public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable, RememberMeAware {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@@ -40,6 +41,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
private final UserDetails userDetails;
private final int keyHash;
private final Assertion assertion;
+ private final boolean rememberMe;
//~ Constructors ===================================================================================================
@@ -57,15 +59,18 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
* org.springframework.security.core.userdetails.UserDetailsService}) (cannot be null
)
* @param assertion the assertion returned from the CAS servers. It contains the principal and how to obtain a
* proxy ticket for the user.
+ * @param rememberMeAttributeName the CAS remember-me attribute name
*
* @throws IllegalArgumentException if a null
was passed
*/
public CasAuthenticationToken(final String key, final Object principal, final Object credentials,
- final Collection extends GrantedAuthority> authorities, final UserDetails userDetails, final Assertion assertion) {
+ final Collection extends GrantedAuthority> authorities, final UserDetails userDetails, final Assertion assertion,
+ String rememberMeAttributeName) {
super(authorities);
if ((key == null) || ("".equals(key)) || (principal == null) || "".equals(principal) || (credentials == null)
- || "".equals(credentials) || (authorities == null) || (userDetails == null) || (assertion == null)) {
+ || "".equals(credentials) || (authorities == null) || (userDetails == null) || (assertion == null) ||
+ (rememberMeAttributeName == null)) {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
@@ -74,6 +79,8 @@ public CasAuthenticationToken(final String key, final Object principal, final Ob
this.credentials = credentials;
this.userDetails = userDetails;
this.assertion = assertion;
+ String rememberMeStringValue = (String) assertion.getPrincipal().getAttributes().get(rememberMeAttributeName);
+ this.rememberMe = rememberMeStringValue != null && Boolean.parseBoolean(rememberMeStringValue);
setAuthenticated(true);
}
@@ -121,9 +128,14 @@ public UserDetails getUserDetails() {
return userDetails;
}
+ public boolean isRememberMe() {
+ return rememberMe;
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
+ sb.append(" RememberMe: ").append(this.rememberMe);
sb.append(" Assertion: ").append(this.assertion);
sb.append(" Credentials (Service/Proxy Ticket): ").append(this.credentials);
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 a06b73dfade..bd8e9fe4e05 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
@@ -36,8 +36,9 @@
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
* This page is specified by the loginUrl
property. Once login is complete, the CAS login page will
* redirect to the page indicated by the service
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.
+ * belonging to the current application. The renew
parameter can be forced to true when redirecting
+ * to the CAS server. The service
URL is monitored by the {@link CasAuthenticationFilter}, which will
+ * validate the CAS login was successful.
*
* @author Ben Alex
* @author Scott Battaglia
@@ -68,11 +69,18 @@ public void afterPropertiesSet() throws Exception {
Assert.notNull(this.serviceProperties.getService(),"serviceProperties.getService() cannot be null.");
}
+
+ public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
+ final AuthenticationException authenticationException) throws IOException, ServletException {
+ // by default, don't force the renew parameter to true
+ commence(servletRequest, response, authenticationException, false);
+ }
+
public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
- final AuthenticationException authenticationException) throws IOException, ServletException {
+ final AuthenticationException authenticationException, boolean forceRenew) throws IOException, ServletException {
final String urlEncodedService = createServiceUrl(servletRequest, response);
- final String redirectUrl = createRedirectUrl(urlEncodedService);
+ final String redirectUrl = createRedirectUrl(urlEncodedService, forceRenew);
preCommence(servletRequest, response);
@@ -91,12 +99,19 @@ protected String createServiceUrl(final HttpServletRequest request, final HttpSe
/**
* Constructs the Url for Redirection to the CAS server. Default implementation relies on the CAS client to do the bulk of the work.
+ * The renew parameter can be forced to true if the forceRenew parameter is true.
*
* @param serviceUrl the service url that should be included.
+ * @param forceRenew if the renew parameter should be forced to true
* @return the redirect url. CANNOT be NULL.
*/
- protected String createRedirectUrl(final String serviceUrl) {
- return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl, this.serviceProperties.isSendRenew(), false);
+ protected String createRedirectUrl(final String serviceUrl, final boolean forceRenew) {
+ boolean renew = this.serviceProperties.isSendRenew();
+ // if forceRenew is true, the renew parameter is forced to true
+ if (forceRenew) {
+ renew = true;
+ }
+ return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl, renew, false);
}
/**
diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasRememberMeAccessDeniedHandler.java b/cas/src/main/java/org/springframework/security/cas/web/CasRememberMeAccessDeniedHandler.java
new file mode 100644
index 00000000000..f0ce8c69483
--- /dev/null
+++ b/cas/src/main/java/org/springframework/security/cas/web/CasRememberMeAccessDeniedHandler.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012 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.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+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.access.AccessDeniedHandlerImpl;
+import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
+import org.springframework.security.web.savedrequest.RequestCache;
+import org.springframework.util.Assert;
+
+/**
+ * This class represents the AccessDeniedHandlerImpl
dedicated to CAS authentication with remember-me support.
+ *
+ * @author Jerome Leleu
+ * @since 3.2.0
+ */
+public class CasRememberMeAccessDeniedHandler extends AccessDeniedHandlerImpl implements InitializingBean {
+
+ private RequestCache requestCache = new HttpSessionRequestCache();
+
+ private CasAuthenticationEntryPoint casAuthenticationEntryPoint;
+
+ public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
+ throws IOException, ServletException {
+
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ // if CAS authentication token
+ if (authentication != null && authentication instanceof CasAuthenticationToken) {
+ CasAuthenticationToken casAuthenticationToken = (CasAuthenticationToken) authentication;
+ // in remember-me mode
+ if (casAuthenticationToken.isRememberMe()) {
+ requestCache.saveRequest(request, response);
+ logger.debug("Calling Authentication entry point with renew=true.");
+ casAuthenticationEntryPoint.commence(request, response,
+ new InsufficientAuthenticationException("Full CAS authentication is required to access this resource"), true);
+ return;
+ }
+ }
+
+ super.handle(request, response, accessDeniedException);
+ }
+
+ public RequestCache getRequestCache() {
+ return requestCache;
+ }
+
+ public void setRequestCache(RequestCache requestCache) {
+ this.requestCache = requestCache;
+ }
+
+ public CasAuthenticationEntryPoint getCasAuthenticationEntryPoint() {
+ return casAuthenticationEntryPoint;
+ }
+
+ public void setCasAuthenticationEntryPoint(CasAuthenticationEntryPoint casAuthenticationEntryPoint) {
+ this.casAuthenticationEntryPoint = casAuthenticationEntryPoint;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(this.casAuthenticationEntryPoint, "casAuthenticationEntryPoint must be specified");
+ }
+}
diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java
index a62d3f60494..c08bb7186dc 100644
--- a/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java
+++ b/cas/src/test/java/org/springframework/security/cas/authentication/AbstractStatelessTicketCacheTests.java
@@ -25,7 +25,7 @@ protected CasAuthenticationToken getToken() {
final Assertion assertion = new AssertionImpl("rod");
return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ",
- AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"), user, assertion);
+ AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"), user, assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
}
}
diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java
index 495ded74d83..5c0ab2b03a9 100644
--- a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java
+++ b/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java
@@ -18,6 +18,8 @@
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
+import org.jasig.cas.client.authentication.AttributePrincipal;
+import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.AssertionImpl;
import org.jasig.cas.client.validation.TicketValidationException;
@@ -50,6 +52,7 @@
*/
@SuppressWarnings("unchecked")
public class CasAuthenticationProviderTests {
+ public static final String TEST_REMEMBERME_ATTRIBUTE_NAME = "testRememberMeName";
//~ Methods ========================================================================================================
private UserDetails makeUserDetails() {
@@ -80,7 +83,7 @@ public void statefulAuthenticationIsSuccessful() throws Exception {
cap.setStatelessTicketCache(cache);
cap.setServiceProperties(makeServiceProperties());
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token =
@@ -103,15 +106,61 @@ public void statefulAuthenticationIsSuccessful() throws Exception {
assertTrue(casResult.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_B")));
assertEquals(cap.getKey().hashCode(), casResult.getKeyHash());
assertEquals("details", casResult.getDetails());
+ assertFalse(casResult.isRememberMe());
// Now confirm the CasAuthenticationToken is automatically re-accepted.
// To ensure TicketValidator not called again, set it to deliver an exception...
- cap.setTicketValidator(new MockTicketValidator(false));
+ cap.setTicketValidator(new MockTicketValidator(false, null));
Authentication laterResult = cap.authenticate(result);
assertEquals(result, laterResult);
}
+ @Test
+ public void testRememberMeDefaultAttributeName() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
+ cap.setKey("qwerty");
+
+ StatelessTicketCache cache = new MockStatelessTicketCache();
+ cap.setStatelessTicketCache(cache);
+ cap.setServiceProperties(makeServiceProperties());
+
+ cap.setTicketValidator(new MockTicketValidator(true, CasAuthenticationProvider.DEFAULT_REMEMBERME_ATTRIBUTE_NAME));
+ cap.afterPropertiesSet();
+
+ UsernamePasswordAuthenticationToken token =
+ new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-124");
+
+ Authentication result = cap.authenticate(token);
+
+ CasAuthenticationToken casResult = (CasAuthenticationToken) result;
+ assertTrue(casResult.isRememberMe());
+ }
+
+ @Test
+ public void testRememberMeNewAttributeName() throws Exception {
+ CasAuthenticationProvider cap = new CasAuthenticationProvider();
+ cap.setRememberMeAttributeName(TEST_REMEMBERME_ATTRIBUTE_NAME);
+ cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
+ cap.setKey("qwerty");
+
+ StatelessTicketCache cache = new MockStatelessTicketCache();
+ cap.setStatelessTicketCache(cache);
+ cap.setServiceProperties(makeServiceProperties());
+
+ cap.setTicketValidator(new MockTicketValidator(true, TEST_REMEMBERME_ATTRIBUTE_NAME));
+ cap.afterPropertiesSet();
+
+ UsernamePasswordAuthenticationToken token =
+ new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-125");
+
+ Authentication result = cap.authenticate(token);
+
+ CasAuthenticationToken casResult = (CasAuthenticationToken) result;
+ assertTrue(casResult.isRememberMe());
+ }
+
@Test
public void statelessAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
@@ -120,7 +169,7 @@ public void statelessAuthenticationIsSuccessful() throws Exception {
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
@@ -143,7 +192,7 @@ public void statelessAuthenticationIsSuccessful() throws Exception {
// Now try to authenticate again. To ensure TicketValidator not
// called again, set it to deliver an exception...
- cap.setTicketValidator(new MockTicketValidator(false));
+ cap.setTicketValidator(new MockTicketValidator(false, null));
// Previously created UsernamePasswordAuthenticationToken is OK
Authentication newResult = cap.authenticate(token);
@@ -240,7 +289,7 @@ public void missingTicketIdIsDetected() throws Exception {
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
@@ -259,12 +308,12 @@ public void invalidKeyIsDetected() throws Exception {
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasAuthenticationToken token = new CasAuthenticationToken("WRONG_KEY", makeUserDetails(), "credentials",
- AuthorityUtils.createAuthorityList("XX"), makeUserDetails(), assertion);
+ AuthorityUtils.createAuthorityList("XX"), makeUserDetails(), assertion, TEST_REMEMBERME_ATTRIBUTE_NAME);
cap.authenticate(token);
}
@@ -274,7 +323,7 @@ public void detectsMissingAuthoritiesPopulator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@@ -284,7 +333,7 @@ public void detectsMissingKey() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setStatelessTicketCache(new MockStatelessTicketCache());
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@@ -296,7 +345,7 @@ public void detectsMissingStatelessTicketCache() throws Exception {
cap.setStatelessTicketCache(null);
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@@ -317,7 +366,7 @@ public void gettersAndSettersMatch() throws Exception {
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
@@ -334,7 +383,7 @@ public void ignoresClassesItDoesNotSupport() throws Exception {
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
@@ -351,7 +400,7 @@ public void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPr
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
- cap.setTicketValidator(new MockTicketValidator(true));
+ cap.setTicketValidator(new MockTicketValidator(true, null));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
@@ -398,15 +447,27 @@ public void removeTicketFromCache(String serviceTicket) {
private class MockTicketValidator implements TicketValidator {
private boolean returnTicket;
+ private String rememberMeAttributeName;
- public MockTicketValidator(boolean returnTicket) {
+ public MockTicketValidator(boolean returnTicket, String rememberMeAttributeName) {
this.returnTicket = returnTicket;
+ this.rememberMeAttributeName = rememberMeAttributeName;
}
+ @SuppressWarnings("rawtypes")
public Assertion validate(final String ticket, final String service)
throws TicketValidationException {
if (returnTicket) {
- return new AssertionImpl("rod");
+ Assertion assertion;
+ if (rememberMeAttributeName != null) {
+ Map attributes = new HashMap();
+ attributes.put(rememberMeAttributeName, "true");
+ final AttributePrincipal principal = new AttributePrincipalImpl("rod", attributes);
+ assertion = new AssertionImpl(principal);
+ } else {
+ assertion = new AssertionImpl("rod");
+ }
+ return assertion;
}
throw new BadCredentialsException("As requested from mock");
}
diff --git a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java b/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java
index 16dc11f1d67..c9ee389cffa 100644
--- a/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java
+++ b/cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java
@@ -16,6 +16,9 @@
package org.springframework.security.cas.authentication;
import junit.framework.TestCase;
+
+import org.jasig.cas.client.authentication.AttributePrincipal;
+import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.AssertionImpl;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -50,51 +53,59 @@ public final void setUp() throws Exception {
public void testConstructorRejectsNulls() {
final Assertion assertion = new AssertionImpl("test");
try {
- new CasAuthenticationToken(null, makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
+ new CasAuthenticationToken(null, makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
- new CasAuthenticationToken("key", null, "Password", ROLES, makeUserDetails(), assertion);
+ new CasAuthenticationToken("key", null, "Password", ROLES, makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
- new CasAuthenticationToken("key", makeUserDetails(), null, ROLES, makeUserDetails(), assertion);
+ new CasAuthenticationToken("key", makeUserDetails(), null, ROLES, makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
- new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, makeUserDetails(), null);
+ new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, makeUserDetails(), null, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
- new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, null, assertion);
+ new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, null, assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
- new CasAuthenticationToken("key", makeUserDetails(), "Password", AuthorityUtils.createAuthorityList("ROLE_1", null), makeUserDetails(), assertion);
+ new CasAuthenticationToken("key", makeUserDetails(), "Password", AuthorityUtils.createAuthorityList("ROLE_1", null),
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
- }
+
+ try {
+ new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion, null);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ assertTrue(true);
+ }
+}
public void testEqualsWhenEqual() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
assertEquals(token1, token2);
}
@@ -103,7 +114,7 @@ public void testGetters() {
// Build the proxy list returned in the ticket from CAS
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
assertEquals("key".hashCode(), token.getKeyHash());
assertEquals(makeUserDetails(), token.getPrincipal());
assertEquals("Password", token.getCredentials());
@@ -111,6 +122,7 @@ public void testGetters() {
assertTrue(token.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_TWO")));
assertEquals(assertion, token.getAssertion());
assertEquals(makeUserDetails().getUsername(), token.getUserDetails().getUsername());
+ assertFalse(token.isRememberMe());
}
public void testNoArgConstructorDoesntExist() {
@@ -126,10 +138,10 @@ public void testNotEqualsDueToAbstractParentEqualsCheck() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails("OTHER_NAME"), "Password",
- ROLES, makeUserDetails(), assertion);
+ ROLES, makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
assertTrue(!token1.equals(token2));
}
@@ -138,7 +150,7 @@ public void testNotEqualsDueToDifferentAuthenticationClass() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", "Password", ROLES);
assertTrue(!token1.equals(token2));
@@ -148,10 +160,10 @@ public void testNotEqualsDueToKey() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
CasAuthenticationToken token2 = new CasAuthenticationToken("DIFFERENT_KEY", makeUserDetails(), "Password",
- ROLES, makeUserDetails(), assertion);
+ ROLES, makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
assertTrue(!token1.equals(token2));
}
@@ -161,10 +173,10 @@ public void testNotEqualsDueToAssertion() {
final Assertion assertion2 = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion2);
+ makeUserDetails(), assertion2, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
assertTrue(!token1.equals(token2));
}
@@ -172,16 +184,29 @@ public void testNotEqualsDueToAssertion() {
public void testSetAuthenticated() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
}
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ public void testIsRemember() {
+ Map attributes = new HashMap();
+ attributes.put(CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME, "true");
+ final AttributePrincipal principal = new AttributePrincipalImpl("test", attributes);
+ final Assertion assertion = new AssertionImpl(principal);
+ CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
+ assertTrue(token.isRememberMe());
+ }
+
public void testToString() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password",ROLES,
- makeUserDetails(), assertion);
+ makeUserDetails(), assertion, CasAuthenticationProviderTests.TEST_REMEMBERME_ATTRIBUTE_NAME);
String result = token.toString();
assertTrue(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1);
}
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 5349fc4e944..449de1a241d 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
@@ -108,4 +108,25 @@ public void testNormalOperationWithRenewTrue() throws Exception {
+ URLEncoder.encode("https://mycompany.com/bigWebApp/j_spring_cas_security_check", "UTF-8") + "&renew=true",
response.getRedirectedUrl());
}
+
+ public void testNormalOperationWithForceRenewTrue() 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, null, true);
+ assertEquals("https://cas/login?service="
+ + URLEncoder.encode("https://mycompany.com/bigWebApp/j_spring_cas_security_check", "UTF-8") + "&renew=true",
+ response.getRedirectedUrl());
+ }
}
diff --git a/core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java b/core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java
index 8dc03cdfbfc..3f5955baa94 100644
--- a/core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java
+++ b/core/src/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java
@@ -21,12 +21,14 @@
/**
* Basic implementation of {@link AuthenticationTrustResolver}.
*
- * Makes trust decisions based on whether the passed Authentication
is an instance of a defined class.
+ * Makes trust decisions based on whether the passed Authentication
is an instance of a defined class or
+ * implements the {@link RememberMeAware} interface and its isRemember()
method returns true
.
*
* If {@link #anonymousClass} or {@link #rememberMeClass} is This is a protected page. You can get to me if you've been authenticated. This is a protected page. You can get to me if you've authenticated this session. Your principal object is....: <%= request.getUserPrincipal() %>null
, the corresponding method will
* always return false
.
*
* @author Ben Alex
+ * @see RememberMeAware#isRememberMe()
*/
public class AuthenticationTrustResolverImpl implements AuthenticationTrustResolver {
//~ Instance fields ================================================================================================
@@ -57,7 +59,15 @@ public boolean isRememberMe(Authentication authentication) {
return false;
}
- return rememberMeClass.isAssignableFrom(authentication.getClass());
+ if (rememberMeClass.isAssignableFrom(authentication.getClass())) {
+ return true;
+ }
+
+ if (authentication instanceof RememberMeAware) {
+ return ((RememberMeAware) authentication).isRememberMe();
+ }
+
+ return false;
}
public void setAnonymousClass(Class extends Authentication> anonymousClass) {
diff --git a/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java b/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java
index 2315f0fab89..a54e6ad62a9 100644
--- a/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java
+++ b/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java
@@ -30,7 +30,7 @@
* @author Ben Alex
* @author Luke Taylor
*/
-public class RememberMeAuthenticationToken extends AbstractAuthenticationToken {
+public class RememberMeAuthenticationToken extends AbstractAuthenticationToken implements RememberMeAware {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@@ -99,4 +99,12 @@ public boolean equals(Object obj) {
return false;
}
+ /**
+ * This method always returns true as a RememberMeAuthenticationToken
is always considered as remember-me.
+ *
+ * @return true
+ */
+ public final boolean isRememberMe() {
+ return true;
+ }
}
diff --git a/core/src/main/java/org/springframework/security/authentication/RememberMeAware.java b/core/src/main/java/org/springframework/security/authentication/RememberMeAware.java
new file mode 100644
index 00000000000..70baa1d5412
--- /dev/null
+++ b/core/src/main/java/org/springframework/security/authentication/RememberMeAware.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2012 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.authentication;
+
+/**
+ * This interface represents the ability for an authentication token to be considered as remember-me.
+ *
+ * @author Jerome Leleu
+ * @since 3.2.0
+ */
+public interface RememberMeAware {
+
+ public boolean isRememberMe();
+}
diff --git a/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java b/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java
index fe2f0b476d0..f0bf53f5684 100644
--- a/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java
+++ b/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java
@@ -15,7 +15,6 @@
package org.springframework.security.authentication;
-import java.util.Arrays;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
@@ -29,12 +28,13 @@
*
* @author Ben Alex
*/
-public class TestingAuthenticationToken extends AbstractAuthenticationToken {
+public class TestingAuthenticationToken extends AbstractAuthenticationToken implements RememberMeAware {
//~ Instance fields ================================================================================================
private static final long serialVersionUID = 1L;
private final Object credentials;
private final Object principal;
+ private final boolean isRemember;
//~ Constructors ===================================================================================================
@@ -42,6 +42,7 @@ public TestingAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
+ this.isRemember = false;
}
public TestingAuthenticationToken(Object principal, Object credentials, String... authorities) {
@@ -53,6 +54,14 @@ public TestingAuthenticationToken(Object principal, Object credentials, ListisAuthenticated() Page
+isFullyAuthenticated() Page
+