From c18a24e1844c17be985400a9b0029469e6b4369d Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Thu, 7 Nov 2024 12:05:27 +0100 Subject: [PATCH] =?UTF-8?q?OZG-7062=20Logging=20Aspect=20gefixed.=20Sync?= =?UTF-8?q?=20ozgCloudUserId=20ITCase=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/de/ozgcloud/user/UserService.java | 38 +++---- .../user/common/logging/OzgCloudLogging.java | 37 ++++++ .../logging/OzgCloudLoggingInterceptor.java | 49 ++++++++ .../user/keycloak/KeycloakApiService.java | 21 ++-- .../keycloak/KeycloakUserRemoteService.java | 13 +-- .../OrganisationsEinheitService.java | 9 +- .../user/settings/UserSettingsService.java | 8 +- .../de/ozgcloud/user/sync/SyncService.java | 17 ++- .../user/sync/KeycloakMongoDbTestProfile.java | 38 +++++++ .../user/sync/SyncOzgCloudIdITCase.java | 105 ++++++++++++++++++ 10 files changed, 274 insertions(+), 61 deletions(-) create mode 100644 user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLogging.java create mode 100644 user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLoggingInterceptor.java create mode 100644 user-manager-server/src/test/java/de/ozgcloud/user/sync/KeycloakMongoDbTestProfile.java create mode 100644 user-manager-server/src/test/java/de/ozgcloud/user/sync/SyncOzgCloudIdITCase.java diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/UserService.java b/user-manager-server/src/main/java/de/ozgcloud/user/UserService.java index b3ec71cd..b33b35f0 100644 --- a/user-manager-server/src/main/java/de/ozgcloud/user/UserService.java +++ b/user-manager-server/src/main/java/de/ozgcloud/user/UserService.java @@ -1,9 +1,5 @@ /* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * + * Copyright (c) 2022-2024. * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -31,10 +27,10 @@ import jakarta.enterprise.context.ApplicationScoped; import org.apache.commons.lang3.StringUtils; -import de.ozgcloud.common.logging.OzgCloudLogging; import de.ozgcloud.user.common.errorhandling.FunctionalException; import de.ozgcloud.user.common.errorhandling.ResourceNotFoundException; import de.ozgcloud.user.common.errorhandling.TechnicalException; +import de.ozgcloud.user.common.logging.OzgCloudLogging; import de.ozgcloud.user.keycloak.KeycloakUserRemoteService; import de.ozgcloud.user.settings.UserSettings; import io.smallrye.mutiny.Uni; @@ -53,7 +49,7 @@ public class UserService { public User save(User user) { findUser(user).ifPresentOrElse(persistedUser -> repository.updateUser(addIdUser(user, persistedUser)), - () -> saveNewUser(user)); + () -> saveNewUser(user)); return repository.refresh(user); } @@ -71,10 +67,10 @@ public class UserService { public Stream<User> findActiveUsers(FindUserProfilesQuery query) { if (StringUtils.isNotBlank(query.getOrganisationsEinheitId())) { return repository.findUsersByDeletedAndOrganisationsEinheitId( - query.getSearchBy(), - false, - query.getOrganisationsEinheitId(), - query.getLimit()); + query.getSearchBy(), + false, + query.getOrganisationsEinheitId(), + query.getLimit()); } return repository.findUsersByDeleted(query.getSearchBy(), false, query.getLimit()); } @@ -82,10 +78,10 @@ public class UserService { public Stream<User> findInactiveUsers(FindUserProfilesQuery query) { if (StringUtils.isNotBlank(query.getOrganisationsEinheitId())) { return repository.findUsersByDeletedAndOrganisationsEinheitId( - query.getSearchBy(), - true, - query.getOrganisationsEinheitId(), - query.getLimit()); + query.getSearchBy(), + true, + query.getOrganisationsEinheitId(), + query.getLimit()); } return repository.findUsersByDeleted(query.getSearchBy(), true, query.getLimit()); } @@ -115,8 +111,8 @@ public class UserService { public User findByExternalId(String externalId) { return repository.findByKeycloakId(externalId) - .or(() -> findInKeycloakAndSave(externalId)) - .orElseThrow(() -> new ResourceNotFoundException(User.class, externalId)); + .or(() -> findInKeycloakAndSave(externalId)) + .orElseThrow(() -> new ResourceNotFoundException(User.class, externalId)); } private Optional<User> findInKeycloakAndSave(String externalId) { @@ -126,10 +122,10 @@ public class UserService { public User saveAndSync(User user) { var saved = save(user); Uni.createFrom() - .item(user) - .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .subscribe() - .with(u -> keycloakUserRemoteService.updateOzgCloudUserId(saved)); + .item(user) + .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) + .subscribe() + .with(u -> keycloakUserRemoteService.updateOzgCloudUserId(saved)); return saved; } diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLogging.java b/user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLogging.java new file mode 100644 index 00000000..2458859c --- /dev/null +++ b/user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLogging.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024. + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.user.common.logging; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.interceptor.InterceptorBinding; + +@Target({ TYPE, METHOD }) +@Retention(RUNTIME) +@Documented +@InterceptorBinding +public @interface OzgCloudLogging { + +} diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLoggingInterceptor.java b/user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLoggingInterceptor.java new file mode 100644 index 00000000..05e84a81 --- /dev/null +++ b/user-manager-server/src/main/java/de/ozgcloud/user/common/logging/OzgCloudLoggingInterceptor.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024. + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.user.common.logging; + +import jakarta.annotation.Priority; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InvocationContext; + +import de.ozgcloud.common.logging.AspectLoggingUtils; +import de.ozgcloud.common.logging.OzgCloudLogging; + +@OzgCloudLogging +@Priority(10) +@Interceptor +public class OzgCloudLoggingInterceptor { + + @AroundInvoke + Object logging(InvocationContext context) throws Exception { + try { + AspectLoggingUtils.log(context); + + var nextChainMethod = context.proceed(); + + AspectLoggingUtils.logReturnValue(context, nextChainMethod); + return nextChainMethod; + } catch (Exception e) { + AspectLoggingUtils.logException(context, e); + throw e; + } + } +} \ No newline at end of file diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakApiService.java b/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakApiService.java index 5855bd35..de55da8e 100644 --- a/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakApiService.java +++ b/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakApiService.java @@ -1,9 +1,5 @@ /* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * + * Copyright (c) 2022-2024. * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -29,23 +25,24 @@ import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.ClientErrorException; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.ProcessingException; + import org.apache.commons.lang3.ObjectUtils; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.representations.idm.UserRepresentation; -import de.ozgcloud.common.logging.OzgCloudLogging; import de.ozgcloud.user.RemoteUserIterator; import de.ozgcloud.user.User; import de.ozgcloud.user.UserResourceMapper; import de.ozgcloud.user.common.errorhandling.KeycloakUnavailableException; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.ws.rs.BadRequestException; -import jakarta.ws.rs.ClientErrorException; -import jakarta.ws.rs.NotFoundException; -import jakarta.ws.rs.ProcessingException; +import de.ozgcloud.user.common.logging.OzgCloudLogging; import lombok.extern.log4j.Log4j2; @ApplicationScoped diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakUserRemoteService.java b/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakUserRemoteService.java index 555b33c1..c8f959b1 100644 --- a/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakUserRemoteService.java +++ b/user-manager-server/src/main/java/de/ozgcloud/user/keycloak/KeycloakUserRemoteService.java @@ -1,9 +1,5 @@ /* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * + * Copyright (c) 2024. * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -26,16 +22,17 @@ package de.ozgcloud.user.keycloak; import java.util.Optional; import java.util.stream.Stream; -import de.ozgcloud.common.logging.OzgCloudLogging; -import de.ozgcloud.user.User; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import de.ozgcloud.user.User; +import de.ozgcloud.user.common.logging.OzgCloudLogging; + @ApplicationScoped @OzgCloudLogging public class KeycloakUserRemoteService { - static final String ATTRIBUTE_NAME_OZG_CLOUD_USER_ID = "ozgCloudUserId"; + public static final String ATTRIBUTE_NAME_OZG_CLOUD_USER_ID = "ozgCloudUserId"; @Inject KeycloakApiService apiService; diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/organisationseinheit/OrganisationsEinheitService.java b/user-manager-server/src/main/java/de/ozgcloud/user/organisationseinheit/OrganisationsEinheitService.java index bf096522..5fd1885e 100644 --- a/user-manager-server/src/main/java/de/ozgcloud/user/organisationseinheit/OrganisationsEinheitService.java +++ b/user-manager-server/src/main/java/de/ozgcloud/user/organisationseinheit/OrganisationsEinheitService.java @@ -1,9 +1,5 @@ /* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * + * Copyright (c) 2023-2024. * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -28,6 +24,9 @@ import java.util.Collection; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import de.ozgcloud.user.common.logging.OzgCloudLogging; + +@OzgCloudLogging @ApplicationScoped class OrganisationsEinheitService { diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/settings/UserSettingsService.java b/user-manager-server/src/main/java/de/ozgcloud/user/settings/UserSettingsService.java index d7aa65b6..c1243a62 100644 --- a/user-manager-server/src/main/java/de/ozgcloud/user/settings/UserSettingsService.java +++ b/user-manager-server/src/main/java/de/ozgcloud/user/settings/UserSettingsService.java @@ -1,9 +1,5 @@ /* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * + * Copyright (c) 2022-2024. * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -30,7 +26,9 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import de.ozgcloud.user.UserService; +import de.ozgcloud.user.common.logging.OzgCloudLogging; +@OzgCloudLogging @ApplicationScoped class UserSettingsService { diff --git a/user-manager-server/src/main/java/de/ozgcloud/user/sync/SyncService.java b/user-manager-server/src/main/java/de/ozgcloud/user/sync/SyncService.java index 498cde45..c4a79d48 100644 --- a/user-manager-server/src/main/java/de/ozgcloud/user/sync/SyncService.java +++ b/user-manager-server/src/main/java/de/ozgcloud/user/sync/SyncService.java @@ -1,9 +1,5 @@ /* - * Copyright (C) 2022-2023 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * + * Copyright (c) 2022-2024. * Lizenziert unter der EUPL, Version 1.2 oder - sobald * diese von der Europäischen Kommission genehmigt wurden - * Folgeversionen der EUPL ("Lizenz"); @@ -25,14 +21,15 @@ package de.ozgcloud.user.sync; import java.util.function.Predicate; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + import org.apache.commons.collections4.CollectionUtils; -import de.ozgcloud.common.logging.OzgCloudLogging; import de.ozgcloud.user.User; import de.ozgcloud.user.UserService; +import de.ozgcloud.user.common.logging.OzgCloudLogging; import de.ozgcloud.user.keycloak.KeycloakUserRemoteService; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; @ApplicationScoped @OzgCloudLogging @@ -47,8 +44,8 @@ class SyncService { void sync(long syncTimestamp) { keycloakService.getAllUsers() - .filter(HAS_ANY_ROLE) - .forEach(user -> userService.saveAndSync(addLastSyncTimestamp(user, syncTimestamp))); + .filter(HAS_ANY_ROLE) + .forEach(user -> userService.saveAndSync(addLastSyncTimestamp(user, syncTimestamp))); userService.markUnsyncedUsersAsDeleted(syncTimestamp); } diff --git a/user-manager-server/src/test/java/de/ozgcloud/user/sync/KeycloakMongoDbTestProfile.java b/user-manager-server/src/test/java/de/ozgcloud/user/sync/KeycloakMongoDbTestProfile.java new file mode 100644 index 00000000..63676d74 --- /dev/null +++ b/user-manager-server/src/test/java/de/ozgcloud/user/sync/KeycloakMongoDbTestProfile.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024. + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.user.sync; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTestProfile; + +public class KeycloakMongoDbTestProfile implements QuarkusTestProfile { + + @Override + public Map<String, String> getConfigOverrides() { + return Map.of("ozgcloud.keycloak.api.user", "userManagerApiUser", + "ozgcloud.keycloak.api.password", "hlc_j1I1Ji0trC0", + "ozgcloud.keycloak.api.realm", "by-kiel-dev", + "keycloak.url", "https://sso.dev.by.ozg-cloud.de", + "quarkus.mongodb.devservices.enabled", "true", + "quarkus.oidc.auth-server-url", "https://sso.dev.by.ozg-cloud.de" + ); + } +} \ No newline at end of file diff --git a/user-manager-server/src/test/java/de/ozgcloud/user/sync/SyncOzgCloudIdITCase.java b/user-manager-server/src/test/java/de/ozgcloud/user/sync/SyncOzgCloudIdITCase.java new file mode 100644 index 00000000..88220b14 --- /dev/null +++ b/user-manager-server/src/test/java/de/ozgcloud/user/sync/SyncOzgCloudIdITCase.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2024. + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ + +package de.ozgcloud.user.sync; + +import static de.ozgcloud.user.keycloak.KeycloakUserRemoteService.*; +import static org.assertj.core.api.Assertions.*; + +import java.time.Instant; +import java.util.List; + +import jakarta.inject.Inject; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.keycloak.admin.client.resource.RealmResource; + +import com.mongodb.client.MongoClient; + +import de.ozgcloud.user.TestDatabaseUtils; +import de.ozgcloud.user.User; +import de.ozgcloud.user.UserService; +import de.ozgcloud.user.UserTestFactory; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(KeycloakMongoDbTestProfile.class) +public class SyncOzgCloudIdITCase { + public static final String KEYCLOAK_USER_ID = "90748555-7894-4854-8292-1f6106826962"; + @Inject + SyncService service; + + @Inject + MongoClient mongoClient; + + @Inject + UserService userService; + + @Inject + RealmResource realmResource; + + private String userIdDb; + + @BeforeEach + void init() { + TestDatabaseUtils.getDatabase(mongoClient).drop(); + + prepareData(); + } + + private void prepareData() { + User user1 = UserTestFactory.createBuilder() + .externalId(KEYCLOAK_USER_ID) + .keycloakUserId(KEYCLOAK_USER_ID) + .email("testerozg+dorothea@gmail.com") + .firstName("Dorothea") + .lastName("Doe") + .fullName("Dorothea Doe") + .fullNameReversed("Doe Dorothea") + .username("dorothea") + .organisationsEinheitIds(List.of("248240886", "9030229", "10363455")) + .build(); + + userIdDb = userService.save(user1).getId().toString(); + } + + @DisplayName("Update ozgCloudUserId attribute in keycloak") + @Test + void shouldUpdateOzgCloudUserId() { + syncUsers(); + + var ozgCloudUserId = getOzgCloudUserIdFromKeycloak(); + + assertThat(userIdDb).isEqualTo(ozgCloudUserId); + } + + private void syncUsers() { + var timestamp = Instant.now().toEpochMilli(); + service.sync(timestamp); + } + + private String getOzgCloudUserIdFromKeycloak() { + var userResource = realmResource.users().get(KEYCLOAK_USER_ID); + return userResource.toRepresentation().getAttributes().get(ATTRIBUTE_NAME_OZG_CLOUD_USER_ID).getFirst(); + } +} -- GitLab