Updating the UP configuration needs to trigger an admin event

Close #23896

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2023-12-14 13:43:48 +01:00 committed by Marek Posolda
parent bee7595275
commit d841971ff4
14 changed files with 327 additions and 17 deletions

View file

@ -21,6 +21,7 @@ package org.keycloak.representations.userprofile.config;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* Configuration of the Attribute. * Configuration of the Attribute.
@ -170,4 +171,28 @@ public class UPAttribute implements Cloneable {
attr.setGroup(this.group); attr.setGroup(this.group);
return attr; return attr;
} }
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final UPAttribute other = (UPAttribute) obj;
return Objects.equals(this.name, other.name)
&& Objects.equals(this.displayName, other.displayName)
&& Objects.equals(this.group, other.group)
&& Objects.equals(this.validations, other.validations)
&& Objects.equals(this.annotations, other.annotations)
&& Objects.equals(this.required, other.required)
&& Objects.equals(this.permissions, other.permissions)
&& Objects.equals(this.selector, other.selector);
}
} }

View file

@ -20,6 +20,7 @@ package org.keycloak.representations.userprofile.config;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
@ -75,4 +76,22 @@ public class UPAttributePermissions implements Cloneable {
Set<String> edit = this.edit == null ? null : new HashSet<>(this.edit); Set<String> edit = this.edit == null ? null : new HashSet<>(this.edit);
return new UPAttributePermissions(view, edit); return new UPAttributePermissions(view, edit);
} }
@Override
public int hashCode() {
return Objects.hash(view, edit);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final UPAttributePermissions other = (UPAttributePermissions) obj;
return Objects.equals(this.view, other.view)
&& Objects.equals(this.edit, other.edit);
}
} }

View file

@ -19,6 +19,7 @@
package org.keycloak.representations.userprofile.config; package org.keycloak.representations.userprofile.config;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
@ -82,4 +83,21 @@ public class UPAttributeRequired implements Cloneable {
return new UPAttributeRequired(roles, scopes); return new UPAttributeRequired(roles, scopes);
} }
@Override
public int hashCode() {
return Objects.hash(roles, scopes);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final UPAttributeRequired other = (UPAttributeRequired) obj;
return Objects.equals(this.roles, other.roles)
&& Objects.equals(this.scopes, other.scopes);
}
} }

View file

@ -19,6 +19,7 @@
package org.keycloak.representations.userprofile.config; package org.keycloak.representations.userprofile.config;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@ -56,4 +57,21 @@ public class UPAttributeSelector implements Cloneable {
protected UPAttributeSelector clone() { protected UPAttributeSelector clone() {
return new UPAttributeSelector(scopes == null ? null : new HashSet<>(scopes)); return new UPAttributeSelector(scopes == null ? null : new HashSet<>(scopes));
} }
@Override
public int hashCode() {
return Objects.hash(scopes);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final UPAttributeSelector other = (UPAttributeSelector) obj;
return Objects.equals(this.scopes, other.scopes);
}
} }

View file

@ -21,6 +21,7 @@ package org.keycloak.representations.userprofile.config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
@ -137,4 +138,23 @@ public class UPConfig implements Cloneable {
return cfg; return cfg;
} }
@Override
public int hashCode() {
return Objects.hash(attributes, groups, unmanagedAttributePolicy);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final UPConfig other = (UPConfig) obj;
return Objects.equals(this.attributes, other.attributes)
&& Objects.equals(this.groups, other.groups)
&& this.unmanagedAttributePolicy == other.unmanagedAttributePolicy;
}
} }

View file

@ -21,6 +21,7 @@ package org.keycloak.representations.userprofile.config;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* Configuration of the attribute group. * Configuration of the attribute group.
@ -82,4 +83,24 @@ public class UPGroup implements Cloneable {
group.setAnnotations(this.annotations == null ? null : new HashMap<>(this.annotations)); group.setAnnotations(this.annotations == null ? null : new HashMap<>(this.annotations));
return group; return group;
} }
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final UPGroup other = (UPGroup) obj;
return Objects.equals(this.name, other.name)
&& Objects.equals(this.displayHeader, other.displayHeader)
&& Objects.equals(this.displayDescription, other.displayDescription)
&& Objects.equals(this.annotations, other.annotations);
}
} }

