Wait for the brute force off-thread processing in AbstractAdvancedBrokerTest
Closes #30188 Closes #30641 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
3e134f714c
commit
5ea3becef5
2 changed files with 51 additions and 6 deletions
|
@ -27,6 +27,7 @@ import org.junit.rules.TestRule;
|
|||
import org.junit.runners.model.Statement;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
@ -41,6 +42,7 @@ import java.util.HashMap;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.emptyOrNullString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -369,16 +371,21 @@ public class AssertEvents implements TestRule {
|
|||
}
|
||||
|
||||
public EventRepresentation assertEvent() {
|
||||
return assertEvent(false);
|
||||
return assertEvent(false, 0);
|
||||
}
|
||||
|
||||
public EventRepresentation assertEvent(boolean ignorePreviousEvents) {
|
||||
return assertEvent(ignorePreviousEvents, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the expected event was sent to the listener by Keycloak server. Returns this event.
|
||||
*
|
||||
* @param ignorePreviousEvents if true, test will ignore all the events, which were already present. Test will poll the events from the queue until it finds the event of expected type
|
||||
* @param seconds The seconds to wait for the next event to come
|
||||
* @return the expected event
|
||||
*/
|
||||
public EventRepresentation assertEvent(boolean ignorePreviousEvents) {
|
||||
public EventRepresentation assertEvent(boolean ignorePreviousEvents, int seconds) {
|
||||
if (expected.getError() != null && ! expected.getType().endsWith("_ERROR")) {
|
||||
expected.setType(expected.getType() + "_ERROR");
|
||||
}
|
||||
|
@ -387,7 +394,7 @@ public class AssertEvents implements TestRule {
|
|||
// Consider 25 as a "limit" for maximum number of events in the queue for now
|
||||
List<String> presentedEventTypes = new LinkedList<>();
|
||||
for (int i = 0 ; i < 25 ; i++) {
|
||||
EventRepresentation event = fetchNextEvent();
|
||||
EventRepresentation event = fetchNextEvent(seconds);
|
||||
if (event == null) {
|
||||
Assert.fail("Did not find the event of expected type " + expected.getType() +". Events present: " + presentedEventTypes);
|
||||
}
|
||||
|
@ -523,4 +530,26 @@ public class AssertEvents implements TestRule {
|
|||
private EventRepresentation fetchNextEvent() {
|
||||
return context.testingClient.testing().pollEvent();
|
||||
}
|
||||
|
||||
private EventRepresentation fetchNextEvent(int seconds) {
|
||||
if (seconds <= 0) {
|
||||
return fetchNextEvent();
|
||||
}
|
||||
|
||||
final long millis = TimeUnit.SECONDS.toMillis(seconds);
|
||||
final long start = Time.currentTimeMillis();
|
||||
do {
|
||||
try {
|
||||
EventRepresentation event = fetchNextEvent();
|
||||
if (event != null) {
|
||||
return event;
|
||||
}
|
||||
// wait a bit to receive the event
|
||||
TimeUnit.MILLISECONDS.sleep(millis / 10L);
|
||||
} catch (InterruptedException e) {
|
||||
// no-op
|
||||
}
|
||||
} while (Time.currentTimeMillis() - start < millis);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.utils.TimeBasedOTP;
|
||||
|
@ -18,6 +21,7 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory;
|
||||
import org.keycloak.testsuite.util.AccountHelper;
|
||||
|
@ -70,6 +74,8 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
|||
*/
|
||||
public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
protected void createRoleMappersForConsumerRealm() {
|
||||
createRoleMappersForConsumerRealm(IdentityProviderMapperSyncMode.FORCE);
|
||||
|
@ -546,7 +552,8 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
|||
totpPage.assertCurrent();
|
||||
String totpSecret = totpPage.getTotpSecret();
|
||||
totpPage.configure(totp.generateTOTP(totpSecret));
|
||||
assertNumFederatedIdentities(realm.users().search(bc.getUserLogin()).get(0).getId(), 1);
|
||||
final UserRepresentation user = realm.users().search(bc.getUserLogin()).get(0);
|
||||
assertNumFederatedIdentities(user.getId(), 1);
|
||||
AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), bc.getUserLogin());
|
||||
AccountHelper.logout(adminClient.realm(bc.providerRealmName()), bc.getUserLogin());
|
||||
|
||||
|
@ -563,16 +570,25 @@ public abstract class AbstractAdvancedBrokerTest extends AbstractBrokerTest {
|
|||
loginTotpPage.login("bad-totp");
|
||||
Assert.assertEquals("Invalid authenticator code.", loginTotpPage.getInputError());
|
||||
|
||||
events.clear();
|
||||
|
||||
loginTotpPage.login("bad-totp");
|
||||
Assert.assertEquals("Invalid authenticator code.", loginTotpPage.getInputError());
|
||||
|
||||
// wait for the disabled to come
|
||||
events.expect(EventType.USER_DISABLED_BY_TEMPORARY_LOCKOUT)
|
||||
.realm(consumerRealmRep.getId())
|
||||
.user(user.getId())
|
||||
.client((String) null)
|
||||
.detail(Details.REASON, "brute_force_attack detected")
|
||||
.assertEvent(true, 5);
|
||||
|
||||
// Login with valid TOTP. I should not be able to login
|
||||
loginTotpPage.login(totp.generateTOTP(totpSecret));
|
||||
Assert.assertEquals("Invalid authenticator code.", loginTotpPage.getInputError());
|
||||
|
||||
// Clear login failures
|
||||
String userId = ApiUtil.findUserByUsername(realm, bc.getUserLogin()).getId();
|
||||
realm.attackDetection().clearBruteForceForUser(userId);
|
||||
realm.attackDetection().clearBruteForceForUser(user.getId());
|
||||
|
||||
setOtpTimeOffset(TimeBasedOTP.DEFAULT_INTERVAL_SECONDS, totp);
|
||||
|
||||
|
|
Loading…
Reference in a new issue