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 |
Simple cache for keeping track of OTP codes that has been used |
4856 |
18 Jun 18 |
nicklas |
to prevent the same code from being used more than once. This |
4856 |
18 Jun 18 |
nicklas |
should prevent an attacker from picking up a valid code and then |
4856 |
18 Jun 18 |
nicklas |
use it immediately to create a secondary login. This would be possible |
4856 |
18 Jun 18 |
nicklas |
since each code is valid for several minutes. |
4856 |
18 Jun 18 |
nicklas |
13 |
|
4856 |
18 Jun 18 |
nicklas |
The cache is automatically cleaned. |
4856 |
18 Jun 18 |
nicklas |
15 |
|
4856 |
18 Jun 18 |
nicklas |
@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 |
The default instance of this class uses a timeout of 120 seconds. |
4856 |
18 Jun 18 |
nicklas |
This should be enough to invalidate OTP codes using the default |
4856 |
18 Jun 18 |
nicklas |
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 |
Create a new cache with the given timeout. |
4856 |
18 Jun 18 |
nicklas |
@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 |
Try to use the given OTP code. If it has not been used |
4856 |
18 Jun 18 |
nicklas |
for the given user id, it is stored and TRUE is returned |
4856 |
18 Jun 18 |
nicklas |
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 |
// 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 |
// 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 |
// 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 |
} |