View file

@ -19,8 +19,6 @@
package org.keycloak.admin.ui.rest; package org.keycloak.admin.ui.rest;
import java.io.IOException;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.PUT; import jakarta.ws.rs.PUT;
@ -35,7 +33,6 @@ import org.keycloak.services.resources.admin.AdminEventBuilder;
import org.keycloak.services.resources.admin.RealmAdminResource; import org.keycloak.services.resources.admin.RealmAdminResource;
import org.keycloak.services.resources.admin.UserProfileResource; import org.keycloak.services.resources.admin.UserProfileResource;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.util.JsonSerialization;
/** /**
* This JAX-RS resource is decorating the Admin Realm API in order to support specific behaviors from the * This JAX-RS resource is decorating the Admin Realm API in order to support specific behaviors from the
@ -48,10 +45,12 @@ public class UIRealmResource {
private final RealmAdminResource delegate; private final RealmAdminResource delegate;
private final KeycloakSession session; private final KeycloakSession session;
private final AdminPermissionEvaluator auth; private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
public UIRealmResource(KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) { public UIRealmResource(KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.session = session; this.session = session;
this.auth = auth; this.auth = auth;
this.adminEvent = adminEvent;
this.delegate = new RealmAdminResource(session, auth, adminEvent); this.delegate = new RealmAdminResource(session, auth, adminEvent);
} }
@ -75,7 +74,9 @@ public class UIRealmResource {
return; return;
} }
Response response = new UserProfileResource(session, auth).update(upConfig); UserProfileResource userProfileResource = new UserProfileResource(session, auth, adminEvent);
if (!upConfig.equals(userProfileResource.getConfiguration())) {
Response response = userProfileResource.update(upConfig);
if (isSuccessful(response)) { if (isSuccessful(response)) {
return; return;
@ -83,6 +84,7 @@ public class UIRealmResource {
throw new InternalServerErrorException("Failed to update user profile configuration"); throw new InternalServerErrorException("Failed to update user profile configuration");
} }
}
private boolean isSuccessful(Response response) { private boolean isSuccessful(Response response) {
return Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()); return Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily());

View file

@ -186,5 +186,10 @@ public enum ResourceType {
/** /**
* *
*/ */
, CUSTOM; , CUSTOM
/**
* The user profile configuration
*/
, USER_PROFILE;
} }

View file

@ -36,6 +36,8 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.keycloak.component.ComponentValidationException; import org.keycloak.component.ComponentValidationException;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.UserProfileMetadata; import org.keycloak.representations.idm.UserProfileMetadata;
@ -54,14 +56,15 @@ import org.keycloak.representations.userprofile.config.UPConfig;
public class UserProfileResource { public class UserProfileResource {
protected final KeycloakSession session; protected final KeycloakSession session;
protected final AdminEventBuilder adminEvent;
protected final RealmModel realm; protected final RealmModel realm;
private final AdminPermissionEvaluator auth; private final AdminPermissionEvaluator auth;
public UserProfileResource(KeycloakSession session, AdminPermissionEvaluator auth) { public UserProfileResource(KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.session = session; this.session = session;
this.realm = session.getContext().getRealm(); this.realm = session.getContext().getRealm();
this.auth = auth; this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.USER_PROFILE);
} }
@GET @GET
@ -101,6 +104,11 @@ public class UserProfileResource {
throw ErrorResponse.error(e.getMessage(), Response.Status.BAD_REQUEST); throw ErrorResponse.error(e.getMessage(), Response.Status.BAD_REQUEST);
} }
adminEvent.operation(OperationType.UPDATE)
.resourcePath(session.getContext().getUri())
.representation(config)
.success();
return Response.ok(t.getConfiguration()).type(MediaType.APPLICATION_JSON).build(); return Response.ok(t.getConfiguration()).type(MediaType.APPLICATION_JSON).build();
} }
} }

View file

