diff --git a/pom.xml b/pom.xml index 04d90dcfbdd02e2a5ee0bd6d73f8f9acc46f6b84..35173b6144238836713452915acf8a247cc02014 100644 --- a/pom.xml +++ b/pom.xml @@ -2,21 +2,23 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> + <parent> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-parent</artifactId> - <version>3.1.0</version> + <groupId>de.itvsh.kop.common</groupId> + <artifactId>kop-common-parent</artifactId> + <version>2.1.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> + + + <groupId>de.ozgcloud</groupId> <artifactId>ozg-operator</artifactId> <version>1.0.0-SNAPSHOT</version> <name>OZG Cloud Operator</name> <description>OZG Cloud Operator</description> <properties> - <java.version>17</java.version> <operator-sdk.version>5.0.0</operator-sdk.version> -<!-- <operator-sdk.version>4.2.8</operator-sdk.version>--> <spring-boot.build-image.imageName>docker.ozg-sh.de/ozg-operator:build-latest</spring-boot.build-image.imageName> </properties> <dependencies> @@ -24,17 +26,14 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.springframework.boot</groupId>--> -<!-- <artifactId>spring-boot-starter-web-services</artifactId>--> -<!-- </dependency>--> - -<!-- https://mvnrepository.com/artifact/org.keycloak/keycloak-admin-client --> -<dependency> - <groupId>org.keycloak</groupId> - <artifactId>keycloak-admin-client</artifactId> - <version>21.1.1</version> -</dependency> + <dependency> + <groupId>org.keycloak</groupId> + <artifactId>keycloak-admin-client</artifactId> + </dependency> + <dependency> + <groupId>org.mapstruct</groupId> + <artifactId>mapstruct</artifactId> + </dependency> <dependency> @@ -46,41 +45,16 @@ <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.apache.logging.log4j</groupId>--> -<!-- <artifactId>log4j-core</artifactId>--> -<!-- </dependency>--> -<!-- https://mvnrepository.com/artifact/org.reflections/reflections --> <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.10.2</version> </dependency> -<!-- <dependency>--> -<!-- <groupId>org.springframework.boot</groupId>--> -<!-- <artifactId>spring-boot-starter-actuator</artifactId>--> -<!-- </dependency>--> - - -<!-- <dependency>--> -<!-- <groupId>io.fabric8</groupId>--> -<!-- <artifactId>crd-generator-apt</artifactId>--> -<!-- <scope>provided</scope>--> -<!-- <version>6.7.1</version>--> -<!-- </dependency>--> - - <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> -<!-- <dependency>--> -<!-- <groupId>io.javaoperatorsdk</groupId>--> -<!-- <artifactId>operator-framework-spring-boot-starter-test</artifactId>--> -<!-- <version>${operator-sdk.version}</version>--> -<!-- <scope>test</scope>--> -<!-- </dependency>--> </dependencies> <build> diff --git a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java b/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java index cc9f184bc83ab0c0e86895ad748cf8ebd52f5f82..1797ee6b4fd0b6279c16af85e1ab49a2d03fe339 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java +++ b/src/main/java/de/ozgcloud/operator/keycloak/KeycloakClient.java @@ -1,33 +1,49 @@ package de.ozgcloud.operator.keycloak; +import java.util.Base64; + import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.KeycloakBuilder; -import org.keycloak.representations.idm.UserRepresentation; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.Resource; import lombok.extern.java.Log; @Log @Component public class KeycloakClient { + @Autowired + private KubernetesClient kubernetesClient; + public Keycloak getKeycloak() { - Keycloak keycloak = KeycloakBuilder.builder() // - .serverUrl("https://sso.dev.by.ozg-cloud.de/") // - .realm("master") // -// .grantType(OAuth2Constants.PASSWORD) // - .username("admin") // - .password(" ") // + return KeycloakBuilder.builder() + .serverUrl("http://keycloak-keycloakx-http.keycloak") + .realm("master") + .username("admin") + .password(getKeycloakAdminPassword()) .clientId("admin-cli") .build(); + } - log.info(keycloak.realms().findAll().toString()); + String getKeycloakAdminPassword() { + return decodeBase64(getKeycloakRealmAdminSecret() + .get() + .getData() + .get("password")); + } - UserRepresentation user = new UserRepresentation(); - user.setUsername("helge"); - keycloak.realm("by-torsten-ozg-operator-dev").users().create(user); + Resource<Secret> getKeycloakRealmAdminSecret() { + return kubernetesClient.secrets() + .inNamespace("keycloak") + .withName("keycloak-admin-secret"); + } - return keycloak; + String decodeBase64(String base64String) { + return new String(Base64.getDecoder().decode(base64String)); } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java b/src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java new file mode 100644 index 0000000000000000000000000000000000000000..d95cccb90dbf1d78a799bdcaa07b9e9cebc42525 --- /dev/null +++ b/src/main/java/de/ozgcloud/operator/keycloak/KeycloakException.java @@ -0,0 +1,9 @@ +package de.ozgcloud.operator.keycloak; + +@SuppressWarnings("serial") +public class KeycloakException extends RuntimeException { + + public KeycloakException(String string) { + super(string); + } +} diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserRemoteService.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserRemoteService.java deleted file mode 100644 index 63ce46fe8969c8cea44a95dfab2dd76e449edff1..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserRemoteService.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.ozgcloud.operator.keycloak.user; - -import org.keycloak.representations.account.UserRepresentation; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import de.ozgcloud.operator.keycloak.KeycloakClient; - -@Component -public class KeycloakClientUserRemoteService { - - @Autowired - private KeycloakClient keycloakClient; - - void createUser(UserRepresentation user) { - - keycloakClient.getKeycloak(); - } -} diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserService.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserService.java deleted file mode 100644 index a87f0edb5edb01681e9b50182868747b93877d23..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserService.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.ozgcloud.operator.keycloak.user; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class KeycloakClientUserService { - - @Autowired - KeycloakClientUserRemoteService remoteService; - - void addUser(KeycloakUserSpec userSpec) { - - } -} diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..3d855fa0fb413930eb5ee80d134430e74f878c6c --- /dev/null +++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapper.java @@ -0,0 +1,37 @@ +package de.ozgcloud.operator.keycloak.user; + +import org.keycloak.representations.idm.UserRepresentation; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper +interface KeycloakUserMapper { + + @Mapping(target = "access", ignore = true) + @Mapping(target = "attributes", ignore = true) + @Mapping(target = "clientConsents", ignore = true) + @Mapping(target = "clientRoles", ignore = true) + @Mapping(target = "createdTimestamp", ignore = true) + @Mapping(target = "credentials", ignore = true) + @Mapping(target = "disableableCredentialTypes", ignore = true) + @Mapping(target = "email", ignore = true) + @Mapping(target = "emailVerified", ignore = true) + @Mapping(target = "enabled", ignore = true) + @Mapping(target = "federatedIdentities", ignore = true) + @Mapping(target = "federationLink", ignore = true) + @Mapping(target = "groups", ignore = true) + @Mapping(target = "id", ignore = true) + @Mapping(target = "notBefore", ignore = true) + @Mapping(target = "origin", ignore = true) + @Mapping(target = "realmRoles", ignore = true) + @Mapping(target = "requiredActions", ignore = true) + @Mapping(target = "self", ignore = true) + @Mapping(target = "serviceAccountClientId", ignore = true) + @Mapping(target = "socialLinks", ignore = true) + @Mapping(target = "totp", ignore = true) + @Mapping(target = "applicationRoles", ignore = true) + @Mapping(target = "username", source = "keycloakUser.username") + @Mapping(target = "firstName", source = "keycloakUser.firstName") + @Mapping(target = "lastName", source = "keycloakUser.lastName") + UserRepresentation toUserRepresentation(KeycloakUserSpec user); +} diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java index 3aff0f6c6213f426cabf24704025a89a913ff612..a9a8672dda044a3b26fef91868c2736a14c64144 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java +++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconciler.java @@ -1,11 +1,9 @@ package de.ozgcloud.operator.keycloak.user; -import java.time.LocalDate; -import java.util.Base64; +import java.util.logging.Level; + +import org.springframework.beans.factory.annotation.Autowired; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.Resource; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; @@ -13,50 +11,30 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import lombok.extern.java.Log; @ControllerConfiguration -//@Log4j2 @Log public class KeycloakUserReconciler implements Reconciler<KeycloakUser> { - private final KubernetesClient kubernetesClient; + public static final String STATUS_OK = "OK"; + public static final String STATUS_ERROR = "ERROR"; - public KeycloakUserReconciler(KubernetesClient kubernetesClient) { - this.kubernetesClient = kubernetesClient; - } + @Autowired + private KeycloakUserService keycloakUserService; @Override - public UpdateControl<KeycloakUser> reconcile(KeycloakUser crd, Context<KeycloakUser> context) throws Exception { - -// String name = crd.getMetadata().getName(); - String name = crd.getSpec().getKeycloakUser().getUsername(); - String namespace = crd.getMetadata().getNamespace(); - log.warning("Reconciling: " + name + "/" + namespace); - log.warning("SecretTest:" + getKeycloakRealmAdminPassword(namespace)); - crd.setStatus(KeycloakUserStatus.builder().status("Updated status for " + name + LocalDate.now().toString()).phase("Pending").build()); - return UpdateControl.updateStatus(crd); - } - - String getKeycloakRealmAdminPassword(String namespace) { + public UpdateControl<KeycloakUser> reconcile(KeycloakUser crd, Context<KeycloakUser> context) { - Resource<Secret> secret = getKeycloakRealmAdminSecret(namespace); + try { + String namespace = crd.getMetadata().getNamespace(); -// if (!secret.isReady()) { -// throw new RuntimeException("Secret not exists " + namespace + "-admin-credentials"); -// } + keycloakUserService.addUser(crd.getSpec(), namespace); - return decodeBase64(secret - .get() - .getData() - .get("username")); -// .toString(); - } - - Resource<Secret> getKeycloakRealmAdminSecret(String namespace) { - return kubernetesClient.secrets() - .inNamespace("keycloak") - .withName("keycloak-admin-secret"); - } + crd.setStatus(KeycloakUserStatus.builder().status(STATUS_OK).errorMessage(null).build()); + return UpdateControl.updateStatus(crd); - String decodeBase64(String base64String) { - return new String(Base64.getDecoder().decode(base64String)); + } catch (Exception e) { + log.log(Level.SEVERE, "Could not reconcile user", e); + crd.setStatus(KeycloakUserStatus.builder().status(STATUS_ERROR).errorMessage(e.getMessage()).build()); + return UpdateControl.updateStatus(crd); + } } } diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..ac5a68c6c52c1eaa7cddb9311e44f8240cfd6614 --- /dev/null +++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java @@ -0,0 +1,28 @@ +package de.ozgcloud.operator.keycloak.user; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status.Family; + +import org.keycloak.representations.idm.UserRepresentation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import de.ozgcloud.operator.keycloak.KeycloakClient; +import de.ozgcloud.operator.keycloak.KeycloakException; + +@Component +class KeycloakUserRemoteService { + + @Autowired + private KeycloakClient keycloakClient; + + void createUser(UserRepresentation user, String realm) { + + try (Response response = keycloakClient.getKeycloak().realm(realm).users().create(user)) { + + if (!response.getStatusInfo().getFamily().equals(Family.SUCCESSFUL)) { + throw new KeycloakException("Could not update user " + user.getUsername() + ";" + response.getStatusInfo()); + } + } + } +} diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java new file mode 100644 index 0000000000000000000000000000000000000000..717a1be965821a1ae23fecf8cedd125f59415d19 --- /dev/null +++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java @@ -0,0 +1,22 @@ +package de.ozgcloud.operator.keycloak.user; + +import org.keycloak.representations.idm.UserRepresentation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class KeycloakUserService { + + @Autowired + private KeycloakUserRemoteService remoteService; + + @Autowired + private KeycloakUserMapper userMapper; + + void addUser(KeycloakUserSpec userSpec, String namespace) { + + UserRepresentation keycloakUser = userMapper.toUserRepresentation(userSpec); + + remoteService.createUser(keycloakUser, namespace); + } +} diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserStatus.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserStatus.java index 7e83e84b1c343b21c7009345ecc2b8f87d0fbf0f..43a5e5f717bd773f029c26d70284ae081f6900ae 100644 --- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserStatus.java +++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserStatus.java @@ -16,5 +16,5 @@ public class KeycloakUserStatus extends ObservedGenerationAwareStatus { private String status; - private String phase; + private String errorMessage; } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserRemoteServiceTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserRemoteServiceTest.java deleted file mode 100644 index 86cd6ffbdd66d99c92465203acabe2b5a4267970..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakClientUserRemoteServiceTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.ozgcloud.operator.keycloak.user; - -import static org.mockito.Mockito.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; - -import de.ozgcloud.operator.keycloak.KeycloakClient; - -@ExtendWith(MockitoExtension.class) -class KeycloakClientUserRemoteServiceTest { - - @Spy - @InjectMocks - KeycloakClientUserRemoteService userRemoteService; - - @Mock - KeycloakClient keycloakClient; - - @Test - void test() { - - userRemoteService.createUser(null); - - verify(keycloakClient).getKeycloak(); - } -} diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6b84fdfb0c24d2e3471e7f2cb4ff851ee91f8a2b --- /dev/null +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserMapperTest.java @@ -0,0 +1,39 @@ +package de.ozgcloud.operator.keycloak.user; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; +import org.mockito.Spy; + +class KeycloakUserMapperTest { + + @Spy + private KeycloakUserMapper mapper = Mappers.getMapper(KeycloakUserMapper.class); + + @Nested + class TesttoUserRepresentation { + + @Test + void shouldMapUsername() { + var keycloakUser = mapper.toUserRepresentation(KeycloakUserSpecTestFactory.create()); + + assertThat(keycloakUser.getUsername()).isEqualTo(KeycloakUserSpecUserTestFactory.USERNAME); + } + + @Test + void shouldMapFirstname() { + var keycloakUser = mapper.toUserRepresentation(KeycloakUserSpecTestFactory.create()); + + assertThat(keycloakUser.getFirstName()).isEqualTo(KeycloakUserSpecUserTestFactory.FIRSTNAME); + } + + @Test + void shouldMapLastname() { + var keycloakUser = mapper.toUserRepresentation(KeycloakUserSpecTestFactory.create()); + + assertThat(keycloakUser.getLastName()).isEqualTo(KeycloakUserSpecUserTestFactory.LASTNAME); + } + } +} diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java index 9c66f82ec2bd241c639bdfed40d444ec28f1ed30..d8df746b6377a7d050b11710c066ea10b9467dde 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserReconcilerTest.java @@ -1,23 +1,14 @@ package de.ozgcloud.operator.keycloak.user; import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import de.ozgcloud.operator.keycloak.user.KeycloakUser; -import de.ozgcloud.operator.keycloak.user.KeycloakUserReconciler; -import io.fabric8.kubernetes.client.KubernetesClient; -import lombok.SneakyThrows; - -@Disabled class KeycloakUserReconcilerTest { @Spy @@ -25,26 +16,42 @@ class KeycloakUserReconcilerTest { private KeycloakUserReconciler conciler; @Mock - private KubernetesClient kubernetesClient; - - @Nested - class TestKeycloalUserStatus { + private KeycloakUserService userService; - @BeforeEach - void init() { - doReturn("hase").when(conciler).getKeycloakRealmAdminPassword(anyString()); + @BeforeEach + void init() { +// doReturn("hase").when(conciler).getKeycloakRealmAdminPassword(anyString()); // when(conciler.getKeycloakRealmAdminPassword(anyString())).thenReturn("hase"); - } + } + + @Test + void shouldCallServiceAddUser() { + + KeycloakUser user = KeycloakUserTestFactory.create(); + + conciler.reconcile(user, null); + + verify(userService).addUser(user.getSpec(), KeycloakUserTestFactory.METADATA_NAMESPACE); + } + + @Test + void shouldUpdateStatus() { + + KeycloakUser user = KeycloakUserTestFactory.create(); + + conciler.reconcile(user, null); + + assertThat(user.getStatus().getStatus()).isEqualTo(KeycloakUserReconciler.STATUS_OK); + } - @Test - @SneakyThrows - void shouldSetStatus() { + @Test + void shouldSetErrorStatusOnException() { - KeycloakUser user = KeycloakUserTestFactory.create(); + KeycloakUser user = KeycloakUserTestFactory.create(); + user.setMetadata(null); - conciler.reconcile(user, null); + conciler.reconcile(user, null); - assertThat(user.getStatus().getStatus()).contains("Updated"); - } + assertThat(user.getStatus().getStatus()).isEqualTo(KeycloakUserReconciler.STATUS_ERROR); } } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a9491afe52f85f715673eea5694195ceeeb71efd --- /dev/null +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java @@ -0,0 +1,74 @@ +package de.ozgcloud.operator.keycloak.user; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status.Family; +import javax.ws.rs.core.Response.StatusType; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.representations.idm.UserRepresentation; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.operator.keycloak.KeycloakClient; +import de.ozgcloud.operator.keycloak.KeycloakException; + +class KeycloakUserRemoteServiceTest { + + @Spy + @InjectMocks + private KeycloakUserRemoteService userRemoteService; + + @Mock + private KeycloakClient keycloakClient; + @Mock + private Keycloak keycloak; + @Mock + private RealmResource realmResource; + @Mock + private UsersResource usersResource; + @Mock + private Response response; + @Mock + private StatusType statusType; + + private final static UserRepresentation USER_REPRESENTATION = UserRepresentationTestFactory.create(); + private final static String REALM = "TestRealm"; + + @BeforeEach + void init() { + when(keycloakClient.getKeycloak()).thenReturn(keycloak); + when(keycloak.realm(REALM)).thenReturn(realmResource); + when(realmResource.users()).thenReturn(usersResource); + when(usersResource.create(USER_REPRESENTATION)).thenReturn(response); + when(response.getStatusInfo()).thenReturn(statusType); + when(statusType.getFamily()).thenReturn(Family.SUCCESSFUL); + } + + @Test + void shouldCallKeycloakClient() { + + when(statusType.getFamily()).thenReturn(Family.SUCCESSFUL); + + userRemoteService.createUser(USER_REPRESENTATION, REALM); + + verify(keycloakClient).getKeycloak(); + } + + @Test + void shouldThrowOnResponseError() { + + when(statusType.getFamily()).thenReturn(Family.SERVER_ERROR); + + assertThrows(KeycloakException.class, () -> + userRemoteService.createUser(USER_REPRESENTATION, REALM) + ); + } +} diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1587a8fb4c796e4f41cd2e88b6319b89c938b830 --- /dev/null +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java @@ -0,0 +1,46 @@ +package de.ozgcloud.operator.keycloak.user; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +class KeycloakUserServiceTest { + + private static final String TEST_NAMESPACE = "TestNamespace"; + + @Spy + @InjectMocks + private KeycloakUserService userService; + + @Mock + private KeycloakUserRemoteService userRemoteService; + + @Mock + private KeycloakUserMapper userMapper; + + @Test + void shouldCallUserMapper() { + + var testUser = KeycloakUserSpecTestFactory.create(); + + userService.addUser(testUser, TEST_NAMESPACE); + + verify(userMapper).toUserRepresentation(testUser); + } + + @Test + void shouldCallUserRemoteService() { + + var userRepresentation = UserRepresentationTestFactory.create(); + when(userMapper.toUserRepresentation(any())).thenReturn(userRepresentation); + + userService.addUser(KeycloakUserSpecTestFactory.create(), TEST_NAMESPACE); + + verify(userRemoteService).createUser(eq(userRepresentation), eq(TEST_NAMESPACE)); + } + +} diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java index d38867135f29d59ed7d30b369925fa18b02a15d8..6ed256d89756fc7e2d9330e8c8d8bb2f3bf98d73 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserSpecUserTestFactory.java @@ -5,6 +5,8 @@ import de.ozgcloud.operator.keycloak.user.KeycloakUserSpec.KeycloakUserSpecUser; class KeycloakUserSpecUserTestFactory { public static final String USERNAME = "dorothea"; + public static final String FIRSTNAME = "Dorothea"; + public static final String LASTNAME = "Doe"; public static KeycloakUserSpecUser create() { return createBuiler().build(); @@ -12,7 +14,8 @@ class KeycloakUserSpecUserTestFactory { public static KeycloakUserSpecUser.KeycloakUserSpecUserBuilder createBuiler() { return KeycloakUserSpecUser.builder() - .username(USERNAME); + .username(USERNAME) + .firstName(FIRSTNAME) + .lastName(LASTNAME); } - } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserTestFactory.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserTestFactory.java index fd3a9fbeafc8fe6a4791bede46cd62188f3e2013..4c71c4033236c791bd9b724b17bd0e0bf61c7ee9 100644 --- a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserTestFactory.java +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserTestFactory.java @@ -1,19 +1,20 @@ package de.ozgcloud.operator.keycloak.user; -import de.ozgcloud.operator.keycloak.user.KeycloakUser; -import de.ozgcloud.operator.keycloak.user.KeycloakUserSpec; -import de.ozgcloud.operator.keycloak.user.KeycloakUserStatus; - class KeycloakUserTestFactory { public static final KeycloakUserStatus KEYCLOAK_USER_STATUS = KeycloakUserStatusTestFactory.create(); public static final KeycloakUserSpec KEYCLOAK_USER_SPEC = KeycloakUserSpecTestFactory.create(); + public static final String METADATA_NAMESPACE = "TestNamespace"; + public static KeycloakUser create() { KeycloakUser keycloakUser = new KeycloakUser(); keycloakUser.setStatus(KEYCLOAK_USER_STATUS); keycloakUser.setSpec(KEYCLOAK_USER_SPEC); + + keycloakUser.getMetadata().setNamespace(METADATA_NAMESPACE); + return keycloakUser; } diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java b/src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..022c856db738f19ad136d71f8195effbc9f1b00e --- /dev/null +++ b/src/test/java/de/ozgcloud/operator/keycloak/user/UserRepresentationTestFactory.java @@ -0,0 +1,18 @@ +package de.ozgcloud.operator.keycloak.user; + +import org.keycloak.representations.idm.UserRepresentation; + +public class UserRepresentationTestFactory { + + public static String USERNAME = KeycloakUserSpecUserTestFactory.USERNAME; + public static String FIRSTNAME = KeycloakUserSpecUserTestFactory.FIRSTNAME; + public static String LASTNAME = KeycloakUserSpecUserTestFactory.LASTNAME; + + public static UserRepresentation create() { + UserRepresentation user = new UserRepresentation(); + user.setUsername(USERNAME); + user.setFirstName(FIRSTNAME); + user.setLastName(LASTNAME); + return user; + } +} diff --git a/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 0000000000000000000000000000000000000000..79b126e6cdb86bec1f4f08c205de8961bde1934a --- /dev/null +++ b/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +org.mockito.junit.jupiter.MockitoExtension \ No newline at end of file diff --git a/src/test/resources/junit-platform.properties b/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000000000000000000000000000000000..b059a65dc46792ea5494de02f888ab2ad08cd420 --- /dev/null +++ b/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.extensions.autodetection.enabled=true \ No newline at end of file