Using _system client when account client is disabled for email actions

Closes #17857

Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
Giuseppe Graziano 2024-06-28 17:18:40 +02:00 committed by Alexander Schwartz
parent 20cedb84eb
commit 02d64d959c
3 changed files with 57 additions and 12 deletions

View file

@ -43,7 +43,7 @@ public class SystemClientUtil {
public static ClientModel getSystemClient(RealmModel realm) {
// Try to return builtin "account" client first
ClientModel client = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
if (client != null) {
if (client != null && client.isEnabled()) {
return client;
}

View file

@ -60,6 +60,7 @@ import org.keycloak.models.light.LightweightUserAdapter;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.models.utils.SystemClientUtil;
import org.keycloak.policy.PasswordPolicyNotMetException;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.utils.RedirectUtils;
@ -1112,11 +1113,8 @@ public class UserResource {
throw ErrorResponse.error("Client id missing", Status.BAD_REQUEST);
}
if (clientId == null) {
clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
}
ClientModel client = realm.getClientByClientId(clientId);
ClientModel client = clientId != null ? realm.getClientByClientId(clientId) : SystemClientUtil.getSystemClient(realm);
if (client == null) {
logger.debugf("Client %s doesn't exist", clientId);
throw ErrorResponse.error("Client doesn't exist", Status.BAD_REQUEST);

View file

@ -52,6 +52,7 @@ import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.StripSecretsUtils;
import org.keycloak.models.utils.SystemClientUtil;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
@ -800,16 +801,16 @@ public class UserTest extends AbstractAdminTest {
Map<String, String> attributes = new HashMap<>();
attributes.put("test1", "test2");
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(0));
attributes = new HashMap<>();
attributes.put("test", "test1");
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(1));
attributes = new HashMap<>();
attributes.put("test", "test2");
attributes.put("attr", "common");
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(1));
attributes = new HashMap<>();
attributes.put("attr", "common");
assertThat(realm.users().count(null, null, null, null, null, null, null, mapToSearchQuery(attributes)), is(9));
@ -965,14 +966,14 @@ public class UserTest extends AbstractAdminTest {
getCleanup().addUserId(createUser(REALM_NAME, "user1", "password", "user1FirstName", "user1LastName", "user1@example.com",
user -> user.setAttributes(Map.of("test1", List.of(longValue, "v2"), "test2", List.of("v2")))));
getCleanup().addUserId(createUser(REALM_NAME, "user2", "password", "user2FirstName", "user2LastName", "user2@example.com",
getCleanup().addUserId(createUser(REALM_NAME, "user2", "password", "user2FirstName", "user2LastName", "user2@example.com",
user -> user.setAttributes(Map.of("test1", List.of(longValue, "v2"), "test2", List.of(longValue2)))));
getCleanup().addUserId(createUser(REALM_NAME, "user3", "password", "user3FirstName", "user3LastName", "user3@example.com",
getCleanup().addUserId(createUser(REALM_NAME, "user3", "password", "user3FirstName", "user3LastName", "user3@example.com",
user -> user.setAttributes(Map.of("test2", List.of(longValue, "v3"), "test4", List.of("v4")))));
assertThat(realm.users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
assertThat(realm.users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
containsInAnyOrder("user1", "user2"));
assertThat(realm.users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue, "test2", longValue2))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
assertThat(realm.users().searchByAttributes(mapToSearchQuery(Map.of("test1", longValue, "test2", longValue2))).stream().map(UserRepresentation::getUsername).collect(Collectors.toList()),
contains("user2"));
//case-insensitive search
@ -1908,6 +1909,8 @@ public class UserTest extends AbstractAdminTest {
passwordUpdatePage.changePassword("new-pass", "new-pass");
assertThat(driver.getCurrentUrl(), Matchers.containsString("client_id=" + Constants.ACCOUNT_MANAGEMENT_CLIENT_ID));
assertEquals("Your account has been updated.", PageUtils.getPageTitle(driver));
driver.navigate().to(link);
@ -1915,6 +1918,50 @@ public class UserTest extends AbstractAdminTest {
assertEquals("We are sorry...", PageUtils.getPageTitle(driver));
}
@Test
public void sendResetPasswordEmailSuccessWithAccountClientDisabled() throws IOException {
ClientRepresentation clientRepresentation = realm.clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
clientRepresentation.setEnabled(false);
realm.clients().get(clientRepresentation.getId()).update(clientRepresentation);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientResourcePath(clientRepresentation.getId()), clientRepresentation, ResourceType.CLIENT);
UserRepresentation userRep = new UserRepresentation();
userRep.setEnabled(true);
userRep.setUsername("user1");
userRep.setEmail("user1@test.com");
String id = createUser(userRep);
UserResource user = realm.users().get(id);
List<String> actions = new LinkedList<>();
actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
user.executeActionsEmail(actions);
assertAdminEvents.assertEvent(realmId, OperationType.ACTION, AdminEventPaths.userResourcePath(id) + "/execute-actions-email", ResourceType.USER);
Assert.assertEquals(1, greenMail.getReceivedMessages().length);
MimeMessage message = greenMail.getReceivedMessages()[0];
MailUtils.EmailBody body = MailUtils.getBody(message);
String link = MailUtils.getPasswordResetEmailLink(body);
driver.navigate().to(link);
proceedPage.assertCurrent();
assertThat(proceedPage.getInfo(), Matchers.containsString("Update Password"));
proceedPage.clickProceedLink();
passwordUpdatePage.assertCurrent();
passwordUpdatePage.changePassword("new-pass", "new-pass");
assertThat(driver.getCurrentUrl(), Matchers.containsString("client_id=" + SystemClientUtil.SYSTEM_CLIENT_ID));
clientRepresentation.setEnabled(true);
realm.clients().get(clientRepresentation.getId()).update(clientRepresentation);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientResourcePath(clientRepresentation.getId()), clientRepresentation, ResourceType.CLIENT);
}
@Test
public void testEmailLinkBasedOnRealmFrontEndUrl() throws Exception {
try {