add linear strategy to brute force
closes #25917 Signed-off-by: Gilvan Filho <gilvan.sfilho@gmail.com>
This commit is contained in:
parent
6d52520730
commit
c4005d29f0
16 changed files with 182 additions and 8 deletions
|
@ -92,6 +92,7 @@ public class RealmRepresentation {
|
|||
protected Boolean bruteForceProtected;
|
||||
protected Boolean permanentLockout;
|
||||
protected Integer maxTemporaryLockouts;
|
||||
protected BruteForceStrategy bruteForceStrategy;
|
||||
protected Integer maxFailureWaitSeconds;
|
||||
protected Integer minimumQuickLoginWaitSeconds;
|
||||
protected Integer waitIncrementSeconds;
|
||||
|
@ -777,6 +778,14 @@ public class RealmRepresentation {
|
|||
this.maxTemporaryLockouts = maxTemporaryLockouts;
|
||||
}
|
||||
|
||||
public BruteForceStrategy getBruteForceStrategy() {
|
||||
return this.bruteForceStrategy;
|
||||
}
|
||||
|
||||
public void setBruteForceStrategy(BruteForceStrategy bruteForceStrategy) {
|
||||
this.bruteForceStrategy = bruteForceStrategy;
|
||||
}
|
||||
|
||||
public Integer getMaxFailureWaitSeconds() {
|
||||
return maxFailureWaitSeconds;
|
||||
}
|
||||
|
@ -1450,4 +1459,8 @@ public class RealmRepresentation {
|
|||
}
|
||||
organizations.add(org);
|
||||
}
|
||||
|
||||
public enum BruteForceStrategy {
|
||||
LINEAR, MULTIPLE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,15 +75,19 @@ wait time will never reach the value you have set to `Max wait`.
|
|||
.. If the time between this failure and the last failure is greater than _Failure Reset Time_
|
||||
... Reset `count`
|
||||
.. Increment `count`
|
||||
.. Calculate `wait` using _Wait Increment_ * (`count` / _Max Login Failures_). The division is an integer division rounded down to a whole number
|
||||
.. If `wait` equals 0 and the time between this failure and the last failure is less than _Quick Login Check Milliseconds_, set `wait` to _Minimum Quick Login Wait_.
|
||||
.. Calculate `wait` according brute force strategy defined (see below Strategies to set Wait Time).
|
||||
.. If `wait` equals to or less than 0 and the time between this failure and the last failure is less than _Quick Login Check Milliseconds_, set `wait` to _Minimum Quick Login Wait_.
|
||||
... Temporarily disable the user for the smallest of `wait` and _Max Wait_ seconds
|
||||
... Increment the temporary lockout counter
|
||||
|
||||
`count` does not increment when a temporarily disabled account commits a login failure.
|
||||
====
|
||||
|
||||
For instance, if you have set `Max Login Failures` to `5` and a `Wait Increment` of `30` seconds, the effective time an account will be disabled after several failed authentication attempts will be:
|
||||
*Strategies to set Wait Time*
|
||||
|
||||
Keycloak provides two strategies to calculate wait time: By multiples or Linear. By multiples is the first strategy introduced by keycloak, so that is the default one.
|
||||
|
||||
With by multiples strategy wait time will be incremented when number (or count) of failures are multiple of `Max Login Failure`. For instance, if you have set `Max Login Failures` to `5` and a `Wait Increment` of `30` seconds, the effective time an account will be disabled after several failed authentication attempts will be:
|
||||
|
||||
[cols="1,1,1,1"]
|
||||
|===
|
||||
|
@ -100,9 +104,30 @@ For instance, if you have set `Max Login Failures` to `5` and a `Wait Increment`
|
|||
|**10** |**30** | 5 | **60**
|
||||
|===
|
||||
|
||||
Note that the `Effective Wait Time` at the 5th failed attempt will disable the account for `30` seconds. Only after reaching
|
||||
the next multiple of `Max Login Failures`, in this case `10`, will the time increase from `30` to `60`. The time the account will be disabled
|
||||
is only increased when reaching multiples of `Max Login Failures`.
|
||||
Note that the `Effective Wait Time` at the 5th failed attempt will disable the account for `30` seconds. Only after reaching the next multiple of `Max Login Failures`, in this case `10`, will the time increase from `30` to `60`. The time the account will be disabled is only increased when reaching multiples of `Max Login Failures`.
|
||||
|
||||
The by multiple strategy uses the following formula to calculate wait time: _Wait Increment_ * (`count` / _Max Login Failures_). The division is an integer division rounded down to a whole number.
|
||||
|
||||
With linear strategy wait time will be incremented when number (or count) of failures are equal to or greater than `Max Login Failure`. For instance, if you have set `Max Login Failures` to `5` and a `Wait Increment` of `30` seconds, the effective time an account will be disabled after several failed authentication attempts will be:
|
||||
|
||||
[cols="1,1,1,1"]
|
||||
|===
|
||||
|`Number of Failures` | `Wait Increment` | `Max Login Failures` | `Effective Wait Time`
|
||||
|1 |30 | 5 | 0
|
||||
|2 |30 | 5 | 0
|
||||
|3 |30 | 5 | 0
|
||||
|4 |30 | 5 | 0
|
||||
|**5** |**30** | 5 | **30**
|
||||
|**6** |**30** | 5 | **60**
|
||||
|**7** |**30** | 5 | **90**
|
||||
|**8** |**30** | 5 | **120**
|
||||
|**9** |**30** | 5 | **150**
|
||||
|**10** |**30** | 5 | **180**
|
||||
|===
|
||||
|
||||
Note that the `Effective Wait Time` at the 5th failed attempt will disable the account for `30` seconds. Each new failed attempt will increase wait time.
|
||||
|
||||
The linear strategy uses the following formula to calculate wait time: _Wait Increment_ * (1 + `count` - _Max Login Failures_).
|
||||
|
||||
*Permanent Lockout Parameters*
|
||||
|
||||
|
|
|
@ -729,6 +729,10 @@ rowSaveBtnAriaLabel=Save edits for {{messageBundle}}
|
|||
permanentLockout=Permanent lockout
|
||||
maxTemporaryLockouts=Maximum temporary lockouts
|
||||
maxTemporaryLockoutsHelp=The number of temporary lockouts permitted before the user is permanently locked out.
|
||||
bruteForceStrategy=Strategy to increase wait time
|
||||
bruteForceStrategyHelp=Multiple means wait time will be increased only when number of failures are multiples of '{{failureFactor}}'. Linear means each new failure starting at '{{failureFactor}}' will increase wait time.
|
||||
bruteForceStrategy.LINEAR=Linear
|
||||
bruteForceStrategy.MULTIPLE=Multiple
|
||||
debug=Debug
|
||||
webAuthnPolicyRequireResidentKey=Require discoverable credential
|
||||
unlockUsersConfirm=All the users that are temporarily locked will be unlocked.
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
KeycloakSelect,
|
||||
NumberControl,
|
||||
SelectVariant,
|
||||
SelectControl,
|
||||
} from "@keycloak/keycloak-ui-shared";
|
||||
import {
|
||||
ActionGroup,
|
||||
|
@ -52,6 +53,8 @@ export const BruteForceDetection = ({
|
|||
BruteForceMode.PermanentAfterTemporaryLockout,
|
||||
];
|
||||
|
||||
const bruteForceStrategyTypes = ["MULTIPLE", "LINEAR"];
|
||||
|
||||
const setupForm = () => {
|
||||
convertToFormValues(realm, setValue);
|
||||
setIsBruteForceModeUpdated(false);
|
||||
|
@ -155,6 +158,16 @@ export const BruteForceDetection = ({
|
|||
bruteForceMode ===
|
||||
BruteForceMode.PermanentAfterTemporaryLockout) && (
|
||||
<>
|
||||
<SelectControl
|
||||
name="bruteForceStrategy"
|
||||
label={t("bruteForceStrategy")}
|
||||
labelIcon={t("bruteForceStrategyHelp")}
|
||||
controller={{ defaultValue: "" }}
|
||||
options={bruteForceStrategyTypes.map((key) => ({
|
||||
key,
|
||||
value: t(`bruteForceStrategy.${key}`),
|
||||
}))}
|
||||
/>
|
||||
<Time name="waitIncrementSeconds" />
|
||||
<Time name="maxFailureWaitSeconds" />
|
||||
<Time name="maxDeltaTimeSeconds" />
|
||||
|
|
|
@ -74,6 +74,7 @@ export default interface RealmRepresentation {
|
|||
maxDeltaTimeSeconds?: number;
|
||||
maxFailureWaitSeconds?: number;
|
||||
maxTemporaryLockouts?: number;
|
||||
bruteForceStrategy?: "MULTIPLE" | "LINEAR";
|
||||
minimumQuickLoginWaitSeconds?: number;
|
||||
notBefore?: number;
|
||||
oauth2DeviceCodeLifespan?: number;
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.keycloak.models.WebAuthnPolicy;
|
|||
import org.keycloak.models.cache.CachedRealmModel;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageUtil;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
|
@ -283,6 +284,18 @@ public class RealmAdapter implements CachedRealmModel {
|
|||
updated.setMaxTemporaryLockouts(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmRepresentation.BruteForceStrategy getBruteForceStrategy() {
|
||||
if(isUpdated()) return updated.getBruteForceStrategy();
|
||||
return cached.getBruteForceStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBruteForceStrategy(final RealmRepresentation.BruteForceStrategy val) {
|
||||
getDelegateForUpdate();
|
||||
updated.setBruteForceStrategy(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxFailureWaitSeconds() {
|
||||
if (isUpdated()) return updated.getMaxFailureWaitSeconds();
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.keycloak.models.RequiredCredentialModel;
|
|||
import org.keycloak.models.WebAuthnPolicy;
|
||||
import org.keycloak.models.cache.infinispan.DefaultLazyLoader;
|
||||
import org.keycloak.models.cache.infinispan.LazyLoader;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -78,6 +79,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
protected boolean bruteForceProtected;
|
||||
protected boolean permanentLockout;
|
||||
protected int maxTemporaryLockouts;
|
||||
protected RealmRepresentation.BruteForceStrategy bruteForceStrategy;
|
||||
protected int maxFailureWaitSeconds;
|
||||
protected int minimumQuickLoginWaitSeconds;
|
||||
protected int waitIncrementSeconds;
|
||||
|
@ -193,6 +195,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
bruteForceProtected = model.isBruteForceProtected();
|
||||
permanentLockout = model.isPermanentLockout();
|
||||
maxTemporaryLockouts = model.getMaxTemporaryLockouts();
|
||||
bruteForceStrategy = model.getBruteForceStrategy();
|
||||
maxFailureWaitSeconds = model.getMaxFailureWaitSeconds();
|
||||
minimumQuickLoginWaitSeconds = model.getMinimumQuickLoginWaitSeconds();
|
||||
waitIncrementSeconds = model.getWaitIncrementSeconds();
|
||||
|
@ -376,6 +379,10 @@ public class CachedRealm extends AbstractExtendableRevisioned {
|
|||
return maxTemporaryLockouts;
|
||||
}
|
||||
|
||||
public RealmRepresentation.BruteForceStrategy getBruteForceStrategy() {
|
||||
return bruteForceStrategy;
|
||||
}
|
||||
|
||||
public int getMaxFailureWaitSeconds() {
|
||||
return this.maxFailureWaitSeconds;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import org.keycloak.provider.ProviderConfigProperty;
|
|||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.LockModeType;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
@ -268,6 +270,20 @@ public class RealmAdapter implements StorageProviderRealmModel, JpaModel<RealmEn
|
|||
return getAttribute("maxTemporaryLockouts", 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmRepresentation.BruteForceStrategy getBruteForceStrategy() {
|
||||
String name = getAttribute("bruteForceStrategy");
|
||||
if(name == null)
|
||||
return RealmRepresentation.BruteForceStrategy.MULTIPLE;
|
||||
|
||||
return RealmRepresentation.BruteForceStrategy.valueOf(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBruteForceStrategy(final RealmRepresentation.BruteForceStrategy val) {
|
||||
setAttribute("bruteForceStrategy", val.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxTemporaryLockouts(final int val) {
|
||||
setAttribute("maxTemporaryLockouts", val);
|
||||
|
|
|
@ -186,6 +186,7 @@ public class DefaultExportImportManager implements ExportImportManager {
|
|||
if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected());
|
||||
if (rep.isPermanentLockout() != null) newRealm.setPermanentLockout(rep.isPermanentLockout());
|
||||
if (rep.getMaxTemporaryLockouts() != null) newRealm.setMaxTemporaryLockouts(rep.getMaxTemporaryLockouts());
|
||||
if (rep.getBruteForceStrategy() != null) newRealm.setBruteForceStrategy(rep.getBruteForceStrategy());
|
||||
if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
|
||||
if (rep.getMinimumQuickLoginWaitSeconds() != null)
|
||||
newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
|
||||
|
@ -751,6 +752,7 @@ public class DefaultExportImportManager implements ExportImportManager {
|
|||
if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected());
|
||||
if (rep.isPermanentLockout() != null) realm.setPermanentLockout(rep.isPermanentLockout());
|
||||
if (rep.getMaxTemporaryLockouts() != null) realm.setMaxTemporaryLockouts(rep.getMaxTemporaryLockouts());
|
||||
if (rep.getBruteForceStrategy() != null) realm.setBruteForceStrategy(rep.getBruteForceStrategy());
|
||||
if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
|
||||
if (rep.getMinimumQuickLoginWaitSeconds() != null)
|
||||
realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
|
||||
|
|
|
@ -84,6 +84,7 @@ public class ModelToRepresentation {
|
|||
REALM_EXCLUDED_ATTRIBUTES.add("bruteForceProtected");
|
||||
REALM_EXCLUDED_ATTRIBUTES.add("permanentLockout");
|
||||
REALM_EXCLUDED_ATTRIBUTES.add("maxTemporaryLockouts");
|
||||
REALM_EXCLUDED_ATTRIBUTES.add("bruteForceStrategy");
|
||||
REALM_EXCLUDED_ATTRIBUTES.add("maxFailureWaitSeconds");
|
||||
REALM_EXCLUDED_ATTRIBUTES.add("waitIncrementSeconds");
|
||||
REALM_EXCLUDED_ATTRIBUTES.add("quickLoginCheckMilliSeconds");
|
||||
|
@ -372,6 +373,7 @@ public class ModelToRepresentation {
|
|||
rep.setBruteForceProtected(realm.isBruteForceProtected());
|
||||
rep.setPermanentLockout(realm.isPermanentLockout());
|
||||
rep.setMaxTemporaryLockouts(realm.getMaxTemporaryLockouts());
|
||||
rep.setBruteForceStrategy(realm.getBruteForceStrategy());
|
||||
rep.setMaxFailureWaitSeconds(realm.getMaxFailureWaitSeconds());
|
||||
rep.setMinimumQuickLoginWaitSeconds(realm.getMinimumQuickLoginWaitSeconds());
|
||||
rep.setWaitIncrementSeconds(realm.getWaitIncrementSeconds());
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.keycloak.models.RequiredCredentialModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.WebAuthnPolicy;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -187,6 +188,10 @@ public class RealmModelDelegate implements RealmModel {
|
|||
delegate.setBruteForceProtected(value);
|
||||
}
|
||||
|
||||
public RealmRepresentation.BruteForceStrategy getBruteForceStrategy() { return delegate.getBruteForceStrategy(); }
|
||||
|
||||
public void setBruteForceStrategy(RealmRepresentation.BruteForceStrategy value) { delegate.setBruteForceStrategy(value); }
|
||||
|
||||
public boolean isPermanentLockout() {
|
||||
return delegate.isPermanentLockout();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.broker.provider.util;
|
|||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -688,6 +689,16 @@ public class IdentityBrokerStateTestHelpers {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmRepresentation.BruteForceStrategy getBruteForceStrategy() {
|
||||
return RealmRepresentation.BruteForceStrategy.MULTIPLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBruteForceStrategy(RealmRepresentation.BruteForceStrategy val) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxFailureWaitSeconds() {
|
||||
return 0;
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.common.enums.SslRequired;
|
|||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -142,6 +143,8 @@ public interface RealmModel extends RoleContainerModel {
|
|||
void setPermanentLockout(boolean val);
|
||||
int getMaxTemporaryLockouts();
|
||||
void setMaxTemporaryLockouts(int val);
|
||||
RealmRepresentation.BruteForceStrategy getBruteForceStrategy();
|
||||
void setBruteForceStrategy(RealmRepresentation.BruteForceStrategy val);
|
||||
int getMaxFailureWaitSeconds();
|
||||
void setMaxFailureWaitSeconds(int val);
|
||||
int getWaitIncrementSeconds();
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserLoginFailureModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
@ -90,13 +91,18 @@ public class DefaultBruteForceProtector implements BruteForceProtector {
|
|||
|
||||
int waitSeconds = 0;
|
||||
if(!(realm.isPermanentLockout() && realm.getMaxTemporaryLockouts() == 0)) {
|
||||
if(RealmRepresentation.BruteForceStrategy.MULTIPLE.equals(realm.getBruteForceStrategy())) {
|
||||
waitSeconds = realm.getWaitIncrementSeconds() * (userLoginFailure.getNumFailures() / realm.getFailureFactor());
|
||||
} else {
|
||||
waitSeconds = realm.getWaitIncrementSeconds() * (1 + userLoginFailure.getNumFailures() - realm.getFailureFactor());
|
||||
}
|
||||
}
|
||||
|
||||
logger.debugv("waitSeconds: {0}", waitSeconds);
|
||||
logger.debugv("deltaTime: {0}", deltaTime);
|
||||
|
||||
boolean quickLoginFailure = false;
|
||||
if (waitSeconds == 0) {
|
||||
if (waitSeconds <= 0) {
|
||||
if (last > 0 && deltaTime < realm.getQuickLoginCheckMilliSeconds()) {
|
||||
logger.debugv("quick login, set min wait seconds");
|
||||
waitSeconds = realm.getMinimumQuickLoginWaitSeconds();
|
||||
|
|
|
@ -253,6 +253,7 @@ public class RealmManager {
|
|||
realm.setBruteForceProtected(false); // default settings off for now todo set it on
|
||||
realm.setPermanentLockout(false);
|
||||
realm.setMaxTemporaryLockouts(0);
|
||||
realm.setBruteForceStrategy(RealmRepresentation.BruteForceStrategy.MULTIPLE);
|
||||
realm.setMaxFailureWaitSeconds(900);
|
||||
realm.setMinimumQuickLoginWaitSeconds(60);
|
||||
realm.setWaitIncrementSeconds(60);
|
||||
|
|
|
@ -112,6 +112,7 @@ public class BruteForceTest extends AbstractTestRealmKeycloakTest {
|
|||
UserBuilder.edit(user).totpSecret("totpSecret").emailVerified(true);
|
||||
|
||||
testRealm.setBruteForceProtected(true);
|
||||
testRealm.setBruteForceStrategy(RealmRepresentation.BruteForceStrategy.MULTIPLE);
|
||||
testRealm.setFailureFactor(failureFactor);
|
||||
testRealm.setMaxDeltaTimeSeconds(60);
|
||||
testRealm.setMaxFailureWaitSeconds(100);
|
||||
|
@ -131,6 +132,7 @@ public class BruteForceTest extends AbstractTestRealmKeycloakTest {
|
|||
clearUserFailures();
|
||||
clearAllUserFailures();
|
||||
RealmRepresentation realm = adminClient.realm("test").toRepresentation();
|
||||
realm.setBruteForceStrategy(RealmRepresentation.BruteForceStrategy.MULTIPLE);
|
||||
realm.setFailureFactor(failureFactor);
|
||||
realm.setMaxDeltaTimeSeconds(60);
|
||||
realm.setMaxFailureWaitSeconds(100);
|
||||
|
@ -501,6 +503,56 @@ public class BruteForceTest extends AbstractTestRealmKeycloakTest {
|
|||
testingClient.testing().setTimeOffset(Collections.singletonMap("offset", String.valueOf(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByMultipleStrategy() throws Exception {
|
||||
|
||||
try {
|
||||
UserRepresentation user = adminClient.realm("test").users().search("test-user@localhost", 0, 1).get(0);
|
||||
loginSuccess();
|
||||
loginInvalidPassword();
|
||||
loginInvalidPassword();
|
||||
expectTemporarilyDisabled();
|
||||
assertUserNumberOfFailures(user.getId(), 2);
|
||||
this.setTimeOffset(30);
|
||||
|
||||
loginInvalidPassword();
|
||||
assertUserNumberOfFailures(user.getId(), 3);
|
||||
this.setTimeOffset(60);
|
||||
loginSuccess();
|
||||
} finally {
|
||||
this.resetTimeOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLinearStrategy() throws Exception {
|
||||
RealmRepresentation realm = testRealm().toRepresentation();
|
||||
UserRepresentation user = adminClient.realm("test").users().search("test-user@localhost", 0, 1).get(0);
|
||||
try {
|
||||
realm.setBruteForceStrategy(RealmRepresentation.BruteForceStrategy.LINEAR);
|
||||
testRealm().update(realm);
|
||||
|
||||
loginSuccess();
|
||||
|
||||
loginInvalidPassword();
|
||||
loginInvalidPassword();
|
||||
expectTemporarilyDisabled();
|
||||
assertUserNumberOfFailures(user.getId(), 2);
|
||||
this.setTimeOffset(30);
|
||||
|
||||
loginInvalidPassword();
|
||||
assertUserNumberOfFailures(user.getId(), 3);
|
||||
this.setTimeOffset(60);
|
||||
expectTemporarilyDisabled();
|
||||
|
||||
} finally {
|
||||
realm.setPermanentLockout(false);
|
||||
realm.setBruteForceStrategy(RealmRepresentation.BruteForceStrategy.MULTIPLE);
|
||||
testRealm().update(realm);
|
||||
this.resetTimeOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrowserInvalidPasswordDifferentCase() throws Exception {
|
||||
loginSuccess("test-user@localhost");
|
||||
|
|
Loading…
Reference in a new issue