From 445409c72de0e1c1f14f9b3ecf99863eb61fe177 Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Tue, 20 Aug 2024 10:47:01 +0200 Subject: [PATCH] OZG-6472 ad userProfileConfig attribute ozgCloudUserId --- .../realm/KeycloakRealmRemoteService.java | 14 +++ .../keycloak/realm/KeycloakRealmService.java | 43 +++++++-- .../realm/KeycloakRealmRemoteServiceTest.java | 52 ++++++++++- .../realm/KeycloakRealmServiceTest.java | 88 ++++++++++++++++++- 4 files changed, 183 insertions(+), 14 deletions(-) diff --git a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java index 5829cac..f896921 100644 --- a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteService.java @@ -29,6 +29,7 @@ import java.util.Optional; import org.keycloak.admin.client.Keycloak; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.userprofile.config.UPConfig; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakException; @@ -69,4 +70,17 @@ class KeycloakRealmRemoteService { keycloak.realm(realm).roles().create(role); } + UPConfig getUserProfileConfig(RealmRepresentation realmRepresentation) { + return keycloak.realm(realmRepresentation.getRealm()) + .users() + .userProfile() + .getConfiguration(); + } + + void updateUserProfileConfig(RealmRepresentation realmRepresentation, UPConfig userProfileConfig) { + keycloak.realm(realmRepresentation.getRealm()) + .users() + .userProfile() + .update(userProfileConfig); + } } diff --git a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java index 1ebad4e..f219de3 100644 --- a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmService.java @@ -24,8 +24,12 @@ package de.ozgcloud.operator.keycloak.realm; import java.util.Optional; +import java.util.Set; 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.UPConfig; import org.springframework.stereotype.Component; import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; @@ -37,21 +41,15 @@ import lombok.extern.log4j.Log4j2; @Component class KeycloakRealmService { + public static final String USER_PROFILE_CONFIG_OZGCLOUD_ATTRIBUTE_NAME = "ozgCloudUserId"; + private final KeycloakRealmRemoteService remoteService; private final KeycloakRealmMapper mapper; private final KeycloakGenericRemoteService keycloakGenericRemoteService; - void createRealm(OzgCloudKeycloakRealmSpec realm, String realmName) { - Optional.of(realm) - .map(mapper::map) - .map(realmRepresentation -> addRealmName(realmRepresentation, realmName)) - .filter(realmRepresentation -> !keycloakGenericRemoteService.realmExists(realmName)) - .ifPresent(remoteService::createRealm); - } - - public void createOrUpdateRealm(OzgCloudKeycloakRealmSpec realm, String realmName) { + void createOrUpdateRealm(OzgCloudKeycloakRealmSpec realm, String realmName) { keycloakGenericRemoteService.getRealmRepresentation(realmName) .ifPresentOrElse(existingRealm -> updateRealm(existingRealm, realm), () -> createRealm(realm, realmName)); @@ -63,12 +61,25 @@ class KeycloakRealmService { LOG.debug("{}: Updating existing realm...", existingRealm); var realmRepresentation = mapper.update(existingRealm, spec); remoteService.updateRealm(realmRepresentation); + addUserProfileAttributes(realmRepresentation); } catch (Exception e) { LOG.warn(existingRealm + ": Updating existing realm failed: ", e); throw e; } addOrUpdateRealmRoles(spec, existingRealm.getRealm()); + } + void createRealm(OzgCloudKeycloakRealmSpec realm, String realmName) { + Optional.of(realm) + .map(mapper::map) + .map(realmRepresentation -> addRealmName(realmRepresentation, realmName)) + // TODO dieser Filter kann vermutlich gelöscht werden, die Prüfung auf + // realmExists passiert bereits vorher + .filter(realmRepresentation -> !keycloakGenericRemoteService.realmExists(realmName)) + .ifPresent(realmRepresentation -> { + remoteService.createRealm(realmRepresentation); + addUserProfileAttributes(realmRepresentation); + }); } void addOrUpdateRealmRoles(OzgCloudKeycloakRealmSpec spec, String realm) { @@ -91,4 +102,18 @@ class KeycloakRealmService { public boolean realmExists(String realmName) { return keycloakGenericRemoteService.realmExists(realmName); } + + void addUserProfileAttributes(RealmRepresentation realmRepresentation) { + UPConfig userProfileConfig = remoteService.getUserProfileConfig(realmRepresentation); + + updateUserProfileConfig(userProfileConfig); + + remoteService.updateUserProfileConfig(realmRepresentation, userProfileConfig); + } + + void updateUserProfileConfig(UPConfig userProfileConfig) { + userProfileConfig.addOrReplaceAttribute( + new UPAttribute(USER_PROFILE_CONFIG_OZGCLOUD_ATTRIBUTE_NAME, + new UPAttributePermissions(Set.of("admin", "user"), Set.of("admin", "user")))); + } } diff --git a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java index 0b20c33..83bfaf5 100644 --- a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmRemoteServiceTest.java @@ -23,10 +23,10 @@ */ package de.ozgcloud.operator.keycloak.realm; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; import java.util.List; import java.util.Optional; @@ -39,7 +39,11 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmsResource; import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.RolesResource; +import org.keycloak.admin.client.resource.UserProfileResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RoleRepresentation; +import org.keycloak.representations.userprofile.config.UPConfig; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; @@ -108,7 +112,7 @@ class KeycloakRealmRemoteServiceTest { verify(realmResource).update(realm); } } - + @Nested class TestDeleteRealm { @@ -191,4 +195,44 @@ class KeycloakRealmRemoteServiceTest { } } } + + @Nested + class TestUserProfileConfig { + + @Mock + RealmRepresentation realmRepresentation; + + @Mock + UsersResource usersResource; + + @Mock + UserProfileResource userProfileResource; + + @Mock + UPConfig userProfileConfig; + + @BeforeEach + void init() { + when(realmRepresentation.getRealm()).thenReturn(REALM_NAME); + when(keycloak.realm(REALM_NAME)).thenReturn(realmResource); + when(realmResource.users()).thenReturn(usersResource); + when(usersResource.userProfile()).thenReturn(userProfileResource); + } + + @Test + void getUserProfileConfigshouldReturnKeycloakConfiguration() { + when(userProfileResource.getConfiguration()).thenReturn(userProfileConfig); + + var config = remoteService.getUserProfileConfig(realmRepresentation); + + assertThat(config).isSameAs(userProfileConfig); + } + + @Test + void updateUserProfileConfigshouldCallKeycloakUpdate() { + remoteService.updateUserProfileConfig(realmRepresentation, userProfileConfig); + + verify(userProfileResource).update(userProfileConfig); + } + } } diff --git a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java index 872e9bb..0513da5 100644 --- a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/realm/KeycloakRealmServiceTest.java @@ -23,6 +23,7 @@ */ package de.ozgcloud.operator.keycloak.realm; +import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @@ -33,6 +34,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.userprofile.config.UPConfig; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; @@ -80,6 +82,7 @@ class KeycloakRealmServiceTest { void shouldCallUpdateRealmIfAlreadyExists() { var existingRealm = RealmRepresentationTestFactory.create(); when(keycloakGenericRemoteService.getRealmRepresentation(REALM_NAME)).thenReturn(Optional.of(existingRealm)); + doNothing().when(service).updateUserProfileConfig(any()); service.createOrUpdateRealm(REALM, REALM_NAME); @@ -101,6 +104,7 @@ class KeycloakRealmServiceTest { @BeforeEach void init() { when(mapper.update(any(), any())).thenReturn(realmRepresentation); + when(remoteService.getUserProfileConfig(realmRepresentation)).thenReturn(new UPConfig()); } @Test @@ -126,6 +130,12 @@ class KeycloakRealmServiceTest { verify(service).addOrUpdateRealmRoles(REALM, realmRepresentation.getRealm()); } + @Test + void createRealmShouldCallAddAttributes() { + service.updateRealm(realmRepresentation, REALM); + + verify(service).addUserProfileAttributes(realmRepresentation); + } } @DisplayName("Add or Update Realm Roles") @@ -170,6 +180,7 @@ class KeycloakRealmServiceTest { @BeforeEach void init() { when(mapper.map(REALM)).thenReturn(realmRepresentation); + when(remoteService.getUserProfileConfig(realmRepresentation)).thenReturn(new UPConfig()); } @Test @@ -197,6 +208,7 @@ class KeycloakRealmServiceTest { @Test void shouldNOTCallCreateRealmIfAlreadyExists() { + reset(remoteService); when(keycloakGenericRemoteService.realmExists(REALM_NAME)).thenReturn(true); service.createRealm(REALM, REALM_NAME); @@ -212,6 +224,13 @@ class KeycloakRealmServiceTest { verify(service).addRealmName(realmRepresentation, REALM_NAME); } + + @Test + void updateRealmShouldCallAddAttributes() { + service.createRealm(REALM, REALM_NAME); + + verify(service).addUserProfileAttributes(realmRepresentation); + } } @Test @@ -232,6 +251,7 @@ class KeycloakRealmServiceTest { verify(remoteService).deleteRealm(REALM_NAME); } } + @Nested class TestRealmExists { @@ -242,5 +262,71 @@ class KeycloakRealmServiceTest { verify(keycloakGenericRemoteService).realmExists(REALM_NAME); } } - + + @Nested + class TestAddUserProfileAttributes { + + private UPConfig userProfileConfig; + + @BeforeEach + void init() { + userProfileConfig = new UPConfig(); + when(remoteService.getUserProfileConfig(realmRepresentation)).thenReturn(userProfileConfig); + } + + @Test + void shouldCallRemoteServiceGetUserProfileConfig() { + service.addUserProfileAttributes(realmRepresentation); + + verify(remoteService).getUserProfileConfig(realmRepresentation); + } + + @Test + void shouldCallRemoteServiceUpdateUserProfileConfig() { + service.addUserProfileAttributes(realmRepresentation); + + verify(remoteService).updateUserProfileConfig(realmRepresentation, userProfileConfig); + } + + @Test + void shouldCallUpdateUserProfileConfig() { + service.addUserProfileAttributes(realmRepresentation); + + verify(service).updateUserProfileConfig(any(UPConfig.class)); + } + } + + @Nested + class TestUpdateUserProfileConfig { + + @Test + void shouldAddUserProfileAttribute() { + var userProfileConfig = new UPConfig(); + + service.updateUserProfileConfig(userProfileConfig); + + assertThat(userProfileConfig.getAttributes()).hasSize(1); + } + + @Test + void shouldAddUserProfileAttributeOzgCloud() { + var userProfileConfig = new UPConfig(); + + service.updateUserProfileConfig(userProfileConfig); + + var attributeName = userProfileConfig.getAttributes().get(0).getName(); + assertThat(attributeName).isEqualTo("ozgCloudUserId"); + } + + @Test + void shouldSetPermissions() { + var userProfileConfig = new UPConfig(); + + service.updateUserProfileConfig(userProfileConfig); + + var permissions = userProfileConfig.getAttributes().get(0).getPermissions(); + assertThat(permissions.getEdit()).contains("admin", "user"); + assertThat(permissions.getView()).contains("admin", "user"); + } + } } -- GitLab