001    package org.trails.security;
002    
003    import java.util.Date;
004    import java.util.List;
005    
006    import org.acegisecurity.Authentication;
007    import org.acegisecurity.AuthenticationException;
008    import org.acegisecurity.BadCredentialsException;
009    import org.acegisecurity.providers.AuthenticationProvider;
010    import org.acegisecurity.userdetails.UserDetails;
011    import org.acegisecurity.userdetails.UserDetailsService;
012    import org.apache.log4j.Logger;
013    import org.hibernate.criterion.DetachedCriteria;
014    import org.hibernate.criterion.Restrictions;
015    import org.trails.persistence.HibernatePersistenceService;
016    
017    public class ExpiringKeyAuthenticationProvider implements AuthenticationProvider {
018            private static final Logger log = Logger.getLogger(ExpiringKeyAuthenticationProvider.class);
019            
020            private HibernatePersistenceService persistenceService;
021            private UserDetailsService userDetailsService;
022    
023            public void setPersistenceService(HibernatePersistenceService persistenceService) {
024                    this.persistenceService = persistenceService;
025            }
026    
027            public void setUserDetailsService(UserDetailsService userDetailsService) {
028                    this.userDetailsService = userDetailsService;
029            }
030    
031            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
032                    // This is called repetitively on the first request when the authentication is not yet established,
033                    // but a cookie is available. Only authenticate if not authenticated (no authorities found)
034                    if (authentication.getAuthorities() != null) return authentication;
035                    // Only process if principal is available
036                    if (authentication.getPrincipal() == null) return authentication;
037                    DetachedCriteria detachedCriteria = DetachedCriteria.forClass(ExpiringKey.class);
038                    detachedCriteria.add(Restrictions.eq("name", authentication.getName()) );
039                    detachedCriteria.add(Restrictions.gt("expiresAfter", new Date() ) );
040                    
041                    List<ExpiringKey> expiringKeys = persistenceService.getInstances(ExpiringKey.class, detachedCriteria );
042                    if (expiringKeys.size() <= 0) throw new BadCredentialsException("No persistent credentials found");
043                    
044                    
045                    String providedToken = authentication.getCredentials().toString();
046                    if (providedToken == null) throw new BadCredentialsException("No remember me token provided");
047                    
048                    for (ExpiringKey key : expiringKeys) if (providedToken.equals(key.getValue()) ) {
049                            UserDetails userDetails = userDetailsService.loadUserByUsername(key.getName());
050                            // TODO we should handle these specific exceptions here
051                            // A DisabledException must be thrown if an account is disabled and the AuthenticationManager can test for this state.
052                            // A LockedException must be thrown if an account is locked and the AuthenticationManager can test for account locking.
053                            if (userDetails == null) throw new BadCredentialsException("Token found, but user doesn't exist");
054                log.info("Successfully authenticated user " + authentication.getName() + " using expiring key");
055                            return new UserKeyAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), userDetails.getAuthorities() );
056                    }
057                    throw new BadCredentialsException("No matching token available");
058            }
059    
060            public boolean supports(Class authenticationClass) {
061                    return (UserKeyAuthenticationToken.class.isAssignableFrom(authenticationClass));
062            }
063    }