From cb0fe4235f07b75dbb83cb9ef3227902d7356519 Mon Sep 17 00:00:00 2001
From: OZGCloud <ozgcloud@mgm-tp.com>
Date: Tue, 15 Oct 2024 15:39:55 +0200
Subject: [PATCH] OZG-6867 OZG-6895 Test keycloak api for accessing groups

---
 pom.xml                                       |  2 +-
 .../ozgcloud/admin/keycloak/GroupMapper.java  |  8 +-
 .../admin/keycloak/KeycloakConfiguration.java |  4 +-
 .../admin/keycloak/KeycloakRemoteService.java | 11 +++
 src/main/resources/application-remotekc.yaml  |  2 +-
 src/main/resources/application.yaml           |  6 +-
 .../admin/common/KeycloakInitializer.java     | 47 ++++++++++
 .../keycloak/KeycloakRemoteServiceITCase.java | 37 ++++++++
 src/test/resources/application-itcase.yaml    |  2 +-
 src/test/resources/keycloak/realm-export.json | 90 ++++++++++++++++++-
 10 files changed, 196 insertions(+), 13 deletions(-)
 create mode 100644 src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java
 create mode 100644 src/test/java/de/ozgcloud/admin/keycloak/KeycloakRemoteServiceITCase.java

diff --git a/pom.xml b/pom.xml
index d06edc17..cf1ce94e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
 		<publishImage>false</publishImage>
 		<build.number>SET_BY_JENKINS</build.number>
 		<spring-cloud-config-server.version>4.1.2</spring-cloud-config-server.version>
-		<testcontainers-keycloak.version>3.2.0</testcontainers-keycloak.version>
+		<testcontainers-keycloak.version>3.4.0</testcontainers-keycloak.version>
 		<mongock.version>5.4.0</mongock.version>
 		<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
 		<mapstruct-processor.version>${mapstruct.version}</mapstruct-processor.version>
diff --git a/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java b/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java
index 3f2862be..a36e5cdb 100644
--- a/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java
+++ b/src/main/java/de/ozgcloud/admin/keycloak/GroupMapper.java
@@ -1,9 +1,11 @@
 package de.ozgcloud.admin.keycloak;
 
 import java.util.List;
+import java.util.Map;
 
 import org.keycloak.representations.idm.GroupRepresentation;
 import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
 import org.mapstruct.NullValueCheckStrategy;
 
 @Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
@@ -11,6 +13,10 @@ interface GroupMapper {
 
 	List<Group> fromGroupRepresentations(List<GroupRepresentation> groupRepresentations);
 
-
+	@Mapping(target = "organisationsEinheitId", source = "attributes")
 	Group fromGroupRepresentation(GroupRepresentation groupRepresentation);
+
+	default String getOrganisationsEinheitId(Map<String, List<String>> attributes) {
+		return "dummy";
+	}
 }
