/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.cas; import org.acegisecurity.AcegiMessageSource; import org.acegisecurity.Authentication; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.providers.AuthenticationProvider; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.acegisecurity.ui.cas.CasProcessingFilter; import org.acegisecurity.userdetails.UserDetails; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.util.Assert; import org.acegisecurity.providers.cas.TicketResponse; import org.acegisecurity.providers.cas.StatelessTicketCache; import org.acegisecurity.providers.cas.CasProxyDecider; import org.acegisecurity.providers.cas.CasAuthenticationToken; import org.acegisecurity.providers.cas.CasAuthoritiesPopulator; public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware { private static Log log = LogFactory.getLog(CasAuthenticationProvider.class); private CasAuthoritiesPopulator casAuthoritiesPopulator; private CasProxyDecider casProxyDecider; private MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); private StatelessTicketCache statelessTicketCache; private String key; private CasProxyTicketValidator ticketValidator; /** {@inheritDoc} */ public void afterPropertiesSet() throws Exception { Assert.notNull(casAuthoritiesPopulator, "A casAuthoritiesPopulator must be set"); Assert.notNull(ticketValidator, "A ticketValidator must be set"); Assert.notNull(casProxyDecider, "A casProxyDecider must be set"); Assert.notNull(statelessTicketCache, "A statelessTicketCache must be set"); Assert.notNull(key, "A Key is required so CasAuthenticationProvider can" + " identify tokens it previously authenticated"); Assert.notNull(messages, "A message source must be set"); } /** {@inheritDoc} */ public Authentication authenticate(final Authentication authentication) { log.trace("Entering authenticate"); if (!supports(authentication.getClass())) { return null; } // Check if it is a user/pass authentication token. If this is the case, // just skip it. if (authentication instanceof UsernamePasswordAuthenticationToken && (!CasProcessingFilter.CAS_STATEFUL_IDENTIFIER.equals( authentication.getPrincipal().toString()) && !CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals( authentication.getPrincipal().toString()))) { // UsernamePasswordAuthenticationToken not CAS related log.trace("Leaving authenticate"); return null; } // If an existing CasAuthenticationToken, just check we created it if (authentication instanceof CasAuthenticationToken) { if (key.hashCode() == ((CasAuthenticationToken) authentication).getKeyHash()) { log.trace("Leaving authenticate"); return authentication; } else { throw new BadCredentialsException(messages.getMessage( "CasAuthenticationProvider.incorrectKey", "The presented CasAuthenticationToken does not contain the" + " expected key")); } } // Ensure credentials are presented if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) { throw new BadCredentialsException(messages.getMessage( "CasAuthenticationProvider.noServiceTicket", "Failed to provide a CAS service ticket to validate")); } boolean stateless = false; if (authentication instanceof UsernamePasswordAuthenticationToken && CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals( authentication.getPrincipal())) { stateless = true; } CasAuthenticationToken result = null; if (stateless) { // Try to obtain from cache result = statelessTicketCache.getByTicketId( authentication.getCredentials().toString()); } if (result == null) { result = authenticateNow(authentication); } if (stateless) { // Add to cache statelessTicketCache.putTicketInCache(result); } log.trace("Leaving authenticate"); return result; } private CasAuthenticationToken authenticateNow(final Authentication authentication) { // Validate log.trace("Entering authenticate"); TicketResponse response = ticketValidator.confirmTicketValid( authentication); // Check proxy list is trusted casProxyDecider.confirmProxyListTrusted(response.getProxyList()); // Lookup user details UserDetails userDetails = casAuthoritiesPopulator.getUserDetails( response.getUser()); // Construct CasAuthenticationToken CasAuthenticationToken result = new CasAuthenticationToken(key, userDetails, authentication.getCredentials(), userDetails.getAuthorities(), userDetails, response.getProxyList(), response.getProxyGrantingTicketIou()); log.trace("Leaving authenticate"); return result; } public CasAuthoritiesPopulator getCasAuthoritiesPopulator() { return casAuthoritiesPopulator; } public CasProxyDecider getCasProxyDecider() { return casProxyDecider; } public String getKey() { return key; } public StatelessTicketCache getStatelessTicketCache() { return statelessTicketCache; } public CasProxyTicketValidator getTicketValidator() { return ticketValidator; } public void setCasAuthoritiesPopulator(final CasAuthoritiesPopulator theCasAuthoritiesPopulator) { casAuthoritiesPopulator = theCasAuthoritiesPopulator; } public void setCasProxyDecider(final CasProxyDecider theCasProxyDecider) { casProxyDecider = theCasProxyDecider; } public void setKey(final String theKey) { key = theKey; } public void setMessageSource(final MessageSource messageSource) { messages = new MessageSourceAccessor(messageSource); } public void setStatelessTicketCache(final StatelessTicketCache theStatelessTicketCache) { statelessTicketCache = theStatelessTicketCache; } public void setTicketValidator(final CasProxyTicketValidator theTicketValidator) { ticketValidator = theTicketValidator; } @SuppressWarnings("unchecked") public boolean supports(final Class authentication) { if (UsernamePasswordAuthenticationToken.class.isAssignableFrom( authentication)) { return true; } return CasAuthenticationToken.class.isAssignableFrom(authentication); } }