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 }