diff --git a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java index c8343f80700a4d031455c212a3e7ce5a6896378f..57041b278e6b1eff5afd42fab398d061bffc2663 100644 --- a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java @@ -26,6 +26,7 @@ package de.ozgcloud.operator.keycloak.user; import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.keycloak.representations.idm.UserRepresentation; import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; @@ -42,14 +43,29 @@ class KeycloakUserService { public void createOrUpdateUser(OzgCloudKeycloakUserSpec userSpec, String namespace) { + createAndSetUserPasswordIfNotExists(userSpec, namespace); + + remoteService.getUserByName(userSpec.getKeycloakUser().getUsername(), namespace) + .ifPresentOrElse(existingUser -> updateUser(userSpec, namespace, existingUser), + () -> createUser(userSpec, namespace)); + } + + void createAndSetUserPasswordIfNotExists(OzgCloudKeycloakUserSpec userSpec, String namespace) { if (userHasNoPassword(userSpec, namespace)) { var secret = userSecretService.getOrCreateClusterSecret(userSpec, namespace); userSpec.getKeycloakUser().setPassword(userSecretService.getPasswordFromSecret(secret)); } + } - remoteService.getUserByName(userSpec.getKeycloakUser().getUsername(), namespace) - .ifPresentOrElse(existingUser -> remoteService.updateUser(userMapper.update(existingUser, userSpec), namespace), - () -> remoteService.createUser(userMapper.map(userSpec), namespace)); + void createUser(OzgCloudKeycloakUserSpec userSpec, String namespace) { + remoteService.createUser(userMapper.map(userSpec), namespace); + } + + void updateUser(OzgCloudKeycloakUserSpec userSpec, String namespace, UserRepresentation existingUser) { + if (!userSpec.isUpdateUser()) { + return; + } + remoteService.updateUser(userMapper.update(existingUser, userSpec), namespace); } boolean userHasNoPassword(OzgCloudKeycloakUserSpec userSpec, String namespace) { diff --git a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java index f6cf222c1c71eae502b81bc983fad6cd0249e559..c7b9f29c00f9fb35fb388367f866b5e11643d5ee 100644 --- a/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java +++ b/ozgcloud-keycloak-operator/src/main/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpec.java @@ -48,6 +48,9 @@ class OzgCloudKeycloakUserSpec { @JsonProperty("keep_after_delete") private boolean keepAfterDelete; + @JsonProperty("updateUser") + private boolean updateUser; + @JsonProperty("keycloak_user") private KeycloakUserSpecUser keycloakUser; diff --git a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java index b43f2a316927c57f42800c83a5240f4f34a6044d..fb6cfcdea02ce6af9173de162ee5cd28dcc668c6 100644 --- a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakLivelTest.java @@ -23,8 +23,12 @@ */ package de.ozgcloud.operator.keycloak.user; +import static org.assertj.core.api.Assertions.*; + +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.jupiter.api.Disabled; @@ -35,13 +39,22 @@ import org.keycloak.representations.idm.UserRepresentation; import org.mockito.InjectMocks; import org.mockito.Spy; +import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService; + @Disabled("Should only be used manually") class KeycloakLivelTest { + private static final String TESTNAMESPACE = "by-torsten-dev"; + private static final String TESTUSERNAME = "hans"; + @Spy @InjectMocks private KeycloakUserRemoteService userRemoteService; + @Spy + @InjectMocks + private KeycloakGenericRemoteService keycloakGenericRemoteService; + @Test void testReal() throws IllegalAccessException { Keycloak kc = KeycloakBuilder.builder() @@ -56,15 +69,26 @@ class KeycloakLivelTest { FieldUtils.writeField(userRemoteService, "keycloak", kc, true); // when(keycloakClient.getKeycloak()).thenReturn(kc); - userRemoteService.createUser(createUser(), "by-torsten-ozgcloud-keycloak-operator-dev"); +// userRemoteService.createUser(createUser(), "by-torsten-dev"); + + Optional<UserRepresentation> user = userRemoteService.getUserByName(TESTUSERNAME, TESTNAMESPACE); + assertThat(user).isPresent(); + UserRepresentation u = user.get(); + u.setCredentials(Collections.emptyList()); + u.setEmail("updated-hans@glueck.local"); + userRemoteService.updateUser(u, TESTNAMESPACE); } private UserRepresentation createUser() { UserRepresentation u = new UserRepresentation(); - u.setUsername("hans"); + u.setUsername(TESTUSERNAME); u.setGroups(List.of("Bauamt")); u.setClientRoles(Map.of("alfa", List.of("VERWALTUNG_USER"))); + u.setEnabled(true); + u.setEmail("hans@glueck.local1"); + u.setClientRoles(Collections.emptyMap()); +// u.setCredentials(); return u; } } diff --git a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java index 827bbb9eff01c698e1b5f9d557c12bedce12f11f..75ced4033b010aedb21b69504b68a27ef76c3163 100644 --- a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java @@ -39,7 +39,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.Spy; import io.fabric8.kubernetes.api.model.Secret; @@ -72,35 +71,58 @@ class KeycloakUserServiceTest { @Captor private ArgumentCaptor<OzgCloudKeycloakUserSpec> ozgCloudKeycloakUserSpecCaptor; - private final OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + @Test + void shouldCallCreateAndSetUserPasswordIfNotExists() { + OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); - @DisplayName("user has no password") - @Nested - class TestOnUserHasNoPassword { + service.createOrUpdateUser(userSpec, TEST_NAMESPACE); - @BeforeEach - void mock() { - doReturn(true).when(service).userHasNoPassword(any(), eq(TEST_NAMESPACE)); - } + verify(service).createAndSetUserPasswordIfNotExists(userSpec, TEST_NAMESPACE); + } - @Test - void shouldGetOrCreateClusterSecret() { - service.createOrUpdateUser(userSpec, TEST_NAMESPACE); + @Test + void shouldCallUserRemoteServiceGetUserByName() { + OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); - verify(userSecretService).getOrCreateClusterSecret(userSpec, TEST_NAMESPACE); - } + service.createOrUpdateUser(userSpec, TEST_NAMESPACE); - @Test - void shouldUpdateUserPassword() { - var userWithoutPassword = OzgCloudKeycloakUserSpecTestFactory.createBuilder() - .keycloakUser(KeycloakUserSpecUserTestFactory.createBuiler().password(StringUtils.EMPTY).build()).build(); - when(userSecretService.getPasswordFromSecret(any())).thenReturn(KeycloakUserSpecUserTestFactory.PASSWORD); + verify(remoteService).getUserByName(KeycloakUserSpecUserTestFactory.USERNAME, TEST_NAMESPACE); + } - service.createOrUpdateUser(userWithoutPassword, TEST_NAMESPACE); + @Test + void shouldCallCreateUserIfNotExists() { + var userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + when(remoteService.getUserByName(KeycloakUserSpecUserTestFactory.USERNAME, TEST_NAMESPACE)) + .thenReturn(Optional.empty()); - verify(userMapper).map(ozgCloudKeycloakUserSpecCaptor.capture()); - assertThat(ozgCloudKeycloakUserSpecCaptor.getValue().getKeycloakUser().getPassword()).isEqualTo(KeycloakUserSpecUserTestFactory.PASSWORD); - } + service.createOrUpdateUser(userSpec, TEST_NAMESPACE); + + verify(service).createUser(userSpec, TEST_NAMESPACE); + } + + @Test + void shouldCallUpdateUserIfAlreadyExists() { + var userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + var remoteUser = UserRepresentationTestFactory.create(); + when(remoteService.getUserByName(KeycloakUserSpecUserTestFactory.USERNAME, TEST_NAMESPACE)) + .thenReturn(Optional.of(remoteUser)); + + service.createOrUpdateUser(userSpec, TEST_NAMESPACE); + + verify(service).updateUser(userSpec, TEST_NAMESPACE, remoteUser); + } + } + + @Nested + class TestCreateAndSetUserPasswordIfNotExists { + + @Test + void shouldCallUserHasNoPassword() { + OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + + service.createAndSetUserPasswordIfNotExists(userSpec, TEST_NAMESPACE); + + verify(service).userHasNoPassword(userSpec, TEST_NAMESPACE); } @DisplayName("on user has password") @@ -114,57 +136,54 @@ class KeycloakUserServiceTest { @Test void shouldNotReadSecretFromCluster() { - service.createOrUpdateUser(userSpec, TEST_NAMESPACE); + OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + + service.createAndSetUserPasswordIfNotExists(userSpec, TEST_NAMESPACE); - verify(userSecretService, never()).create(userSpec, TEST_NAMESPACE); + verify(userSecretService, never()).getOrCreateClusterSecret(userSpec, TEST_NAMESPACE); } - } - @Test - void shouldCallUserHasNoPassword() { - service.createOrUpdateUser(userSpec, TEST_NAMESPACE); + @Test + void shouldNotCallUserSecretServiceGetOrCreateClusterSecret() { + OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); - verify(service).userHasNoPassword(userSpec, TEST_NAMESPACE); - } + service.createAndSetUserPasswordIfNotExists(userSpec, TEST_NAMESPACE); - @Test - void shouldCallUserMapper() { - service.createOrUpdateUser(userSpec, TEST_NAMESPACE); - - verify(userMapper).map(userSpec); + verify(userSecretService, never()).getPasswordFromSecret(any(Secret.class)); + } } - @Test - void shouldCallUserRemoteServiceGetUserByName() { - var userRepresentation = UserRepresentationTestFactory.create(); - - service.createOrUpdateUser(userSpec, TEST_NAMESPACE); + @DisplayName("user has no password") + @Nested + class TestOnUserHasNoPassword { - verify(remoteService).getUserByName(userRepresentation.getUsername(), TEST_NAMESPACE); - } + @BeforeEach + void mock() { + doReturn(true).when(service).userHasNoPassword(any(), eq(TEST_NAMESPACE)); + } - @Test - void shouldCreateUserIfNotExists() { - when(remoteService.getUserByName(KeycloakUserSpecUserTestFactory.USERNAME, TEST_NAMESPACE)) - .thenReturn(Optional.empty()); - when(userMapper.map(any())).thenReturn(Mockito.mock(UserRepresentation.class)); + @Test + void shouldGetOrCreateClusterSecret() { + OzgCloudKeycloakUserSpec userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); - service.createOrUpdateUser(OzgCloudKeycloakUserSpecTestFactory.create(), TEST_NAMESPACE); + service.createAndSetUserPasswordIfNotExists(userSpec, TEST_NAMESPACE); - verify(remoteService).createUser(any(UserRepresentation.class), eq(TEST_NAMESPACE)); - } + verify(userSecretService).getOrCreateClusterSecret(userSpec, TEST_NAMESPACE); + } - @Test - void shouldUpdateUserIfExists() { - var userRepresentation = Mockito.mock(UserRepresentation.class); - when(remoteService.getUserByName(KeycloakUserSpecUserTestFactory.USERNAME, TEST_NAMESPACE)) - .thenReturn(Optional.of(userRepresentation)); - when(userMapper.update(eq(userRepresentation), any())).thenReturn(userRepresentation); + @Test + void shouldUpdateUserPassword() { + String NEWPASSWORD = "NewPassword"; + var userWithoutPassword = OzgCloudKeycloakUserSpecTestFactory.createBuilder() + .keycloakUser(KeycloakUserSpecUserTestFactory.createBuiler().password(StringUtils.EMPTY).build()).build(); + when(userSecretService.getPasswordFromSecret(any())).thenReturn(NEWPASSWORD); - service.createOrUpdateUser(OzgCloudKeycloakUserSpecTestFactory.create(), TEST_NAMESPACE); + service.createAndSetUserPasswordIfNotExists(userWithoutPassword, TEST_NAMESPACE); - verify(remoteService).updateUser(userRepresentation, TEST_NAMESPACE); + assertThat(userWithoutPassword.getKeycloakUser().getPassword()).isEqualTo(NEWPASSWORD); + } } + } @DisplayName("Test user has no password") @@ -197,6 +216,46 @@ class KeycloakUserServiceTest { } } + @Nested + class TestCreateUser { + + @Test + void shouldCallRemoteServiceCreateUser() { + var userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + UserRepresentation userRepresentation = UserRepresentationTestFactory.create(); + when(userMapper.map(userSpec)).thenReturn(userRepresentation); + + service.createUser(userSpec, TEST_NAMESPACE); + + verify(remoteService).createUser(userRepresentation, TEST_NAMESPACE); + } + } + + @Nested + class TestUpdateUser { + + @Test + void shouldUpdateUserIfEnabled() { + var userSpec = OzgCloudKeycloakUserSpecTestFactory.create(); + UserRepresentation userRepresentation = UserRepresentationTestFactory.create(); + when(userMapper.update(userRepresentation, userSpec)).thenReturn(userRepresentation); + + service.updateUser(userSpec, TEST_NAMESPACE, userRepresentation); + + verify(remoteService).updateUser(any(UserRepresentation.class), eq(TEST_NAMESPACE)); + } + + @Test + void shouldNotUpdateUserIfDisabled() { + var userSpec = OzgCloudKeycloakUserSpecTestFactory.createBuilder() + .updateUser(false).build(); + + service.updateUser(userSpec, TEST_NAMESPACE, UserRepresentationTestFactory.create()); + + verify(remoteService, never()).updateUser(null, TEST_NAMESPACE); + } + } + @Nested class TestDeleteUser { diff --git a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java index a76206d878e3c9a3e240b47f037990787dca16de..2418ed24fb2edc226edea0c03502d844bbb68ef4 100644 --- a/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java +++ b/ozgcloud-keycloak-operator/src/test/java/de/ozgcloud/operator/keycloak/user/OzgCloudKeycloakUserSpecTestFactory.java @@ -27,12 +27,15 @@ import de.ozgcloud.operator.keycloak.user.OzgCloudKeycloakUserSpec.OzgCloudKeycl public class OzgCloudKeycloakUserSpecTestFactory { + public static final boolean UPDATE_USER = true; + public static OzgCloudKeycloakUserSpec create() { return createBuilder().build(); } public static OzgCloudKeycloakUserSpecBuilder createBuilder() { return OzgCloudKeycloakUserSpec.builder() + .updateUser(UPDATE_USER) .keycloakUser(KeycloakUserSpecUserTestFactory.create()); } }