diff --git a/src/main/java/de/ozgcloud/admin/keycloak/KeycloakConfiguration.java b/src/main/java/de/ozgcloud/admin/keycloak/KeycloakConfiguration.java
index 3406a5a7..06929b83 100644
--- a/src/main/java/de/ozgcloud/admin/keycloak/KeycloakConfiguration.java
+++ b/src/main/java/de/ozgcloud/admin/keycloak/KeycloakConfiguration.java
@@ -35,8 +35,6 @@ import lombok.RequiredArgsConstructor;
 @RequiredArgsConstructor
 public class KeycloakConfiguration {
 
-	private static final String CLIENT = "admin-cli";
-
 	private final KeycloakApiProperties keycloakApiProperties;
 
 	@Bean
@@ -53,7 +51,7 @@ public class KeycloakConfiguration {
 		return KeycloakBuilder.builder()
 				.serverUrl(keycloakApiProperties.getUrl())
 				.realm(keycloakApiProperties.getRealm())
-				.clientId(CLIENT)
+				.clientId(keycloakApiProperties.getClient())
 				.username(keycloakApiProperties.getUser())
 				.password(keycloakApiProperties.getPassword())
 				.build();
diff --git a/src/main/java/de/ozgcloud/admin/keycloak/KeycloakRemoteService.java b/src/main/java/de/ozgcloud/admin/keycloak/KeycloakRemoteService.java
index c1e7db46..bca52219 100644
--- a/src/main/java/de/ozgcloud/admin/keycloak/KeycloakRemoteService.java
+++ b/src/main/java/de/ozgcloud/admin/keycloak/KeycloakRemoteService.java
@@ -1,8 +1,10 @@
 package de.ozgcloud.admin.keycloak;
 
+import java.util.List;
 import java.util.stream.Stream;
 
 import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.GroupRepresentation;
 import org.springframework.stereotype.Service;
 
 import lombok.RequiredArgsConstructor;
@@ -17,4 +19,13 @@ public class KeycloakRemoteService {
 //		 realmResource.groups().groups();
 		return Stream.empty();
 	}
+
+	// TODO: only for research
+	public List<GroupRepresentation> getGroupRepresentations() {
+		return realmResource.groups().groups();
+	}
+
+	public GroupRepresentation getGroupRepresentation(String id) {
+		return realmResource.groups().group(id).toRepresentation();
+	}
 }
diff --git a/src/main/resources/application-remotekc.yaml b/src/main/resources/application-remotekc.yaml
index da5346a4..f31f6ab8 100644
--- a/src/main/resources/application-remotekc.yaml
+++ b/src/main/resources/application-remotekc.yaml
@@ -5,4 +5,4 @@ ozgcloud:
     resource: admin
   keycloak:
     api:
-      url: ${auth-server-url}
\ No newline at end of file
+      url: ${ozgcloud.oauth2.auth-server-url}
\ No newline at end of file
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 527ecc91..53c18301 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -78,7 +78,7 @@ ozgcloud:
     api:
       ldap-id-key: LDAP_ID
       organisations-einheit-id-key: organisationseinheitId
-      user: userManagerApiUser
-      password: userManagerApiUser
+      user: administrationApiUser
+      password: administrationApiUser
       realm: by-kiel-dev
-      client: alfa
\ No newline at end of file
+      client: admin-cli
\ No newline at end of file
diff --git a/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java b/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java
new file mode 100644
index 00000000..3cf919b5
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/common/KeycloakInitializer.java
@@ -0,0 +1,47 @@
+package de.ozgcloud.admin.common;
+
+import java.time.Duration;
+
+import org.springframework.boot.test.util.TestPropertyValues;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import dasniko.testcontainers.keycloak.KeycloakContainer;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class KeycloakInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
+
+	private static final String AUTH_SERVER_URL = "ozgcloud.oauth2.auth-server-url";
+
+	private static KeycloakContainer keycloakContainer;
+
+	@Override
+	public void initialize(ConfigurableApplicationContext applicationContext) {
+		initContainer();
+		setProperties(applicationContext);
+	}
+
+	@SuppressWarnings("resource")
+	private void initContainer() {
+		if (keycloakContainer == null) {
+			log.info("Creating Keycloak-container...");
+			keycloakContainer = new KeycloakContainer().withRealmImportFile("keycloak/realm-export.json").withVerboseOutput();
+		}
+		if (!keycloakContainer.isRunning()) {
+			log.info("Starting Keycloak-container...");
+			keycloakContainer.setWaitStrategy(
+					Wait.forLogMessage(".*message\":\"started.*", 1).withStartupTimeout(Duration.ofMinutes(3)));
+			keycloakContainer.start();
+			log.info("Keycloak-container started");
+		}
+	}
+
+	private void setProperties(ConfigurableApplicationContext applicationContext) {
+		log.info("Keycloak URL: {}", keycloakContainer.getAuthServerUrl());
+
+		TestPropertyValues.of(AUTH_SERVER_URL + "=" + keycloakContainer.getAuthServerUrl()).applyTo(applicationContext);
+
+	}
+}
diff --git a/src/test/java/de/ozgcloud/admin/keycloak/KeycloakRemoteServiceITCase.java b/src/test/java/de/ozgcloud/admin/keycloak/KeycloakRemoteServiceITCase.java
new file mode 100644
index 00000000..3fb7597d
--- /dev/null
+++ b/src/test/java/de/ozgcloud/admin/keycloak/KeycloakRemoteServiceITCase.java
@@ -0,0 +1,37 @@
+package de.ozgcloud.admin.keycloak;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+
+import de.ozgcloud.admin.common.KeycloakInitializer;
+import de.ozgcloud.common.test.ITCase;
+
+@ITCase
+@ContextConfiguration(initializers = KeycloakInitializer.class)
+class KeycloakRemoteServiceITCase {
+
+	@Autowired
+	private KeycloakRemoteService service;
+
+	@Nested
+	class TestGetGroupRepresentations {
+
+		@Test
+		void shouldReturnGroupRepresentations() {
+			var result = service.getGroupRepresentations();
+
+			assertThat(result).hasSize(6);
+		}
+
+		@Test
+		void shouldReturnAttributes() {
+			var result = service.getGroupRepresentation("94f0562a-cb66-41a5-96c9-a984f0bee3ec");
+
+			assertThat(result.getAttributes().get("organisationseinheitId").getFirst()).isEqualTo("248240886");
+		}
+	}
+}
diff --git a/src/test/resources/application-itcase.yaml b/src/test/resources/application-itcase.yaml
index 5ef65dbc..e1d232df 100644
--- a/src/test/resources/application-itcase.yaml
+++ b/src/test/resources/application-itcase.yaml
@@ -8,4 +8,4 @@ ozgcloud:
     resource: admin
   keycloak:
     api:
