diff --git a/docs/documentation/upgrading/topics/changes/changes-26_3_0.adoc b/docs/documentation/upgrading/topics/changes/changes-26_3_0.adoc index dd0b574742e..79285fe50c6 100644 --- a/docs/documentation/upgrading/topics/changes/changes-26_3_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-26_3_0.adoc @@ -22,6 +22,10 @@ It has been a while since discussions started about any activity around the Inst and any objection from the community about deprecating it for removal. For more details, see https://github.com/keycloak/keycloak/issues/37967[Deprecate for removal the Instagram social broker]. +=== Inconsistent user endpoints harmonized + +In previous releases there was an inconsistency in the REST endpoint result of getting a user (`GET /admin/realms/{realm}/users/{user-id}`) and searching for a user (`GET /admin/realms/{realm}/users`). When BruteForce is enabled and a user was temporarily locked out the former endpoint would return enabled=false while the latter would return enabled=true. If the user was updated and enabled was false due to temporary lockout then the user would be disabled permanently. Both endpoints now return enabled=true when a user is temporarily locked out. To check whether a user is temporarily locked out the BruteForceUserResource endpoint should be utilised (`GET /admin/realms/{realm}/attack-detection/brute-force/users/{userId}`). + === Deprecated password policy Recovery Codes Warning Threshold In relation to supported Recovery codes, we deprecated the password policy `Recovery Codes Warning Threshold`. This password policy might be removed in the future major version of {project_name}. diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java index 7cef9ab9052..45475bdeb2b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java @@ -354,9 +354,6 @@ public class UserResource { rep.setFederatedIdentities(reps); } - if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) { - rep.setEnabled(false); - } rep.setAccess(auth.users().getAccess(user)); if (!userProfileMetadata) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java index 6e5127c2688..65e14f093bd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java @@ -539,6 +539,19 @@ public class BruteForceTest extends AbstractChangeImportedUserPasswordsTest { expectTemporarilyDisabled("user2", userId); clearAllUserFailures(); } + + @Test + public void testUserDisabledTemporaryLockout() throws Exception { + String userId = adminClient.realm("test").users().search("test-user@localhost", null, null, null, 0, 1).get(0).getId(); + + loginInvalidPassword(); + loginInvalidPassword(); + expectTemporarilyDisabled(); + + assertTrue(testRealm().users().get(userId).toRepresentation().isEnabled()); + assertTrue(testRealm().users().search("test-user@localhost", true).get(0).isEnabled()); + assertEquals(Boolean.TRUE, testRealm().attackDetection().bruteForceUserStatus(userId).get("disabled")); + } @Test public void testBrowserMissingPassword() throws Exception { @@ -819,7 +832,9 @@ public class BruteForceTest extends AbstractChangeImportedUserPasswordsTest { assertTrue(passwordUpdatePage.isCurrent()); UserRepresentation userRepresentation = testRealm().users().get(userId).toRepresentation(); - assertFalse(userRepresentation.isEnabled()); + assertTrue(userRepresentation.isEnabled()); + Map bruteForceStatus = testRealm().attackDetection().bruteForceUserStatus(userId); + assertEquals(Boolean.TRUE, bruteForceStatus.get("disabled")); updatePasswordPage.updatePasswords(getPassword("user2"), getPassword("user2")); @@ -828,6 +843,8 @@ public class BruteForceTest extends AbstractChangeImportedUserPasswordsTest { userRepresentation = testRealm().users().get(userId).toRepresentation(); assertTrue(userRepresentation.isEnabled()); + bruteForceStatus = testRealm().attackDetection().bruteForceUserStatus(userId); + assertEquals(Boolean.FALSE, bruteForceStatus.get("disabled")); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());