extensions/net.sf.basedb.otp/trunk/src/net/sf/basedb/otp/UsedOtpCodes.java

Code
Comments
Other
Rev Date Author Line
4856 18 Jun 18 nicklas 1 package net.sf.basedb.otp;
4856 18 Jun 18 nicklas 2
4856 18 Jun 18 nicklas 3 import java.util.HashMap;
4856 18 Jun 18 nicklas 4 import java.util.Iterator;
4856 18 Jun 18 nicklas 5 import java.util.Map;
4856 18 Jun 18 nicklas 6
4856 18 Jun 18 nicklas 7 /**
4856 18 Jun 18 nicklas 8   Simple cache for keeping track of OTP codes that has been used
4856 18 Jun 18 nicklas 9   to prevent the same code from being used more than once. This 
4856 18 Jun 18 nicklas 10   should prevent an attacker from picking up a valid code and then
4856 18 Jun 18 nicklas 11   use it immediately to create a secondary login. This would be possible
4856 18 Jun 18 nicklas 12   since each code is valid for several minutes.
4856 18 Jun 18 nicklas 13   
4856 18 Jun 18 nicklas 14   The cache is automatically cleaned.
4856 18 Jun 18 nicklas 15   
4856 18 Jun 18 nicklas 16   @since 1.0
4856 18 Jun 18 nicklas 17 */
4856 18 Jun 18 nicklas 18 public class UsedOtpCodes
4856 18 Jun 18 nicklas 19 {
4856 18 Jun 18 nicklas 20
4856 18 Jun 18 nicklas 21   /**
4856 18 Jun 18 nicklas 22     The default instance of this class uses a timeout of 120 seconds.
4856 18 Jun 18 nicklas 23     This should be enough to invalidate OTP codes using the default
4856 18 Jun 18 nicklas 24     settings of 30 seconds with a window size of 3 (+-30 seconds).
4856 18 Jun 18 nicklas 25   */
4856 18 Jun 18 nicklas 26   public static final UsedOtpCodes INSTANCE = new UsedOtpCodes(120);
4856 18 Jun 18 nicklas 27   
4856 18 Jun 18 nicklas 28   private final Map<String, Long> usedCodes;
4856 18 Jun 18 nicklas 29   private final long timeout;
4856 18 Jun 18 nicklas 30   private long nextCleanup;
4856 18 Jun 18 nicklas 31   
4856 18 Jun 18 nicklas 32   /**
4856 18 Jun 18 nicklas 33     Create a new cache with the given timeout.
4856 18 Jun 18 nicklas 34     @param timeout Timeout in seconds
4856 18 Jun 18 nicklas 35   */
4856 18 Jun 18 nicklas 36   public UsedOtpCodes(int timeout)
4856 18 Jun 18 nicklas 37   {
4856 18 Jun 18 nicklas 38     this.timeout = timeout * 1000;
4856 18 Jun 18 nicklas 39     this.nextCleanup = System.currentTimeMillis() + 10 * this.timeout;
4856 18 Jun 18 nicklas 40     this.usedCodes = new HashMap<>();
4856 18 Jun 18 nicklas 41   }
4856 18 Jun 18 nicklas 42
4856 18 Jun 18 nicklas 43   /**
4856 18 Jun 18 nicklas 44     Try to use the given OTP code. If it has not been used
4856 18 Jun 18 nicklas 45     for the given user id, it is stored and TRUE is returned
4856 18 Jun 18 nicklas 46     If the code has already been used, FALSE is returned.
4856 18 Jun 18 nicklas 47     
4856 18 Jun 18 nicklas 48   */
4856 18 Jun 18 nicklas 49   public synchronized boolean useCode(int userId, String otp)
4856 18 Jun 18 nicklas 50   {
4856 18 Jun 18 nicklas 51     long now = System.currentTimeMillis();
4856 18 Jun 18 nicklas 52     
4856 18 Jun 18 nicklas 53     String key = userId + ":" + otp;
4856 18 Jun 18 nicklas 54     Long itemTimeout = usedCodes.get(key);
4856 18 Jun 18 nicklas 55     if (itemTimeout != null && itemTimeout > now) 
4856 18 Jun 18 nicklas 56     {
4856 18 Jun 18 nicklas 57       // The code has been used and it has not timed out
4856 18 Jun 18 nicklas 58       return false;
4856 18 Jun 18 nicklas 59     }
4856 18 Jun 18 nicklas 60     
4856 18 Jun 18 nicklas 61     // Save the given code
4856 18 Jun 18 nicklas 62     usedCodes.put(key, now + timeout);
4856 18 Jun 18 nicklas 63     
4856 18 Jun 18 nicklas 64     // Do cleanup if it was a long time ago
4856 18 Jun 18 nicklas 65     if (now > nextCleanup) cleanUp();
4856 18 Jun 18 nicklas 66     
4856 18 Jun 18 nicklas 67     return true;
4856 18 Jun 18 nicklas 68   }
4856 18 Jun 18 nicklas 69   
4856 18 Jun 18 nicklas 70   private void cleanUp()
4856 18 Jun 18 nicklas 71   {
4856 18 Jun 18 nicklas 72     long now = System.currentTimeMillis();
4856 18 Jun 18 nicklas 73     Iterator<Long> it = usedCodes.values().iterator();
4856 18 Jun 18 nicklas 74     while (it.hasNext())
4856 18 Jun 18 nicklas 75     {
4856 18 Jun 18 nicklas 76       if (it.next() < now) it.remove();
4856 18 Jun 18 nicklas 77     }
4856 18 Jun 18 nicklas 78     nextCleanup = now + 10 * timeout;
4856 18 Jun 18 nicklas 79   }
4856 18 Jun 18 nicklas 80   
4856 18 Jun 18 nicklas 81 }