Browse Source

SEC-1228

started adding support for supporting creating UserDetails via Assertions.
Scott Battaglia 16 years ago
parent
commit
53baac2fd9

+ 20 - 0
cas/src/main/java/org/springframework/security/cas/SamlServiceProperties.java

@@ -0,0 +1,20 @@
+package org.springframework.security.cas;
+
+/**
+ * Sets the appropriate parameters for CAS's implementation of SAML (which is not guaranteed to be actually SAML compliant).
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.0
+ */
+public final class SamlServiceProperties extends ServiceProperties {
+
+    public static final String DEFAULT_SAML_ARTIFACT_PARAMETER = "SAMLart";
+
+    public static final String DEFAULT_SAML_SERVICE_PARAMETER = "TARGET";
+
+    public SamlServiceProperties() {
+        super.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER);
+        super.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER);
+    }
+}

+ 44 - 6
cas/src/main/java/org/springframework/security/cas/ServiceProperties.java

@@ -30,15 +30,27 @@ import org.springframework.util.Assert;
  * @version $Id$
  */
 public class ServiceProperties implements InitializingBean {
+
+    public static final String DEFAULT_CAS_ARTIFACT_PARAMETER = "ticket";
+
+    public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
+
     //~ Instance fields ================================================================================================
 
     private String service;
+
     private boolean sendRenew = false;
 
+    private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
+
+    private String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER;
+
     //~ Methods ========================================================================================================
 
     public void afterPropertiesSet() throws Exception {
         Assert.hasLength(this.service, "service must be specified.");
+        Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
+        Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
     }
 
     /**
@@ -52,8 +64,8 @@ public class ServiceProperties implements InitializingBean {
      *
      * @return the URL of the service the user is authenticating to
      */
-    public String getService() {
-        return service;
+    public final String getService() {
+        return this.service;
     }
 
     /**
@@ -67,15 +79,41 @@ public class ServiceProperties implements InitializingBean {
      *
      * @return whether to send the <code>renew</code> parameter to CAS
      */
-    public boolean isSendRenew() {
-        return sendRenew;
+    public final boolean isSendRenew() {
+        return this.sendRenew;
     }
 
-    public void setSendRenew(boolean sendRenew) {
+    public final void setSendRenew(final boolean sendRenew) {
         this.sendRenew = sendRenew;
     }
 
-    public void setService(String service) {
+    public final void setService(final String service) {
         this.service = service;
     }
+
+    public final String getArtifactParameter() {
+        return this.artifactParameter;
+    }
+
+    /**
+     * Configures the Request Parameter to look for when attempting to see if a CAS ticket was sent from the server.
+     *
+     * @param artifactParameter the id to use.  Default is "ticket".
+     */
+    public final void setArtifactParameter(final String artifactParameter) {
+        this.artifactParameter = artifactParameter;
+    }
+
+    /**
+     * Configures the Request parameter to look for when attempting to send a request to CAS.
+     *
+     * @return the service parameter to use.  Default is "service".
+     */
+    public final String getServiceParameter() {
+        return serviceParameter;
+    }
+
+    public final void setServiceParameter(final String serviceParameter) {
+        this.serviceParameter = serviceParameter;
+    }
 }

+ 41 - 0
cas/src/main/java/org/springframework/security/cas/authentication/CasAssertionAuthenticationToken.java

@@ -0,0 +1,41 @@
+package org.springframework.security.cas.authentication;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.jasig.cas.client.validation.Assertion;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Temporary authentication object needed to load the user details service.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.0
+ */
+public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {
+
+    private final Assertion assertion;
+
+    private final String ticket;
+
+    public CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) {
+        super(new ArrayList<GrantedAuthority>());
+
+        this.assertion = assertion;
+        this.ticket = ticket;
+    }
+
+    public Object getPrincipal() {
+        return this.assertion.getPrincipal().getName();
+    }
+
+    public Object getCredentials() {
+        return this.ticket;
+    }
+
+    public Assertion getAssertion() {
+        return this.assertion;
+    }
+}

+ 17 - 12
cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java

@@ -31,9 +31,7 @@ import org.springframework.security.cas.web.CasProcessingFilter;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.SpringSecurityMessageSource;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsChecker;
-import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.*;
 import org.springframework.util.Assert;
 
 
@@ -47,13 +45,15 @@ import org.springframework.util.Assert;
  * It can also validate a previously created {@link CasAuthenticationToken}.
  *
  * @author Ben Alex
+ * @author Scott Battaglia
  * @version $Id$
  */
 public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
 
     //~ Instance fields ================================================================================================
 
-    private UserDetailsService userDetailsService;
+    private AuthenticationUserDetailsService authenticationUserDetailsService;
+
     private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
     protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
     private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
@@ -64,7 +64,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
     //~ Methods ========================================================================================================
 
     public void afterPropertiesSet() throws Exception {
-        Assert.notNull(this.userDetailsService, "A userDetailsService must be set");
+        Assert.notNull(this.authenticationUserDetailsService, "An authenticationUserDetailsService must be set");
         Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
         Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
         Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
@@ -127,7 +127,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
         return result;
     }
 
-    private final CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
+    private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
         try {
             final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), serviceProperties.getService());
             final UserDetails userDetails = loadUserByAssertion(assertion);
@@ -143,18 +143,23 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
      * can override this method and retrieve the user based on any criteria they desire.
      *
      * @param assertion The CAS Assertion.
-     * @returns the UserDetails.
+     * @return the UserDetails.
      */
     protected UserDetails loadUserByAssertion(final Assertion assertion) {
-        return this.userDetailsService.loadUserByUsername(assertion.getPrincipal().getName());
+        final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "");
+        return this.authenticationUserDetailsService.loadUserDetails(token);
     }
 
-    protected UserDetailsService getUserDetailsService() {
-        return userDetailsService;
+    @Deprecated
+    /**
+     * @deprecated as of 3.0.  Use the {@link org.springframework.security.cas.authentication.CasAuthenticationProvider#setAuthenticationUserDetailsService(org.springframework.security.core.userdetails.AuthenticationUserDetailsService)} instead.
+     */
+    public void setUserDetailsService(final UserDetailsService userDetailsService) {
+        this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService);
     }
 
-    public void setUserDetailsService(final UserDetailsService userDetailsService) {
-        this.userDetailsService = userDetailsService;
+    public void setAuthenticationUserDetailsService(final AuthenticationUserDetailsService authenticationUserDetailsService) {
+        this.authenticationUserDetailsService = authenticationUserDetailsService;
     }
 
     public void setServiceProperties(final ServiceProperties serviceProperties) {

+ 14 - 19
cas/src/main/java/org/springframework/security/cas/authentication/EhCacheBasedTicketCache.java

@@ -51,23 +51,18 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
         Assert.notNull(cache, "cache mandatory");
     }
 
-    public CasAuthenticationToken getByTicketId(String serviceTicket) {
-        Element element = null;
-
+    public CasAuthenticationToken getByTicketId(final String serviceTicket) {
         try {
-            element = cache.get(serviceTicket);
-        } catch (CacheException cacheException) {
-            throw new DataRetrievalFailureException("Cache failure: " + cacheException.getMessage());
-        }
+            final Element element = cache.get(serviceTicket);
 
-        if (logger.isDebugEnabled()) {
-            logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
-        }
+            if (logger.isDebugEnabled()) {
+                logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
+            }
+
+            return element == null ? null : (CasAuthenticationToken) element.getValue();
 
-        if (element == null) {
-            return null;
-        } else {
-            return (CasAuthenticationToken) element.getValue();
+        } catch (CacheException cacheException) {
+            throw new DataRetrievalFailureException("Cache failure: " + cacheException.getMessage());
         }
     }
 
@@ -75,8 +70,8 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
         return cache;
     }
 
-    public void putTicketInCache(CasAuthenticationToken token) {
-        Element element = new Element(token.getCredentials().toString(), token);
+    public void putTicketInCache(final CasAuthenticationToken token) {
+        final Element element = new Element(token.getCredentials().toString(), token);
 
         if (logger.isDebugEnabled()) {
             logger.debug("Cache put: " + element.getKey());
@@ -85,7 +80,7 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
         cache.put(element);
     }
 
-    public void removeTicketFromCache(CasAuthenticationToken token) {
+    public void removeTicketFromCache(final CasAuthenticationToken token) {
         if (logger.isDebugEnabled()) {
             logger.debug("Cache remove: " + token.getCredentials().toString());
         }
@@ -93,11 +88,11 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache, Initializi
         this.removeTicketFromCache(token.getCredentials().toString());
     }
 
-    public void removeTicketFromCache(String serviceTicket) {
+    public void removeTicketFromCache(final String serviceTicket) {
         cache.remove(serviceTicket);
     }
 
-    public void setCache(Ehcache cache) {
+    public void setCache(final Ehcache cache) {
         this.cache = cache;
     }
 }

+ 35 - 0
cas/src/main/java/org/springframework/security/cas/userdetails/AbstractCasAssertionUserDetailsService.java

@@ -0,0 +1,35 @@
+package org.springframework.security.cas.userdetails;
+
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.cas.authentication.CasAuthenticationToken;
+import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
+import org.springframework.util.Assert;
+import org.jasig.cas.client.validation.Assertion;
+
+/**
+ * Abstract class for using the provided CAS assertion to construct a new User object.  This generally is most
+ * useful when combined with a SAML-based response from the CAS Server/client.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.0
+ */
+public abstract class AbstractCasAssertionUserDetailsService implements AuthenticationUserDetailsService {
+
+    public final UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException {
+        Assert.isInstanceOf(CasAuthenticationToken.class, token, "The provided token MUST be an instance of CasAuthenticationToken.class");
+        return loadUserDetails(((CasAssertionAuthenticationToken) token).getAssertion());
+    }
+
+    /**
+     * Protected template method for construct a {@link org.springframework.security.core.userdetails.UserDetails} via the supplied CAS
+     * assertion.
+     *
+     * @param assertion the assertion to use to construct the new UserDetails.  CANNOT be NULL.
+     * @return the newly constructed UserDetails.
+     */
+    protected abstract UserDetails loadUserDetails(Assertion assertion);
+}

+ 57 - 0
cas/src/main/java/org/springframework/security/cas/userdetails/GrantedAuthorityFromAssertionAttributesUserDetailsService.java

@@ -0,0 +1,57 @@
+package org.springframework.security.cas.userdetails;
+
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.GrantedAuthorityImpl;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.jasig.cas.client.validation.Assertion;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by reading a list of attributes that were returned as
+ * part of the CAS response.  Each attribute is read and each value of the attribute is turned into a GrantedAuthority.  If the attribute has no
+ * value then its not added.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.0
+ */
+public final class GrantedAuthorityFromAssertionAttributesUserDetailsService extends AbstractCasAssertionUserDetailsService implements InitializingBean {
+
+    private String[] attributes;
+
+    @Override
+    protected UserDetails loadUserDetails(final Assertion assertion) {
+        final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
+
+        for (final String attribute : this.attributes) {
+            final Object attributes = assertion.getPrincipal().getAttributes().get(attribute);
+
+            if (attributes == null) {
+                continue;
+            }
+
+            if (attributes instanceof List) {
+                final List list = (List) attributes;
+
+                for (final Object o : list) {
+                    grantedAuthorities.add(new GrantedAuthorityImpl(o.toString()));
+                }
+
+            } else {
+                grantedAuthorities.add(new GrantedAuthorityImpl(attributes.toString()));
+            }
+
+        }
+
+        return new User(assertion.getPrincipal().getName(), null, true, true, true, true, grantedAuthorities);
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        Assert.isTrue(attributes != null && attributes.length > 0, "At least one attribute is required to retrieve roles from.");
+    }
+}

+ 9 - 3
cas/src/main/java/org/springframework/security/cas/web/CasProcessingFilter.java

@@ -79,6 +79,8 @@ public class CasProcessingFilter extends AbstractAuthenticationProcessingFilter
      */
     private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
 
+    private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
+
     //~ Constructors ===================================================================================================
 
     public CasProcessingFilter() {
@@ -87,16 +89,16 @@ public class CasProcessingFilter extends AbstractAuthenticationProcessingFilter
 
     //~ Methods ========================================================================================================
 
-    public Authentication attemptAuthentication(final HttpServletRequest request, HttpServletResponse response)
+    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
             throws AuthenticationException {
         final String username = CAS_STATEFUL_IDENTIFIER;
-        String password = request.getParameter("ticket");
+        String password = request.getParameter(this.artifactParameter);
 
         if (password == null) {
             password = "";
         }
 
-        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
+        final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
 
         authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
 
@@ -129,4 +131,8 @@ public class CasProcessingFilter extends AbstractAuthenticationProcessingFilter
             final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
         this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
     }
+
+    public final void setServiceProperties(final ServiceProperties serviceProperties) {
+        this.artifactParameter = serviceProperties.getArtifactParameter();
+    }
 }

+ 4 - 5
cas/src/main/java/org/springframework/security/cas/web/CasProcessingFilterEntryPoint.java

@@ -45,8 +45,8 @@ import org.springframework.util.Assert;
  */
 public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
     //~ Instance fields ================================================================================================
-
     private ServiceProperties serviceProperties;
+
     private String loginUrl;
 
     /**
@@ -66,12 +66,11 @@ public class CasProcessingFilterEntryPoint implements AuthenticationEntryPoint,
         Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
     }
 
-    public void commence(final HttpServletRequest servletRequest, final HttpServletResponse servletResponse,
+    public void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
             final AuthenticationException authenticationException) throws IOException, ServletException {
 
-        final HttpServletResponse response = (HttpServletResponse) servletResponse;
-        final String urlEncodedService = CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, "ticket", this.encodeServiceUrlWithSessionId);
-        final String redirectUrl = CommonUtils.constructRedirectUrl(this.loginUrl, "service", urlEncodedService, this.serviceProperties.isSendRenew(), false);
+        final String urlEncodedService = CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, this.serviceProperties.getArtifactParameter(), this.encodeServiceUrlWithSessionId);
+        final String redirectUrl = CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), urlEncodedService, this.serviceProperties.isSendRenew(), false);
 
         response.sendRedirect(redirectUrl);
     }

+ 16 - 17
cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationProviderTests.java

@@ -29,10 +29,7 @@ import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.authority.GrantedAuthorityImpl;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
-
+import org.springframework.security.core.userdetails.*;
 
 
 import java.util.HashMap;
@@ -77,7 +74,7 @@ public class CasAuthenticationProviderTests {
     @Test
     public void statefulAuthenticationIsSuccessful() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
 
         StatelessTicketCache cache = new MockStatelessTicketCache();
@@ -119,7 +116,7 @@ public class CasAuthenticationProviderTests {
     @Test
     public void statelessAuthenticationIsSuccessful() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
 
         StatelessTicketCache cache = new MockStatelessTicketCache();
@@ -158,7 +155,7 @@ public class CasAuthenticationProviderTests {
     @Test(expected = BadCredentialsException.class)
     public void missingTicketIdIsDetected() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
 
         StatelessTicketCache cache = new MockStatelessTicketCache();
@@ -177,7 +174,7 @@ public class CasAuthenticationProviderTests {
     public void invalidKeyIsDetected() throws Exception {
         final Assertion assertion = new AssertionImpl("test");
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
 
         StatelessTicketCache cache = new MockStatelessTicketCache();
@@ -205,7 +202,7 @@ public class CasAuthenticationProviderTests {
     @Test(expected = IllegalArgumentException.class)
     public void detectsMissingKey() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setStatelessTicketCache(new MockStatelessTicketCache());
         cap.setTicketValidator(new MockTicketValidator(true));
         cap.setServiceProperties(makeServiceProperties());
@@ -217,7 +214,7 @@ public class CasAuthenticationProviderTests {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
         // set this explicitly to null to test failure
         cap.setStatelessTicketCache(null);
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
         cap.setTicketValidator(new MockTicketValidator(true));
         cap.setServiceProperties(makeServiceProperties());
@@ -227,7 +224,7 @@ public class CasAuthenticationProviderTests {
     @Test(expected = IllegalArgumentException.class)
     public void detectsMissingTicketValidator() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
         cap.setStatelessTicketCache(new MockStatelessTicketCache());
         cap.setServiceProperties(makeServiceProperties());
@@ -237,14 +234,15 @@ public class CasAuthenticationProviderTests {
     @Test
     public void gettersAndSettersMatch() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
         cap.setStatelessTicketCache(new MockStatelessTicketCache());
         cap.setTicketValidator(new MockTicketValidator(true));
         cap.setServiceProperties(makeServiceProperties());
         cap.afterPropertiesSet();
 
-        assertTrue(cap.getUserDetailsService() != null);
+// TODO disabled because why do we need to expose this?
+//        assertTrue(cap.getUserDetailsService() != null);
         assertEquals("qwerty", cap.getKey());
         assertTrue(cap.getStatelessTicketCache() != null);
         assertTrue(cap.getTicketValidator() != null);
@@ -253,7 +251,7 @@ public class CasAuthenticationProviderTests {
     @Test
     public void ignoresClassesItDoesNotSupport() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
         cap.setStatelessTicketCache(new MockStatelessTicketCache());
         cap.setTicketValidator(new MockTicketValidator(true));
@@ -271,7 +269,7 @@ public class CasAuthenticationProviderTests {
     @Test
     public void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal() throws Exception {
         CasAuthenticationProvider cap = new CasAuthenticationProvider();
-        cap.setUserDetailsService(new MockAuthoritiesPopulator());
+        cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
         cap.setKey("qwerty");
         cap.setStatelessTicketCache(new MockStatelessTicketCache());
         cap.setTicketValidator(new MockTicketValidator(true));
@@ -292,8 +290,9 @@ public class CasAuthenticationProviderTests {
 
     //~ Inner Classes ==================================================================================================
 
-    private class MockAuthoritiesPopulator implements UserDetailsService {
-        public UserDetails loadUserByUsername(String casUserId) throws AuthenticationException {
+    private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
+
+        public UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException {
             return makeUserDetailsFromAuthoritiesPopulator();
         }
     }