diff --git a/pom.xml b/pom.xml index 6613c2c83d7e87b6c8209bee04237485f5588175..4b246e0a435cb9edaae9a9260e5ec471ce330144 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ <?xml version="1.0"?> -<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"> +<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> @@ -15,7 +16,7 @@ <name>OzgCloud Operator Elster-Transfer</name> <description>OzgCloud Operator Elster-Transfer</description> - + <properties> <spring-boot.version>3.1.8</spring-boot.version> @@ -28,18 +29,19 @@ <reflections.version>0.10.2</reflections.version> <validation-api.version>2.0.1.Final</validation-api.version> <lorem.version>2.2</lorem.version> - <elster-transfer.version>elster-transfer-24.04-rest-api-docs.yaml</elster-transfer.version> + <elster-transfer.version>elster-transfer-24.04-rest-api-docs.yaml</elster-transfer.version> <!-- test --> <junit-jupiter.version>5.10.1</junit-jupiter.version> <kubernetes-server-mock.version>6.9.2</kubernetes-server-mock.version> <io.javaoperatorsdk.version>0.9.5</io.javaoperatorsdk.version> - + <!-- plugin --> <license-maven-plugin.version>4.1</license-maven-plugin.version> <ozgcloud-license.version>1.6.0</ozgcloud-license.version> - <spring-boot.build-image.imageName>docker.ozg-sh.de/ozgcloud-elster-transfer-operator:build-latest</spring-boot.build-image.imageName> - + <spring-boot.build-image.imageName>docker.ozg-sh.de/ozgcloud-elster-transfer-operator:build-latest + </spring-boot.build-image.imageName> + </properties> <dependencies> @@ -57,9 +59,9 @@ <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-core</artifactId> - <version>5.7.2</version> <!-- Use the appropriate version for your project --> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-core</artifactId> + <version>5.7.2</version> <!-- Use the appropriate version for your project --> </dependency> <!-- tools --> @@ -72,15 +74,15 @@ <artifactId>commons-beanutils</artifactId> </dependency> <dependency> - <groupId>com.thedeanda</groupId> - <artifactId>lorem</artifactId> + <groupId>com.thedeanda</groupId> + <artifactId>lorem</artifactId> + </dependency> + + <dependency> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + <version>2.0</version> </dependency> - -<dependency> - <groupId>org.yaml</groupId> - <artifactId>snakeyaml</artifactId> - <version>2.0</version> -</dependency> <!-- test --> <dependency> @@ -153,9 +155,9 @@ <version>${commons-beanutils.version}</version> </dependency> <dependency> - <groupId>com.thedeanda</groupId> - <artifactId>lorem</artifactId> - <version>${lorem.version}</version> + <groupId>com.thedeanda</groupId> + <artifactId>lorem</artifactId> + <version>${lorem.version}</version> </dependency> <!-- javax --> @@ -184,16 +186,16 @@ <version>${kubernetes-server-mock.version}</version> </dependency> <dependency> - <groupId>io.javaoperatorsdk</groupId> - <artifactId>jenvtest-fabric8-client-support</artifactId> - <version>${io.javaoperatorsdk.version}</version> - <scope>test</scope> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>jenvtest-fabric8-client-support</artifactId> + <version>${io.javaoperatorsdk.version}</version> + <scope>test</scope> </dependency> <dependency> - <groupId>io.javaoperatorsdk</groupId> - <artifactId>jenvtest</artifactId> - <version>${io.javaoperatorsdk.version}</version> - <scope>test</scope> + <groupId>io.javaoperatorsdk</groupId> + <artifactId>jenvtest</artifactId> + <version>${io.javaoperatorsdk.version}</version> + <scope>test</scope> </dependency> </dependencies> </dependencyManagement> diff --git a/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserReconciler.java b/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserReconciler.java index d08cf64bf47e03e719c8dab2a17b6c8f6b6a7bbb..60ed474e829f01251ac55854a25a166291ba947e 100644 --- a/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserReconciler.java +++ b/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserReconciler.java @@ -1,16 +1,15 @@ package de.ozgcloud.operator.elstertransfer.user; - import org.springframework.stereotype.Component; import de.ozgcloud.operator.Config; import de.ozgcloud.operator.elstertransfer.OzgCloudCustomResourceStatus; import io.javaoperatorsdk.operator.api.reconciler.Cleaner; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import io.javaoperatorsdk.operator.api.reconciler.Context; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -30,26 +29,18 @@ public class OzgCloudElsterTransferUserReconciler implements Reconciler<OzgCloud public UpdateControl<OzgCloudElsterTransferUser> reconcile(OzgCloudElsterTransferUser elsterTransferUser, Context<OzgCloudElsterTransferUser> context) { try { - String namespace = elsterTransferUser.getMetadata().getNamespace(); - String psw = elsterTransferUserService.updateConfigMap(namespace, ETR_NAMESPACE, CONFIG_MAP_NAME); - - // psw will not be generated if the user "login" already exist in configmap - if (!psw.isEmpty()) { - // Restart the deployment - elsterTransferUserService.restartDeployment(ETR_NAMESPACE, DEPLOYMENT_NAME); - // create or update secret - elsterTransferUserService.createSecret(namespace, psw); - - } + elsterTransferUserService.updateConfigurationAndRestartDeployment(namespace); + //TODO refactor message builder elsterTransferUser.setStatus(OzgCloudElsterTransferUserStatus.builder().status(OzgCloudCustomResourceStatus.OK).message(null).build()); return UpdateControl.updateStatus(elsterTransferUser); } catch (Exception e) { LOG.warn(elsterTransferUser.getMetadata().getName() + " could not reconcile in namespace " + elsterTransferUser.getMetadata().getNamespace(), e); elsterTransferUser + //TODO refactor message builder .setStatus(OzgCloudElsterTransferUserStatus.builder().status(OzgCloudCustomResourceStatus.ERROR).message(e.getMessage()).build()); return UpdateControl.updateStatus(elsterTransferUser).rescheduleAfter(Config.RECONCILER_RETRY_SECONDS); } diff --git a/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserRemoteService.java b/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserRemoteService.java index ca51ddfde829a6f08e3532c6b454a9d4cd8675cc..75e112b591dd7e3b9044a223ae0ff35f311e1257 100644 --- a/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserRemoteService.java +++ b/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserRemoteService.java @@ -3,6 +3,7 @@ package de.ozgcloud.operator.elstertransfer.user; import java.util.Base64; import org.springframework.stereotype.Component; +import org.yaml.snakeyaml.Yaml; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; @@ -24,16 +25,22 @@ public class OzgCloudElsterTransferUserRemoteService { return client.configMaps().inNamespace(configmapNamespace).withName(configMapName).get(); } + + public ConfigMap createConfigMap(String configmapNamespace, String configMapName) { ConfigMap configMap = new ConfigMapBuilder() .withNewMetadata() .withName(configMapName) .endMetadata() - .addToData("users.yaml", "") + .addToData("users.yaml", createEmptyUserYaml()) .build(); return client.configMaps().inNamespace(configmapNamespace).resource(configMap).create(); } + private static String createEmptyUserYaml() { + return new Yaml().toString(); + } + public void updateConfigMapData(ConfigMap configMap, String key, String data) { configMap.getData().put(key, data); client.configMaps().inNamespace(configMap.getMetadata().getNamespace()).resource(configMap).update(); @@ -43,12 +50,16 @@ public class OzgCloudElsterTransferUserRemoteService { Resource<Deployment> deploymentResource = client.apps().deployments().inNamespace(namespace).withName(deploymentName); Deployment deployment = deploymentResource.get(); if (deployment != null) { - deployment.getSpec().getTemplate().getMetadata().getAnnotations() - .put("kubectl.kubernetes.io/restartedAt", String.valueOf(System.currentTimeMillis())); + setRestartFlag(deployment); deploymentResource.replace(deployment); } } + void setRestartFlag(Deployment deployment){ + deployment.getSpec().getTemplate().getMetadata().getAnnotations() + .put("kubectl.kubernetes.io/restartedAt", String.valueOf(System.currentTimeMillis())); + } + public void createSecret(String namespace, String psw) { Secret secret = new SecretBuilder() diff --git a/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserService.java b/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserService.java index a7469d8db67f588f04cb7071283e6b75f2ed7a44..e9d79f719389735fef22e269401e5325f5b20305 100644 --- a/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserService.java +++ b/src/main/java/de/ozgcloud/operator/elstertransfer/user/OzgCloudElsterTransferUserService.java @@ -1,8 +1,5 @@ package de.ozgcloud.operator.elstertransfer.user; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -11,8 +8,6 @@ import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Component; import org.yaml.snakeyaml.Yaml; -import com.fasterxml.jackson.databind.ObjectMapper; - import io.fabric8.kubernetes.api.model.ConfigMap; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -25,44 +20,63 @@ public class OzgCloudElsterTransferUserService { private final OzgCloudElsterTransferUserRemoteService remoteService; private static final String USERS_KEY = "users.yaml"; private static final String ETR_NAMESPACE = "etr-user-creation"; - private static final String USER_ROLE = "USER"; + public static final String USER_ROLE = "USER"; + private static final String CONFIG_MAP_NAME = "etr-user-config"; + private static final String DEPLOYMENT_NAME = "elster-transfer"; + + public void updateConfigurationAndRestartDeployment(String namespace) { + String psw = updateConfigMap(namespace, ETR_NAMESPACE, CONFIG_MAP_NAME); + + // psw will not be generated if the user "login" already exist in configmap + if (!psw.isEmpty()) { + // Restart the deployment + LOG.info("Restart Deployment"); + restartDeployment(ETR_NAMESPACE, DEPLOYMENT_NAME); + // create or update secret + LOG.info("Create Secret"); + createSecret(namespace, psw); + } + } - public String updateConfigMap(String namespace, String configmapNamespace, String configMapName) { + String updateConfigMap(String namespace, String configmapNamespace, String configMapName) { + //TODO rename psw String psw = ""; ConfigMap configMap = remoteService.getConfigMap(configmapNamespace, configMapName); if (configMap == null) { - LOG.info("Creating ConfigMap '{}' in namespace '{}'", configMapName, namespace); + LOG.debug("Creating ConfigMap '{}' in namespace '{}'", configMapName, namespace); configMap = remoteService.createConfigMap(configmapNamespace, configMapName); } - Map<String, Object> usersMap = getUsersMapFromConfigMap(configMap); - List<Map<String, Object>> usersList = getUsersListFromMap(usersMap); + // Map<String, Object> usersMap = getUsersMapFromConfigMap(configMap); + // List<Map<String, Object>> usersList = getUsersListFromMap(usersMap); + + UserList users = getUsersFromYaml(configMap); // use namespace as user "login" and "group" - if (userExistsInList(usersList, namespace)) { + if (userExistsInList(users, namespace)) { LOG.error("User with login '{}' already exists in ConfigMap '{}'.", namespace, configMapName); } else { psw = generatePassword(); String passwordHash = hashPassword(psw); - addUserToList(usersList, namespace, passwordHash); + addUserToList(users, namespace, passwordHash); // Manually construct the YAML string - String usersYaml = constructYamlEntries(usersList); - + String usersYaml = constructYamlEntries(users); + remoteService.updateConfigMapData(configMap, USERS_KEY, usersYaml); - LOG.info("ConfigMap updated successfully: {}", configMapName); + LOG.debug("ConfigMap updated successfully: {}", configMapName); } return psw; } - public void restartDeployment(String etrNamespace, String deploymentName) { + void restartDeployment(String etrNamespace, String deploymentName) { remoteService.restartDeployment(etrNamespace, deploymentName); LOG.info("Deployment '{}' in namespace '{}' restarted successfully", deploymentName, etrNamespace); } - public boolean userExists(String userLogin, String configMapNamespace, String configMapName) { + boolean userExists(String userLogin, String configMapNamespace, String configMapName) { ConfigMap configMap = remoteService.getConfigMap(configMapNamespace, configMapName); if (configMap == null) { @@ -75,28 +89,38 @@ public class OzgCloudElsterTransferUserService { return false; } - Map<String, Object> yamlMap = getUsersMapFromConfigMap(configMap); - - if (!yamlMap.isEmpty()) { - List<Map<String, Object>> users = getUsersListFromMap(yamlMap); - for (Map<String, Object> user : users) { - if (userLogin.equals(user.get("login"))) { - LOG.info("User with login '{}' exists in ConfigMap '{}'", userLogin, configMapName); - return true; - } - } - } - - LOG.info("User with login '{}' does not exist in ConfigMap '{}'", userLogin, configMapName); - return false; + UserList users1 = getUsersFromYaml(configMap); + boolean userExists = users1.existsUser(userLogin); + LOG.info("User with login '{}' exists in ConfigMap '{}'", userLogin, configMapName); + return userExists; + + //TODO: Löschen, wenn der ober Code diese Funktion erfüllt + // Map<String, Object> yamlMap = getUsersMapFromConfigMap(configMap); + // + // if (!yamlMap.isEmpty()) { + // //code refactoren mit UserList Objekt (???) + // + // getUsersFromYaml(yamlMap); + // + // List<Map<String, Object>> users = getUsersListFromMap(yamlMap); + // for (Map<String, Object> user : users) { + // if (userLogin.equals(user.get("login"))) { + // LOG.info("User with login '{}' exists in ConfigMap '{}'", userLogin, configMapName); + // return true; + // } + // } + // } + + // LOG.debug("User with login '{}' does not exist in ConfigMap '{}'", userLogin, configMapName); + // return false; } - public void deleteUser(String userLogin, String configMapName) { + void deleteUser(String userLogin, String configMapName) { try { ConfigMap configMap = remoteService.getConfigMap(ETR_NAMESPACE, configMapName); if (configMap == null) { - LOG.warn("ConfigMap '{}' not found in namespace '{}'", configMapName, ETR_NAMESPACE); + LOG.debug("ConfigMap '{}' not found in namespace '{}'", configMapName, ETR_NAMESPACE); return; } @@ -105,9 +129,8 @@ public class OzgCloudElsterTransferUserService { return; } - Map<String, Object> usersMap = getUsersMapFromConfigMap(configMap); - List<Map<String, Object>> usersList = getUsersListFromMap(usersMap); - usersList.removeIf(userMap -> userLogin.equals(userMap.get("login"))); + UserList usersList = getUsersFromYaml(configMap); + usersList.removeDeleted(userLogin); String updatedUsersYaml = constructYamlEntries(usersList); //usersMap.put("users", usersList); @@ -120,63 +143,90 @@ public class OzgCloudElsterTransferUserService { } } - public void createSecret(String namespace, String psw) { + void createSecret(String namespace, String psw) { remoteService.createSecret(namespace, psw); LOG.info("Secret for user in namespace '{}' created successfully", namespace); } - Map<String, Object> getUsersMapFromConfigMap(ConfigMap configMap) { + //TODO: Löschen wenn Code woanders korrekt +// Map<String, Object> getUsersMapFromConfigMap(ConfigMap configMap) { +// String usersYaml = configMap.getData().get(USERS_KEY); +// +// Map<String, Object> usersMap = new HashMap<>(); +// if (usersYaml != null && !usersYaml.isEmpty()) { +// usersMap = new Yaml().load(usersYaml); +// } +// return usersMap; +// } + + //TODO: Löschen wenn Code woanders korrekt + // List<Map<String, Object>> getUsersListFromMap(Map<String, Object> usersMap) { + // List<Map<String, Object>> usersList = (List<Map<String, Object>>) usersMap.get("users"); + // return usersList != null ? usersList : new ArrayList<>(); + // } + + UserList getUsersFromYaml(ConfigMap configMap) { String usersYaml = configMap.getData().get(USERS_KEY); - Map<String, Object> usersMap = new HashMap<>(); - if (usersYaml != null && !usersYaml.isEmpty()) { - usersMap = new Yaml().load(usersYaml); - } - return usersMap != null ? usersMap : new HashMap<>(); + Map<String, Object> load = new Yaml().load(usersYaml); + List<Map<String, Object>> usersList = (List<Map<String, Object>>) load.get("users"); + return new UserList(usersList); } - List<Map<String, Object>> getUsersListFromMap(Map<String, Object> usersMap) { - List<Map<String, Object>> usersList = (List<Map<String, Object>>) usersMap.get("users"); - return usersList != null ? usersList : new ArrayList<>(); + //TODO: Löschen + // boolean userExistsInList2(List<Map<String, Object>> usersList, String login) { + // for (Map<String, Object> existingUser : usersList) { + // if (login.equals(existingUser.get("login"))) { + // return true; + // } + // } + // return false; + // } + + boolean userExistsInList(UserList usersList, String login) { + return usersList.existsUser(login); } - boolean userExistsInList(List<Map<String, Object>> usersList, String login) { - for (Map<String, Object> existingUser : usersList) { - if (login.equals(existingUser.get("login"))) { - return true; - } - } - return false; + void addUserToList(UserList usersList, String login, String passwordHash) { + usersList.addUserToList(login, passwordHash, USER_ROLE); } - void addUserToList(List<Map<String, Object>> usersList, String login, String passwordHash) { - Map<String, Object> formattedUser = Map.of( - "login", login, - "rolle", USER_ROLE, - "credentials", Map.of("passwortHash", passwordHash), - "gruppe", login); - usersList.add(formattedUser); - } - - String generatePassword() { + //TODO: Löschen + // void addUserToList(List<Map<String, Object>> usersList, String login, String passwordHash) { + // Map<String, Object> formattedUser = Map.of( + // "login", login, + // "rolle", USER_ROLE, + // "credentials", Map.of("passwortHash", passwordHash), + // "gruppe", login); + // usersList.add(formattedUser); + // } + + private String generatePassword() { return UUID.randomUUID().toString(); } - String hashPassword(String password) { + private String hashPassword(String password) { return BCrypt.hashpw(password, BCrypt.gensalt()); } - String constructYamlEntries(List<Map<String, Object>> usersList){ + String constructYamlEntries(UserList userList) { + List<Map<String, Object>> usersList = userList.getUsersList(); StringBuilder usersYaml = new StringBuilder(); - usersYaml.append("fileFormat: 1\nusers:\n"); - - for (Map<String, Object> userEntry : usersList) { - usersYaml.append(" - { login: \"").append(userEntry.get("login")) - .append("\", rolle: \"").append(userEntry.get("rolle")) - .append("\", credentials: { passwortHash: \"") - .append(((Map<String, String>) userEntry.get("credentials")).get("passwortHash")) - .append("\" }, gruppe: \"").append(userEntry.get("gruppe")).append("\" }\n"); - } - return usersYaml.toString(); + usersYaml.append("fileFormat: 1\nusers:\n"); + + for (Map<String, Object> userEntry : usersList) { + usersYaml.append(getYamlForUser(userEntry)); + } + return usersYaml.toString(); + } + + String getYamlForUser(Map<String, Object> userEntry) { + StringBuilder sb = new StringBuilder(); + sb.append(" - { login: \"").append(userEntry.get("login")) + .append("\", rolle: \"").append(userEntry.get("rolle")) + .append("\", credentials: { passwortHash: \"") + .append(((Map<String, String>) userEntry.get("credentials")).get("passwortHash")) + .append("\" }, gruppe: \"").append(userEntry.get("gruppe")).append("\" }\n"); + return sb.toString(); } } diff --git a/src/main/java/de/ozgcloud/operator/elstertransfer/user/UserList.java b/src/main/java/de/ozgcloud/operator/elstertransfer/user/UserList.java new file mode 100644 index 0000000000000000000000000000000000000000..d2603b8b134de684ff22cebad926a2778f8ee7ad --- /dev/null +++ b/src/main/java/de/ozgcloud/operator/elstertransfer/user/UserList.java @@ -0,0 +1,56 @@ +package de.ozgcloud.operator.elstertransfer.user; + + +import java.util.List; +import java.util.Map; + +/** + * TODO Gib mir einen schöneren Namen + */ +public class UserList { + + private List<Map<String, Object>> usersList; + + public UserList(List<Map<String, Object>> usersList) { + this.usersList = usersList; + } + + + boolean existsUser(String login){ + for (Map<String, Object> existingUser : usersList) { + if (login.equals(existingUser.get("login"))) { + return true; + } + } + return false; + } + + //TODO Lösche diese Stelle, wenn sie im Service nicht mehr gebraucht wird +// boolean existsUSer2(String login){ +// List<Map<String, Object>> users = getUsersListFromMap(yamlMap); +// for (Map<String, Object> user : users) { +// if (userLogin.equals(user.get("login"))) { +// LOG.info("User with login '{}' exists in ConfigMap '{}'", userLogin, configMapName); +// return true; +// } +// } +// } + + void addUserToList(String login, String passwordHash, String role) { + Map<String, Object> formattedUser = Map.of( + "login", login, + "rolle", role, + "credentials", Map.of("passwortHash", passwordHash), + "gruppe", login); + usersList.add(formattedUser); + } + + void removeDeleted(String userLogin){ + usersList.removeIf(userMap -> userLogin.equals(userMap.get("login"))); + } + + //TODO Prüfen, ob diese Methode tatsächlich gebraucht wird! Sonst überall mit der Klasse arbeiten. + public List<Map<String, Object>> getUsersList() { + return usersList; + } +}