-      url: ${auth-server-url}
+      url: ${ozgcloud.oauth2.auth-server-url}
diff --git a/src/test/resources/keycloak/realm-export.json b/src/test/resources/keycloak/realm-export.json
index 1a35e536..63f0d9bc 100755
--- a/src/test/resources/keycloak/realm-export.json
+++ b/src/test/resources/keycloak/realm-export.json
@@ -44,6 +44,86 @@
   "quickLoginCheckMilliSeconds": 1000,
   "maxDeltaTimeSeconds": 43200,
   "failureFactor": 30,
+  "groups": [
+    {
+      "id": "94f0562a-cb66-41a5-96c9-a984f0bee3ec",
+      "name": "Bauamt",
+      "path": "/Bauamt",
+      "subGroups": [],
+      "attributes": {
+        "organisationseinheitId": [
+          "248240886"
+        ]
+      },
+      "realmRoles": [],
+      "clientRoles": {}
+    },
+    {
+      "id": "bfd284be-9d30-4e9a-82d2-daf9ac6593b6",
+      "name": "Denkmalpflege",
+      "path": "/Denkmalpflege",
+      "subGroups": [],
+      "attributes": {
+        "organisationseinheitId": [
+          "9093371"
+        ]
+      },
+      "realmRoles": [],
+      "clientRoles": {}
+    },
+    {
+      "id": "e80b596b-7559-4437-8349-4732ac567d15",
+      "name": "Fundstelle",
+      "path": "/Fundstelle",
+      "subGroups": [],
+      "attributes": {
+        "organisationseinheitId": [
+          "10363455"
+        ]
+      },
+      "realmRoles": [],
+      "clientRoles": {}
+    },
+    {
+      "id": "ffad390f-00c0-4459-9512-eb1f4be3631e",
+      "name": "Landesamt für Denkmalpflege",
+      "path": "/Landesamt für Denkmalpflege",
+      "subGroups": [],
+      "attributes": {
+        "organisationseinheitId": [
+          "9093371"
+        ]
+      },
+      "realmRoles": [],
+      "clientRoles": {}
+    },
+    {
+      "id": "155b4752-bbc3-4c6a-afa7-7769f1b2ea8a",
+      "name": "Ordnungsamt",
+      "path": "/Ordnungsamt",
+      "subGroups": [],
+      "attributes": {
+        "organisationseinheitId": [
+          "9030229"
+        ]
+      },
+      "realmRoles": [],
+      "clientRoles": {}
+    },
+    {
+      "id": "53c11c42-9f8e-4cbc-b6da-1c1ff2d46187",
+      "name": "Wirtschaftsförderung",
+      "path": "/Wirtschaftsförderung",
+      "subGroups": [],
+      "attributes": {
+        "organisationseinheitId": [
+          "9797773"
+        ]
+      },
+      "realmRoles": [],
+      "clientRoles": {}
+    }
+  ],
   "defaultRole": {
     "id": "dd27b699-836d-4ed8-9111-ee34acbaf5ce",
     "name": "default-roles-by-kiel-dev",
@@ -2037,16 +2117,20 @@
   },
   "users": [
     {
-      "username": "admin-test",
+      "id": "b46def26-a599-4940-a32f-e070c478750d",
+      "username": "administrationApiUser",
       "firstName": "Vorname",
       "lastName": "Nachname",
       "enabled": true,
       "credentials": [
         {
           "type": "password",
-          "value": "Password"
+          "value": "administrationApiUser"
         }
-      ]
+      ],
+      "clientRoles" : {
+        "realm-management" : [ "view-users" ]
+      }
     }
   ]
 }
\ No newline at end of file
-- 
GitLab