From f9f81fde7bce7d69bc759f1da3171baa476f192b Mon Sep 17 00:00:00 2001
From: OZGCloud <ozgcloud@mgm-tp.com>
Date: Fri, 21 Jul 2023 08:34:03 +0200
Subject: [PATCH] OZG-3961 OZG-4082 create Secret for User if password not
 exists(PoC)

---
 .../client/KeycloakClientService.java         | 14 +++---
 .../user/KeycloakUserRemoteService.java       | 47 +++++++++++++++++++
 .../keycloak/user/KeycloakUserService.java    | 12 +++++
 .../user/KeycloakUserRemoteServiceTest.java   |  3 ++
 .../user/KeycloakUserServiceTest.java         | 13 +++++
 .../user/OzgKeycloakUserSpecTestFactory.java  |  2 +-
 6 files changed, 83 insertions(+), 8 deletions(-)

diff --git a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java b/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java
index 4357803..a059195 100644
--- a/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java
+++ b/src/main/java/de/ozgcloud/operator/keycloak/client/KeycloakClientService.java
@@ -47,13 +47,6 @@ class KeycloakClientService {
 						() -> createClient(spec, namespace));
 	}
 
-	void createClient(OzgKeycloakClientSpec spec, String realm) {
-		ClientRepresentation clientRepresentation = mapper.map(spec);
-		String realClientId = remoteService.createClient(clientRepresentation, realm);
-
-		addOrUpdateClientRoles(spec, realm, realClientId);
-	}
-
 	void updateClient(ClientRepresentation existingClient, OzgKeycloakClientSpec spec, String realm) {
 		ClientRepresentation clientRepresentation = mapper.update(existingClient, spec);
 		remoteService.updateClient(clientRepresentation, realm);
@@ -61,6 +54,13 @@ class KeycloakClientService {
 		addOrUpdateClientRoles(spec, realm, existingClient.getId());
 	}
 
+	void createClient(OzgKeycloakClientSpec spec, String realm) {
+		ClientRepresentation clientRepresentation = mapper.map(spec);
+		String realClientId = remoteService.createClient(clientRepresentation, realm);
+
+		addOrUpdateClientRoles(spec, realm, realClientId);
+	}
+
 	void addOrUpdateClientRoles(OzgKeycloakClientSpec spec, String realm, String realClientId) {
 		spec.getClientRoles().forEach(
 				roleSpec -> genericRemoteService.getClientRole(roleSpec.getName(), realClientId, realm)
diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java
index 4e1d2f0..4430bc2 100644
--- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java
+++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteService.java
@@ -24,7 +24,10 @@
 package de.ozgcloud.operator.keycloak.user;
 
 import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.logging.Level;
 
 import org.keycloak.admin.client.CreatedResponseUtil;
 import org.keycloak.admin.client.Keycloak;
@@ -38,12 +41,22 @@ import org.springframework.stereotype.Component;
 import de.ozgcloud.operator.keycloak.KeycloakException;
 import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService;
 import de.ozgcloud.operator.keycloak.KeycloakResultParser;
+import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.api.model.SecretBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.dsl.Resource;
+import lombok.extern.java.Log;
 
+@Log
 @Component
 class KeycloakUserRemoteService {
 
+	private static final String SECRET_PASSWORD_FIELD = "password";
+
 	@Autowired
 	private Keycloak keycloak;
+	@Autowired
+	private KubernetesClient kubernetesClient;
 
 	@Autowired
 	private KeycloakGenericRemoteService keycloakGenericRemoteService;
@@ -99,4 +112,38 @@ class KeycloakUserRemoteService {
 	void addClientRoleToUser(RoleRepresentation clientRole, RealmResource realmResource, String userId, ClientRepresentation appClient) {
 		realmResource.users().get(userId).roles().clientLevel(appClient.getId()).add(Arrays.asList(clientRole));
 	}
+
+	// PoC
+	public String createSecret(OzgKeycloakUserSpec userSpec, String namespace) {
+		log.log(Level.INFO, "Create secret for user...");
+		var secretName = userSpec.getKeycloakUser().getUsername() + "-credentials";
+
+		var secret = getSecret(secretName, namespace);
+		if (Objects.isNull(secret.get())) {
+			log.log(Level.INFO, "...secret does not exist, create one...");
+			kubernetesClient.secrets().inNamespace(namespace).create(buildSecret());
+			log.log(Level.INFO, "...secret created in " + namespace + " for user " + userSpec.getKeycloakUser().getUsername());
+			log.log(Level.INFO, "...load created secret...");
+			var createdSecret = getSecret(secretName, namespace);
+			var newPassword = getPassword(createdSecret);
+			log.log(Level.INFO, "return password from created secret:" + newPassword);
+			return newPassword;
+		}
+		var password = getPassword(secret);
+		log.log(Level.INFO, "secret exists, return password:" + password);
+		return password;
+	}
+
+	private Resource<Secret> getSecret(String secretName, String namespace) {
+		return kubernetesClient.secrets().inNamespace(namespace).withName(secretName);
+	}
+
+	private Secret buildSecret() {
+		return new SecretBuilder().withData(Map.of(SECRET_PASSWORD_FIELD, "Y9nk43yrQ_zzIPpfFU-I")).build();
+	}
+
+	private String getPassword(Resource<Secret> secret) {
+		return secret.get().getData().get(SECRET_PASSWORD_FIELD);
+	}
+	//
 }
\ No newline at end of file
diff --git a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java
index 1f33da6..a66d409 100644
--- a/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java
+++ b/src/main/java/de/ozgcloud/operator/keycloak/user/KeycloakUserService.java
@@ -24,10 +24,15 @@
 package de.ozgcloud.operator.keycloak.user;
 
 import java.util.Optional;
+import java.util.logging.Level;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
 
+import lombok.extern.java.Log;
+
+@Log
 @Component
 class KeycloakUserService {
 
@@ -38,6 +43,13 @@ class KeycloakUserService {
 	private KeycloakUserMapper userMapper;
 
 	public void createOrUpdateUser(OzgKeycloakUserSpec userSpec, String namespace) {
+		if (!StringUtils.hasLength(userSpec.getKeycloakUser().getPassword())) {
+			log.log(Level.INFO, "User has no password, create secret...");
+			var password = remoteService.createSecret(userSpec, namespace);
+			log.log(Level.INFO, "set password: " + password + " to user...");
+			userSpec.getKeycloakUser().setPassword(password);
+		}
+		log.log(Level.INFO, "proceed");
 		remoteService.getUserByName(userSpec.getKeycloakUser().getUsername(), namespace)
 				.ifPresentOrElse(existingUser -> remoteService.updateUser(userMapper.update(existingUser, userSpec), namespace),
 						() -> remoteService.createUser(userMapper.map(userSpec), namespace));
diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java
index 4f3c6c2..698c7d7 100644
--- a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java
+++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserRemoteServiceTest.java
@@ -54,6 +54,7 @@ import org.mockito.Spy;
 
 import de.ozgcloud.operator.keycloak.KeycloakException;
 import de.ozgcloud.operator.keycloak.KeycloakGenericRemoteService;
+import io.fabric8.kubernetes.client.KubernetesClient;
 
 class KeycloakUserRemoteServiceTest {
 
@@ -91,6 +92,8 @@ class KeycloakUserRemoteServiceTest {
 	private RoleMappingResource roleMappingResource;
 	@Mock
 	private UserRepresentation userRepresentation;
+	@Mock
+	private KubernetesClient kubernetesClient;
 
 	@Nested
 	class TestCreateUser {
diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java
index f55c50d..3d3d0a6 100644
--- a/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java
+++ b/src/test/java/de/ozgcloud/operator/keycloak/user/KeycloakUserServiceTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.*;
 
 import java.util.Optional;
 
+import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.keycloak.representations.idm.UserRepresentation;
@@ -71,6 +72,18 @@ class KeycloakUserServiceTest {
 			verify(userRemoteService).getUserByName(eq(userRepresentation.getUsername()), eq(TEST_NAMESPACE));
 		}
 
+		@Test
+		void shouldCreateSecretIfPasswordIsNotSet() {
+			when(userRemoteService.createSecret(any(), any())).thenReturn("TestPassword");
+
+			var testUser = OzgKeycloakUserSpecTestFactory.createBuilder()
+					.keycloakUser(KeycloakUserSpecUserTestFactory.createBuiler().password(StringUtils.EMPTY).build()).build();
+
+			userService.createOrUpdateUser(testUser, TEST_NAMESPACE);
+
+			verify(userRemoteService).createSecret(testUser, TEST_NAMESPACE);
+		}
+
 		@Test
 		void shouldCreateUserIfNotExists() {
 			when(userRemoteService.getUserByName(OzgKeycloakUserSpecTestFactory.KEYCLOAK_USER.getUsername(), TEST_NAMESPACE))
diff --git a/src/test/java/de/ozgcloud/operator/keycloak/user/OzgKeycloakUserSpecTestFactory.java b/src/test/java/de/ozgcloud/operator/keycloak/user/OzgKeycloakUserSpecTestFactory.java
index 1bf34b4..94ef24b 100644
--- a/src/test/java/de/ozgcloud/operator/keycloak/user/OzgKeycloakUserSpecTestFactory.java
+++ b/src/test/java/de/ozgcloud/operator/keycloak/user/OzgKeycloakUserSpecTestFactory.java
@@ -34,7 +34,7 @@ class OzgKeycloakUserSpecTestFactory {
 		return createBuilder().build();
 	}
 
-	private static OzgKeycloakUserSpecBuilder createBuilder() {
+	public static OzgKeycloakUserSpecBuilder createBuilder() {
 		return OzgKeycloakUserSpec.builder()
 				.keycloakUser(KEYCLOAK_USER);
 	}
-- 
GitLab