Coverage Report - org.trails.security.RollingCookieRememberMeServices
 
Classes in this File Line Coverage Branch Coverage Complexity
RollingCookieRememberMeServices
0% 
0% 
0
 
 1  
 package org.trails.security;
 2  
 
 3  
 import java.math.BigInteger;
 4  
 import java.util.Date;
 5  
 import java.util.List;
 6  
 import java.util.Random;
 7  
 
 8  
 import javax.servlet.http.Cookie;
 9  
 import javax.servlet.http.HttpServletRequest;
 10  
 import javax.servlet.http.HttpServletResponse;
 11  
 
 12  
 import org.acegisecurity.Authentication;
 13  
 import org.acegisecurity.ui.rememberme.RememberMeServices;
 14  
 import org.apache.commons.logging.Log;
 15  
 import org.apache.commons.logging.LogFactory;
 16  
 import org.hibernate.criterion.DetachedCriteria;
 17  
 import org.hibernate.criterion.Restrictions;
 18  
 import org.trails.persistence.HibernatePersistenceService;
 19  
 
 20  0
 public class RollingCookieRememberMeServices implements RememberMeServices {
 21  0
         private static final Log log = LogFactory.getLog(RollingCookieRememberMeServices.class );
 22  
         
 23  0
         private static Random random = new Random((new Date()).getTime() ); 
 24  0
         private enum Keys{j_rememberme, remembermetoken}
 25  
         HibernatePersistenceService persistenceService;
 26  
         
 27  0
         private char separatorChar = '-';
 28  
         // In seconds, default is a month
 29  0
         private int maxAge = 30 * 24 * 3600;
 30  
 
 31  
         public int getMaxAge() {
 32  0
                 return maxAge;
 33  
         }
 34  
 
 35  
         public void setMaxAge(int maxAge) {
 36  0
                 this.maxAge = maxAge;
 37  0
         }
 38  
 
 39  
         public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
 40  0
                 Cookie[] cookies = request.getCookies();
 41  0
                 if ((cookies == null) || (cookies.length == 0)) return null;
 42  0
                 for (Cookie cookie : cookies) if (Keys.remembermetoken.name().equals(cookie.getName()) ) {
 43  0
                         String cookieValue = cookie.getValue();
 44  0
                         int separatorPos = cookieValue.indexOf(separatorChar );
 45  0
                         if (separatorPos <= 0) return null;
 46  
                         //if (!cookie.getPath().equals(request.getContextPath())) return null;
 47  0
                         log.info("Trying to remember user from " + request.getRemoteAddr() + " with credentials " + cookieValue);
 48  0
                         return new UserKeyAuthenticationToken(cookieValue.substring(separatorPos+1), cookieValue.substring(0, separatorPos) );
 49  
                 }
 50  0
                 return null;
 51  
         }
 52  
 
 53  
         public void loginFail(HttpServletRequest request, HttpServletResponse response) {
 54  0
                 clearRememberMeCookie(request.getContextPath(), response);
 55  0
         }
 56  
         
 57  
         private String createExpiringKeyForUser(String username) {
 58  
                 // purge expired tokens here so we don't need to do it periodically - ok to do this everytime?
 59  
                 try {
 60  0
                         DetachedCriteria detachedCriteria = DetachedCriteria.forClass(ExpiringKey.class);
 61  0
                         detachedCriteria.add(Restrictions.eq("name", username) );
 62  0
                         detachedCriteria.add(Restrictions.lt("expiresAfter", new Date()) );
 63  0
                         List<ExpiringKey> credentials = persistenceService.getInstances(ExpiringKey.class, detachedCriteria );
 64  0
                         persistenceService.removeAll(credentials);
 65  
                 }
 66  0
                 catch (Exception e) {
 67  0
                         log.warn("Purging expired credentials failed because of: " + e.getMessage() );
 68  0
                 }
 69  
 
 70  0
                 ExpiringKey expiringKey = new ExpiringKey(username, (new BigInteger(128, random)).toString(), new Date((new Date()).getTime() + maxAge * 1000L) );
 71  0
                 persistenceService.save(expiringKey);
 72  0
                 return expiringKey.getValue() + separatorChar + expiringKey.getName();
 73  
         }
 74  
         
 75  
         public void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
 76  0
                 if (log.isTraceEnabled()) log.trace("j_rememberme is " + request.getParameter(Keys.j_rememberme.name()) );
 77  
                 
 78  0
                 if (request.getParameter(Keys.j_rememberme.name()) == null && !(authentication instanceof UserKeyAuthenticationToken) ) return; 
 79  
                 
 80  0
                 if (authentication instanceof UserKeyAuthenticationToken) try {
 81  
                         // Rolling tokens, remove the used one
 82  
                         // TODO This performs slowly, would be better to add a HQL pass-through method in persistence service
 83  0
                         DetachedCriteria detachedCriteria = DetachedCriteria.forClass(ExpiringKey.class);
 84  0
                         detachedCriteria.add(Restrictions.eq("name", authentication.getName() ) );
 85  0
                         detachedCriteria.add(Restrictions.eq("value", authentication.getCredentials() ) );
 86  0
                         List<ExpiringKey> credentials = persistenceService.getInstances(ExpiringKey.class, detachedCriteria );
 87  
                         
 88  
                         // persistenceService.removeAll(credentials) won't work. When remember me is used, there may be several incoming requests that are sending the same
 89  
                         // token as credentials. If we invalidate the token after the first request, the second request fails, the request
 90  
                         // is redirected to the the login page, but that request is authenticated with the new cookie and so we go to
 91  
                         // an infinite loop. Instead, make the credentials expire soon (for example less than session timeout)
 92  
                         // Assume there's only one credential in the list. Expired credentials will be cleaned up later in any case
 93  0
                         if (credentials.size() > 0) {
 94  0
                                 ExpiringKey credential = credentials.get(0); 
 95  
                                 // Expire in one min
 96  0
                                 credential.setExpiresAfter(new Date((new Date()).getTime() + 60000L));
 97  0
                                 persistenceService.save(credential);
 98  
                         }
 99  
                 }
 100  0
                 catch (Exception e) {
 101  0
                         log.warn("Couldn't expire used credentials because of " + e.getMessage() );
 102  0
                 }
 103  
                 
 104  0
                 String username = authentication.getName();
 105  0
                 Cookie cookie = new Cookie(Keys.remembermetoken.name(), createExpiringKeyForUser(username).toString() );
 106  0
                 cookie.setPath(request.getContextPath());
 107  0
                 cookie.setMaxAge(maxAge);
 108  0
                 response.addCookie(cookie);
 109  0
         }
 110  
 
 111  
         public void setPersistenceService(HibernatePersistenceService persistenceService)
 112  
         {
 113  0
                 this.persistenceService = persistenceService;
 114  0
         }
 115  
         
 116  
         public char getSeparatorChar() {
 117  0
                 return separatorChar;
 118  
         }
 119  
 
 120  
         public void setSeparatorChar(char separatorChar) {
 121  0
                 this.separatorChar = separatorChar;
 122  0
         }
 123  
         
 124  
         public static void clearRememberMeCookie(String contextPath, HttpServletResponse response) {
 125  0
                 Cookie cookie = new Cookie(Keys.remembermetoken.name(), "");
 126  0
                 cookie.setPath(contextPath == null ? "/" : contextPath);
 127  0
                 cookie.setMaxAge(0);
 128  0
                 response.addCookie(cookie);
 129  0
         }
 130  
 }