|
1
|
|
package com.rbctcsworld.ecommerce.auth; |
|
2
|
|
|
|
3
|
|
import java.time.Clock; |
|
4
|
|
import java.time.Duration; |
|
5
|
|
import java.time.Instant; |
|
6
|
|
import java.util.Map; |
|
7
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
8
|
|
|
|
9
|
|
/** |
|
10
|
|
* Failed logins counted in this JVM. Default for development and tests, and the fallback while Redis is |
|
11
|
|
* unreachable. Limits: every backend instance counts on its own, and a restart forgets all locks. |
|
12
|
|
*/ |
|
13
|
|
public class InMemoryLoginAttemptStore implements LoginAttemptStore { |
|
14
|
|
|
|
15
|
|
private static final int MAX_TRACKED = 10_000; |
|
16
|
|
|
|
17
|
|
private final Clock clock; |
|
18
|
|
private final int maxFailures; |
|
19
|
|
private final Duration window; |
|
20
|
|
private final Duration lock; |
|
21
|
|
private final Map<String, Attempts> attempts = new ConcurrentHashMap<>(); |
|
22
|
|
|
|
23
|
|
public InMemoryLoginAttemptStore(Clock clock, int maxFailures, Duration window, Duration lock) { |
|
24
|
|
this.clock = clock; |
|
25
|
|
this.maxFailures = maxFailures; |
|
26
|
|
this.window = window; |
|
27
|
|
this.lock = lock; |
|
28
|
|
} |
|
29
|
|
|
|
30
|
|
private record Attempts(int failures, Instant firstFailure, Instant lockedUntil) { |
|
31
|
|
} |
|
32
|
|
|
|
33
|
|
@Override |
|
34
|
|
public long lockRemainingMillis(String email) { |
|
35
|
|
Attempts a = attempts.get(email); |
|
36
|
|
Instant now = clock.instant(); |
|
37
|
3
1. lockRemainingMillis : removed conditional - replaced equality check with false → KILLED
2. lockRemainingMillis : removed conditional - replaced equality check with false → KILLED
3. lockRemainingMillis : removed conditional - replaced equality check with false → KILLED
|
if (a == null || a.lockedUntil() == null || !now.isBefore(a.lockedUntil())) { |
|
38
|
|
return 0; |
|
39
|
|
} |
|
40
|
1
1. lockRemainingMillis : replaced long return with 0 for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::lockRemainingMillis → KILLED
|
return Duration.between(now, a.lockedUntil()).toMillis(); |
|
41
|
|
} |
|
42
|
|
|
|
43
|
|
@Override |
|
44
|
|
public void recordFailure(String email) { |
|
45
|
|
Instant now = clock.instant(); |
|
46
|
3
1. recordFailure : changed conditional boundary → KILLED
2. recordFailure : removed call to com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::evictStale → KILLED
3. recordFailure : removed conditional - replaced comparison check with false → KILLED
|
if (attempts.size() > MAX_TRACKED) evictStale(now); |
|
47
|
|
attempts.compute(email, (k, a) -> { |
|
48
|
2
1. lambda$recordFailure$0 : removed conditional - replaced equality check with false → KILLED
2. lambda$recordFailure$0 : removed conditional - replaced equality check with false → KILLED
|
if (a == null || now.isAfter(a.firstFailure().plus(window)) |
|
49
|
2
1. lambda$recordFailure$0 : removed conditional - replaced equality check with false → KILLED
2. lambda$recordFailure$0 : removed conditional - replaced equality check with false → KILLED
|
|| (a.lockedUntil() != null && !now.isBefore(a.lockedUntil()))) { |
|
50
|
|
a = new Attempts(0, now, null); // new window (or lock has expired) |
|
51
|
|
} |
|
52
|
1
1. lambda$recordFailure$0 : Replaced integer addition with subtraction → KILLED
|
int failures = a.failures() + 1; |
|
53
|
2
1. lambda$recordFailure$0 : changed conditional boundary → KILLED
2. lambda$recordFailure$0 : removed conditional - replaced comparison check with false → KILLED
|
Instant lockedUntil = failures >= maxFailures ? now.plus(lock) : null; |
|
54
|
1
1. lambda$recordFailure$0 : replaced return value with null for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::lambda$recordFailure$0 → KILLED
|
return new Attempts(failures, a.firstFailure(), lockedUntil); |
|
55
|
|
}); |
|
56
|
|
} |
|
57
|
|
|
|
58
|
|
@Override |
|
59
|
|
public void reset(String email) { |
|
60
|
|
attempts.remove(email); |
|
61
|
|
} |
|
62
|
|
|
|
63
|
|
@Override |
|
64
|
|
public int failures(String email) { |
|
65
|
|
Attempts a = attempts.get(email); |
|
66
|
2
1. failures : replaced int return with 0 for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::failures → KILLED
2. failures : removed conditional - replaced equality check with false → KILLED
|
return a == null ? 0 : a.failures(); |
|
67
|
|
} |
|
68
|
|
|
|
69
|
|
int tracked() { |
|
70
|
1
1. tracked : replaced int return with 0 for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::tracked → KILLED
|
return attempts.size(); |
|
71
|
|
} |
|
72
|
|
|
|
73
|
|
private void evictStale(Instant now) { |
|
74
|
|
attempts.entrySet().removeIf(e -> { |
|
75
|
|
Attempts a = e.getValue(); |
|
76
|
|
boolean windowOver = now.isAfter(a.firstFailure().plus(window)); |
|
77
|
2
1. lambda$evictStale$1 : removed conditional - replaced equality check with false → KILLED
2. lambda$evictStale$1 : removed conditional - replaced equality check with false → KILLED
|
boolean notLocked = a.lockedUntil() == null || !now.isBefore(a.lockedUntil()); |
|
78
|
3
1. lambda$evictStale$1 : removed conditional - replaced equality check with false → KILLED
2. lambda$evictStale$1 : removed conditional - replaced equality check with false → KILLED
3. lambda$evictStale$1 : replaced boolean return with true for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::lambda$evictStale$1 → KILLED
|
return windowOver && notLocked; |
|
79
|
|
}); |
|
80
|
|
} |
|
81
|
|
} |
| | Mutations |
| 37 |
|
1.1 Location : lockRemainingMillis Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:longAfterTheLockNothingNegativeIsReported()] removed conditional - replaced equality check with false → KILLED
2.2 Location : lockRemainingMillis Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] removed conditional - replaced equality check with false → KILLED
3.3 Location : lockRemainingMillis Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] removed conditional - replaced equality check with false → KILLED
|
| 40 |
|
1.1 Location : lockRemainingMillis Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] replaced long return with 0 for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::lockRemainingMillis → KILLED
|
| 46 |
|
1.1 Location : recordFailure Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:exactlyTenThousandTrackedIsStillBelowTheCleanupLimit()] changed conditional boundary → KILLED
2.2 Location : recordFailure Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:activeLocksSurviveTheEviction()] removed call to com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::evictStale → KILLED
3.3 Location : recordFailure Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:activeLocksSurviveTheEviction()] removed conditional - replaced comparison check with false → KILLED
|
| 48 |
|
1.1 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] removed conditional - replaced equality check with false → KILLED
2.2 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] removed conditional - replaced equality check with false → KILLED
|
| 49 |
|
1.1 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.LoginAttemptServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.LoginAttemptServiceTest]/[method:afterTheLockExpiresCountingStartsAgain()] removed conditional - replaced equality check with false → KILLED
2.2 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.LoginAttemptServiceTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.LoginAttemptServiceTest]/[method:afterTheLockExpiresCountingStartsAgain()] removed conditional - replaced equality check with false → KILLED
|
| 52 |
|
1.1 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] Replaced integer addition with subtraction → KILLED
|
| 53 |
|
1.1 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] changed conditional boundary → KILLED
2.2 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] removed conditional - replaced comparison check with false → KILLED
|
| 54 |
|
1.1 Location : lambda$recordFailure$0 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:lockRemainingCountsDownToZero()] replaced return value with null for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::lambda$recordFailure$0 → KILLED
|
| 66 |
|
1.1 Location : failures Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:fourFailuresDoNotLock()] replaced int return with 0 for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::failures → KILLED
2.2 Location : failures Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:unknownEmailIsNeitherLockedNorCounted()] removed conditional - replaced equality check with false → KILLED
|
| 70 |
|
1.1 Location : tracked Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:exactlyTenThousandTrackedIsStillBelowTheCleanupLimit()] replaced int return with 0 for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::tracked → KILLED
|
| 77 |
|
1.1 Location : lambda$evictStale$1 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:memoryStaysBoundedOldEntriesAreEvicted()] removed conditional - replaced equality check with false → KILLED
2.2 Location : lambda$evictStale$1 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:activeLocksSurviveTheEviction()] removed conditional - replaced equality check with false → KILLED
|
| 78 |
|
1.1 Location : lambda$evictStale$1 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:activeLocksSurviveTheEviction()] removed conditional - replaced equality check with false → KILLED
2.2 Location : lambda$evictStale$1 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:activeLocksSurviveTheEviction()] removed conditional - replaced equality check with false → KILLED
3.3 Location : lambda$evictStale$1 Killed by : com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest.[engine:junit-jupiter]/[class:com.rbctcsworld.ecommerce.auth.InMemoryLoginAttemptStoreTest]/[method:memoryStaysBoundedOldEntriesAreEvicted()] replaced boolean return with true for com/rbctcsworld/ecommerce/auth/InMemoryLoginAttemptStore::lambda$evictStale$1 → KILLED
|