@ -70,7 +70,6 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static org.keycloak.models.Constants.SESSION_NOTE_LIGHTWEIGHT_USER;
import static org.keycloak.models.utils.KeycloakModelUtils.findGroupByPath; import static org.keycloak.models.utils.KeycloakModelUtils.findGroupByPath;
import static org.keycloak.userprofile.UserProfileContext.USER_API; import static org.keycloak.userprofile.UserProfileContext.USER_API;
@ -460,7 +459,7 @@ public class UsersResource {
*/ */
@Path("profile") @Path("profile")
public UserProfileResource userProfile() { public UserProfileResource userProfile() {
return new UserProfileResource(session, auth); return new UserProfileResource(session, auth, adminEvent);
} }
private Stream<UserRepresentation> searchForUser(Map<String, String> attributes, RealmModel realm, UserPermissionEvaluator usersEvaluator, Boolean briefRepresentation, Integer firstResult, Integer maxResults, Boolean includeServiceAccounts) { private Stream<UserRepresentation> searchForUser(Map<String, String> attributes, RealmModel realm, UserPermissionEvaluator usersEvaluator, Boolean briefRepresentation, Integer firstResult, Integer maxResults, Boolean includeServiceAccounts) {

View file

@ -51,13 +51,13 @@ public class UserTestWithUserProfile extends UserTest {
public void onBefore() throws IOException { public void onBefore() throws IOException {
RealmRepresentation realmRep = realm.toRepresentation(); RealmRepresentation realmRep = realm.toRepresentation();
VerifyProfileTest.disableDynamicUserProfile(realm); VerifyProfileTest.disableDynamicUserProfile(realm);
assertAdminEvents.poll(); assertAdminEvents.poll(); // update realm
realm.update(realmRep); assertAdminEvents.poll(); // set UP configuration
assertAdminEvents.poll();
VerifyProfileTest.enableDynamicUserProfile(realmRep); VerifyProfileTest.enableDynamicUserProfile(realmRep);
realm.update(realmRep); realm.update(realmRep);
assertAdminEvents.poll(); assertAdminEvents.poll();
VerifyProfileTest.setUserProfileConfiguration(realm, null); VerifyProfileTest.setUserProfileConfiguration(realm, null);
assertAdminEvents.poll();
UPConfig upConfig = realm.users().userProfile().getConfiguration(); UPConfig upConfig = realm.users().userProfile().getConfiguration();
for (String name : managedAttributes) { for (String name : managedAttributes) {
@ -65,6 +65,7 @@ public class UserTestWithUserProfile extends UserTest {
} }
VerifyProfileTest.setUserProfileConfiguration(realm, JsonSerialization.writeValueAsString(upConfig)); VerifyProfileTest.setUserProfileConfiguration(realm, JsonSerialization.writeValueAsString(upConfig));
assertAdminEvents.poll();
} }
@Test @Test

View file

@ -44,7 +44,10 @@ import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
@ -61,6 +64,8 @@ import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.VerifyProfilePage; import org.keycloak.testsuite.pages.VerifyProfilePage;
import org.keycloak.testsuite.runonserver.RunOnServer; import org.keycloak.testsuite.runonserver.RunOnServer;
import org.keycloak.testsuite.util.AdminEventPaths;
import org.keycloak.testsuite.util.AssertAdminEvents;
import org.keycloak.testsuite.util.ClientScopeBuilder; import org.keycloak.testsuite.util.ClientScopeBuilder;
import org.keycloak.testsuite.util.JsonTestUtils; import org.keycloak.testsuite.util.JsonTestUtils;
import org.keycloak.testsuite.util.KeycloakModelUtils; import org.keycloak.testsuite.util.KeycloakModelUtils;
@ -162,6 +167,9 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
@Rule @Rule
public AssertEvents events = new AssertEvents(this); public AssertEvents events = new AssertEvents(this);
@Rule
public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this);
@Page @Page
protected AppPage appPage; protected AppPage appPage;
@ -1193,7 +1201,14 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
} }
protected UPConfig setUserProfileConfiguration(String configuration) { protected UPConfig setUserProfileConfiguration(String configuration) {
return setUserProfileConfiguration(testRealm(), configuration); assertAdminEvents.clear();
UPConfig result = setUserProfileConfiguration(testRealm(), configuration);
AdminEventRepresentation adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME,
OperationType.UPDATE, AdminEventPaths.userProfilePath(), ResourceType.USER_PROFILE);
Assert.assertTrue("Incorrect representation in event", StringUtils.isBlank(configuration)
? StringUtils.isBlank(adminEvent.getRepresentation())
: StringUtils.isNotBlank(adminEvent.getRepresentation()));
return result;
} }
public static void enableDynamicUserProfile(RealmRepresentation testRealm) { public static void enableDynamicUserProfile(RealmRepresentation testRealm) {

View file

@ -0,0 +1,152 @@
/*
*
* * Copyright 2023 Red Hat, Inc. and/or its affiliates
* * and other contributors as indicated by the @author tags.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.keycloak.testsuite.user.profile;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.BearerAuthFilter;
import org.keycloak.admin.ui.rest.model.UIRealmRepresentation;
import org.keycloak.common.Profile;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.userprofile.config.UPAttribute;
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
import org.keycloak.representations.userprofile.config.UPAttributeRequired;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.AssertAdminEvents;
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
import org.keycloak.userprofile.config.UPConfigUtils;
import org.keycloak.util.JsonSerialization;
/**
*
* @author rmartinc
*/
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE)
public class UIRealmResourceTest extends AbstractTestRealmKeycloakTest {
@Rule
public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this);
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
if (testRealm.getAttributes() == null) {
testRealm.setAttributes(new HashMap<>());
}
testRealm.getAttributes().put(DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
}
@Test
public void testNoUpdateUserProfile() throws IOException {
RealmRepresentation rep = testRealm().toRepresentation();
updateRealmExt(toUIRealmRepresentation(rep, null));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
assertAdminEvents.assertEmpty();
}
@Test
public void testSameUpdateUserProfile() throws IOException {
RealmRepresentation rep = testRealm().toRepresentation();
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
assertAdminEvents.assertEmpty();
}
@Test
public void testUpdateUserProfileModification() throws IOException {
RealmRepresentation rep = testRealm().toRepresentation();
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
upConfig.addOrReplaceAttribute(new UPAttribute("foo",
new UPAttributePermissions(Set.of(), Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN))));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
AdminEventRepresentation adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
Assert.assertNotNull(adminEvent.getRepresentation());
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
Assert.assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setDisplayName("Foo");
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
Assert.assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setPermissions(new UPAttributePermissions(Set.of(), Set.of(UPConfigUtils.ROLE_USER)));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
Assert.assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setRequired(new UPAttributeRequired(Set.of(UPConfigUtils.ROLE_ADMIN, UPConfigUtils.ROLE_USER), Set.of()));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
Assert.assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setValidations(Map.of("length", Map.of("min", "3", "max", "128")));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
Assert.assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
assertAdminEvents.assertEmpty();
}
private void updateRealmExt(UIRealmRepresentation rep) {
try (Client client = Keycloak.getClientProvider().newRestEasyClient(null, null, true)) {
Response response = client.target(suiteContext.getAuthServerInfo().getContextRoot().toString() + "/auth")
.path("/admin/realms/" + rep.getRealm() + "/ui-ext")
.register(new BearerAuthFilter(adminClient.tokenManager()))
.request(MediaType.APPLICATION_JSON)
.put(Entity.entity(rep, MediaType.APPLICATION_JSON));
Assert.assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
}
}
private UIRealmRepresentation toUIRealmRepresentation(RealmRepresentation realm, UPConfig upConfig) throws IOException {
UIRealmRepresentation uiRealm = JsonSerialization.readValue(JsonSerialization.writeValueAsString(realm), UIRealmRepresentation.class);
uiRealm.setUpConfig(upConfig);
return uiRealm;
}
private UPConfig toUpConfig(String representation) throws IOException {
return JsonSerialization.readValue(representation, UPConfig.class);
}
}

View file

@ -36,6 +36,7 @@ import org.keycloak.admin.client.resource.RoleByIdResource;
import org.keycloak.admin.client.resource.RoleMappingResource; import org.keycloak.admin.client.resource.RoleMappingResource;
import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource; import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.admin.client.resource.UserProfileResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource; import org.keycloak.admin.client.resource.UsersResource;
@ -69,6 +70,12 @@ public class AdminEventPaths {
return uri.toString(); return uri.toString();
} }
public static String userProfilePath() {
URI uri = UriBuilder.fromUri("").path(RealmResource.class, "users")
.path(UsersResource.class, "userProfile")
.build();
return uri.toString();
}
// CLIENT RESOURCE // CLIENT RESOURCE