From 371bb29278b9dfb365061f438c5d927db72fd018 Mon Sep 17 00:00:00 2001 From: Slawomir Wieczorek Date: Mon, 16 Sep 2024 10:39:03 +0200 Subject: [PATCH 1/3] HELLODATA-1689 - deactivate users --- .../commons/sidecars/events/HDEvent.java | 5 +- .../portal/user/service/UserService.java | 55 +++++---- .../user/AirflowCreateUserConsumer.java | 13 +- .../user/AirflowDisableUserConsumer.java | 72 +++++++++++ .../user/AirflowUserContextRoleConsumer.java | 98 ++------------- .../airflow/service/user/AirflowUserUtil.java | 114 ++++++++++++++++++ .../user/SupersetDisableUserConsumer.java | 53 ++++++++ 7 files changed, 282 insertions(+), 128 deletions(-) create mode 100644 hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowDisableUserConsumer.java create mode 100644 hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserUtil.java create mode 100644 hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java diff --git a/hello-data-commons/hello-data-sidecar-common/src/main/java/ch/bedag/dap/hellodata/commons/sidecars/events/HDEvent.java b/hello-data-commons/hello-data-sidecar-common/src/main/java/ch/bedag/dap/hellodata/commons/sidecars/events/HDEvent.java index 6111e9c5..caa7f4af 100644 --- a/hello-data-commons/hello-data-sidecar-common/src/main/java/ch/bedag/dap/hellodata/commons/sidecars/events/HDEvent.java +++ b/hello-data-commons/hello-data-sidecar-common/src/main/java/ch/bedag/dap/hellodata/commons/sidecars/events/HDEvent.java @@ -46,7 +46,10 @@ public enum HDEvent { PUBLISH_ROLE_RESOURCES(METAINFO_STREAM, "publish_resources.role", RoleResource.class), PUBLISH_PERMISSION_RESOURCES(METAINFO_STREAM, "publish_resources.permission", PermissionResource.class), PUBLISH_PIPELINE_RESOURCES(METAINFO_STREAM, "publish_resources.pipeline", PipelineResource.class), - PUBLISH_USER_RESOURCES(METAINFO_STREAM, "publish_resources.user", UserResource.class), CREATE_USER(METAINFO_STREAM, "create_user", SubsystemUserUpdate.class), + PUBLISH_USER_RESOURCES(METAINFO_STREAM, "publish_resources.user", UserResource.class), + DISABLE_USER(METAINFO_STREAM, "disable_user", SubsystemUserUpdate.class), + ENABLE_USER(METAINFO_STREAM, "enable_user", SubsystemUserUpdate.class), + CREATE_USER(METAINFO_STREAM, "create_user", SubsystemUserUpdate.class), UPDATE_USER_CONTEXT_ROLE(METAINFO_STREAM, "update_user_context_role", UserContextRoleUpdate.class), UPDATE_STORAGE_MONITORING_RESULT(METAINFO_STREAM, "update_storage_monitoring_result", StorageMonitoringResult.class), DELETE_USER(METAINFO_STREAM, "delete_user", SubsystemUserDelete.class), diff --git a/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java b/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java index d8bd98cf..2ed35a9c 100644 --- a/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java +++ b/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java @@ -51,14 +51,7 @@ import ch.bedag.dap.hellodata.portal.metainfo.service.MetaInfoResourceService; import ch.bedag.dap.hellodata.portal.role.data.RoleDto; import ch.bedag.dap.hellodata.portal.role.service.RoleService; -import ch.bedag.dap.hellodata.portal.user.data.AdUserDto; -import ch.bedag.dap.hellodata.portal.user.data.ContextDto; -import ch.bedag.dap.hellodata.portal.user.data.ContextsDto; -import ch.bedag.dap.hellodata.portal.user.data.DashboardsDto; -import ch.bedag.dap.hellodata.portal.user.data.DataDomainDto; -import ch.bedag.dap.hellodata.portal.user.data.UpdateContextRolesForUserDto; -import ch.bedag.dap.hellodata.portal.user.data.UserContextRoleDto; -import ch.bedag.dap.hellodata.portal.user.data.UserDto; +import ch.bedag.dap.hellodata.portal.user.data.*; import ch.bedag.dap.hellodata.portalcommon.role.entity.UserContextRoleEntity; import ch.bedag.dap.hellodata.portalcommon.user.entity.UserEntity; import ch.bedag.dap.hellodata.portalcommon.user.repository.UserRepository; @@ -67,24 +60,6 @@ import io.nats.client.Message; import jakarta.persistence.EntityExistsException; import jakarta.ws.rs.NotFoundException; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.BooleanUtils; @@ -99,6 +74,16 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ResponseStatusException; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + import static ch.bedag.dap.hellodata.commons.SlugifyUtil.DASHBOARD_ROLE_PREFIX; @Log4j2 @@ -160,7 +145,7 @@ public void validateEmailAlreadyExists(String email) { @Transactional(propagation = Propagation.REQUIRES_NEW) public void syncAllUsers() { - List allUsers = getAllUsers(); + List allUsers = getAllUsers().stream().filter(UserDto::getEnabled).toList(); log.info("[syncAllUsers] Found {} users to sync with surrounding systems.", allUsers.size()); AtomicInteger counter = new AtomicInteger(); allUsers.forEach(u -> { @@ -257,6 +242,11 @@ public void updateLastAccess(String userId) { } public void createUserInSubsystems(String userId) { + SubsystemUserUpdate createUser = getSubsystemUserUpdate(userId); + natsSenderService.publishMessageToJetStream(HDEvent.CREATE_USER, createUser); + } + + private SubsystemUserUpdate getSubsystemUserUpdate(String userId) { UserRepresentation representation = getUserRepresentation(userId); SubsystemUserUpdate createUser = new SubsystemUserUpdate(); createUser.setFirstName(representation.getFirstName()); @@ -264,7 +254,7 @@ public void createUserInSubsystems(String userId) { createUser.setUsername(representation.getUsername()); createUser.setEmail(representation.getEmail().toLowerCase(Locale.ROOT)); createUser.setActive(representation.isEnabled()); - natsSenderService.publishMessageToJetStream(HDEvent.CREATE_USER, createUser); + return createUser; } @Transactional @@ -275,6 +265,10 @@ public void disableUserById(String userId) { UserRepresentation representation = userResource.toRepresentation(); representation.setEnabled(false); userResource.update(representation); + userResource.logout(); + SubsystemUserUpdate subsystemUserUpdate = getSubsystemUserUpdate(userId); + subsystemUserUpdate.setActive(false); + natsSenderService.publishMessageToJetStream(HDEvent.DISABLE_USER, subsystemUserUpdate); emailNotificationService.notifyAboutUserDeactivation(representation.getFirstName(), representation.getEmail()); } @@ -286,6 +280,11 @@ public void enableUserById(String userId) { UserRepresentation representation = userResource.toRepresentation(); representation.setEnabled(true); userResource.update(representation); + SubsystemUserUpdate subsystemUserUpdate = getSubsystemUserUpdate(userId); + subsystemUserUpdate.setActive(true); + natsSenderService.publishMessageToJetStream(HDEvent.ENABLE_USER, subsystemUserUpdate); + UserEntity userEntity = userRepository.getByIdOrAuthId(userId); + synchronizeContextRolesWithSubsystems(userEntity); emailNotificationService.notifyAboutUserActivation(representation.getFirstName(), representation.getEmail()); } diff --git a/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowCreateUserConsumer.java b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowCreateUserConsumer.java index 65a06ff0..b16809e4 100644 --- a/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowCreateUserConsumer.java +++ b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowCreateUserConsumer.java @@ -44,6 +44,7 @@ import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; import static ch.bedag.dap.hellodata.commons.sidecars.events.HDEvent.CREATE_USER; +import static ch.bedag.dap.hellodata.sidecars.airflow.service.user.AirflowUserUtil.toAirflowUser; @Log4j2 @Service @@ -54,17 +55,7 @@ public class AirflowCreateUserConsumer { private final AirflowUserResourceProviderService userResourceProviderService; private final AirflowClientProvider airflowClientProvider; - @NotNull - private static AirflowUser toAirflowUser(SubsystemUserUpdate supersetUserCreate) { - AirflowUser airflowUser = new AirflowUser(); - airflowUser.setEmail(supersetUserCreate.getEmail()); - airflowUser.setRoles(new ArrayList<>()); // Default User-Roles are defined in Airflow-Config - airflowUser.setUsername(supersetUserCreate.getUsername()); - airflowUser.setFirstName(supersetUserCreate.getFirstName()); - airflowUser.setLastName(supersetUserCreate.getLastName()); - airflowUser.setPassword(supersetUserCreate.getUsername()); // Login will be handled by Keycloak - return airflowUser; - } + @SuppressWarnings("unused") @JetStreamSubscribe(event = CREATE_USER) diff --git a/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowDisableUserConsumer.java b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowDisableUserConsumer.java new file mode 100644 index 00000000..0e199c64 --- /dev/null +++ b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowDisableUserConsumer.java @@ -0,0 +1,72 @@ +package ch.bedag.dap.hellodata.sidecars.airflow.service.user; + +import ch.bedag.dap.hellodata.commons.nats.annotation.JetStreamSubscribe; +import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUserUpdate; +import ch.bedag.dap.hellodata.sidecars.airflow.client.AirflowClient; +import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowRole; +import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowUserResponse; +import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowUsersResponse; +import ch.bedag.dap.hellodata.sidecars.airflow.service.provider.AirflowClientProvider; +import ch.bedag.dap.hellodata.sidecars.airflow.service.resource.AirflowUserResourceProviderService; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import static ch.bedag.dap.hellodata.commons.sidecars.events.HDEvent.DISABLE_USER; +import static ch.bedag.dap.hellodata.sidecars.airflow.service.user.AirflowUserUtil.*; + +@Log4j2 +@Service +@RequiredArgsConstructor +@SuppressWarnings("java:S3516") +public class AirflowDisableUserConsumer { + + private final AirflowUserResourceProviderService userResourceProviderService; + private final AirflowClientProvider airflowClientProvider; + private final AirflowUserResourceProviderService airflowUserResourceProviderService; + + + @SuppressWarnings("unused") + @JetStreamSubscribe(event = DISABLE_USER) + public CompletableFuture disableUser(SubsystemUserUpdate supersetUserUpdate) { + try { + log.info("------- Received airflow user disable request {}", supersetUserUpdate); + + AirflowClient airflowClient = airflowClientProvider.getAirflowClientInstance(); + AirflowUsersResponse users = airflowClient.users(); + List allAirflowRoles = CollectionUtils.emptyIfNull(airflowClient.roles().getRoles()).stream().toList(); + + // Airflow only allows unique username and email, so we make sure there is nobody with either of these already existing, before creating a new one + Optional userResult = users.getUsers() + .stream() + .filter(user -> user.getEmail().equalsIgnoreCase(supersetUserUpdate.getEmail()) || + user.getUsername().equalsIgnoreCase(supersetUserUpdate.getUsername())) + .findFirst(); + + if (userResult.isPresent()) { + AirflowUserResponse airflowUser = userResult.get(); + removeRoleFromUser(airflowUser, ADMIN_ROLE_NAME, allAirflowRoles); + removeRoleFromUser(airflowUser, VIEWER_ROLE_NAME, allAirflowRoles); + removeRoleFromUser(airflowUser, AF_OPERATOR_ROLE_NAME, allAirflowRoles); + removeAllDataDomainRolesFromUser(airflowUser); + addRoleToUser(airflowUser, PUBLIC_ROLE_NAME, allAirflowRoles); + updateUser(airflowUser, airflowClient, airflowUserResourceProviderService); + userResourceProviderService.publishUsers(); + log.info("User with email: {} disabled", supersetUserUpdate.getEmail()); + } else { + log.warn("User with email: {} not found", supersetUserUpdate.getEmail()); + } + } catch (URISyntaxException | IOException e) { + log.error("Could not disable user {}", supersetUserUpdate.getEmail(), e); + } + return null;//NOSONAR + } + +} diff --git a/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserContextRoleConsumer.java b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserContextRoleConsumer.java index 373cfe56..ea946c9d 100644 --- a/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserContextRoleConsumer.java +++ b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserContextRoleConsumer.java @@ -32,35 +32,30 @@ import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.UserContextRoleUpdate; import ch.bedag.dap.hellodata.sidecars.airflow.client.AirflowClient; import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowRole; -import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowUser; import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowUserResponse; -import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowUserRole; -import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.AirflowUserRolesUpdate; import ch.bedag.dap.hellodata.sidecars.airflow.service.provider.AirflowClientProvider; import ch.bedag.dap.hellodata.sidecars.airflow.service.resource.AirflowRoleResourceProviderService; import ch.bedag.dap.hellodata.sidecars.airflow.service.resource.AirflowUserResourceProviderService; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import lombok.AllArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.collections4.CollectionUtils; -import org.springframework.stereotype.Service; + import static ch.bedag.dap.hellodata.commons.sidecars.events.HDEvent.UPDATE_USER_CONTEXT_ROLE; +import static ch.bedag.dap.hellodata.sidecars.airflow.service.user.AirflowUserUtil.*; @Log4j2 @Service @AllArgsConstructor public class AirflowUserContextRoleConsumer { - private static final String DATA_DOMAIN_ROLE_PREFIX = "DD_"; - private static final String PUBLIC_ROLE_NAME = "Public"; - private static final String VIEWER_ROLE_NAME = "Viewer"; - private static final String ADMIN_ROLE_NAME = "Admin"; - private static final String AF_OPERATOR_ROLE_NAME = "AF_OPERATOR"; + private final AirflowClientProvider airflowClientProvider; private final AirflowRoleResourceProviderService airflowRoleResourceProviderService; private final AirflowUserResourceProviderService airflowUserResourceProviderService; @@ -89,7 +84,7 @@ public CompletableFuture subscribe(UserContextRoleUpdate userContextRoleUp } else { removeAllDataDomainRolesFromUser(airflowUser); leavePublicRoleIfNoneOthersSet(airflowUser, allAirflowRoles); - updateUser(airflowUser, airflowClient); + updateUser(airflowUser, airflowClient, airflowUserResourceProviderService); } } catch (URISyntaxException | IOException e) { log.error("Could not update user {}", userContextRoleUpdate.getEmail(), e); @@ -98,25 +93,6 @@ public CompletableFuture subscribe(UserContextRoleUpdate userContextRoleUp return null; } - private AirflowUserResponse getAirflowUser(UserContextRoleUpdate userContextRoleUpdate, AirflowClient airflowClient) throws URISyntaxException, IOException { - List usersByEmail = - airflowClient.users().getUsers().stream().filter(user -> userContextRoleUpdate.getEmail().equalsIgnoreCase(user.getEmail()) && userContextRoleUpdate.getUsername().equalsIgnoreCase(user.getUsername())).toList(); - if (CollectionUtils.isNotEmpty(usersByEmail) && usersByEmail.size() > 1) { - log.warn("[Found more than one user by an email] --- {} has usernames: [{}]", userContextRoleUpdate.getEmail(), - usersByEmail.stream().map(AirflowUser::getUsername).collect(Collectors.joining(","))); - for (AirflowUserResponse airflowUserResponse : usersByEmail) { - if (!airflowUserResponse.getEmail().equalsIgnoreCase(airflowUserResponse.getUsername())) { - log.warn("[Found more than one user by an email] --- returning user with username != email"); - return airflowUserResponse; - } - } - } - if (CollectionUtils.isEmpty(usersByEmail)) { - return null; - } - return usersByEmail.get(0); - } - private void addOrRemoveAdminRole(UserContextRoleUpdate userContextRoleUpdate, List allAirflowRoles, AirflowUserResponse airflowUser) { if (userContextRoleUpdate.getContextRoles().stream().anyMatch(contextRole -> contextRole.getRoleName() == HdRoleName.HELLODATA_ADMIN)) { addRoleToUser(airflowUser, ADMIN_ROLE_NAME, allAirflowRoles); @@ -154,7 +130,7 @@ private void updateBusinessContextRoleForUser(AirflowUserResponse airflowUser, L } removeRoleFromUser(airflowUser, VIEWER_ROLE_NAME, allAirflowRoles); leavePublicRoleIfNoneOthersSet(airflowUser, allAirflowRoles); - updateUser(airflowUser, airflowClient); + updateUser(airflowUser, airflowClient, airflowUserResourceProviderService); } private void leavePublicRoleIfNoneOthersSet(AirflowUserResponse airflowUser, List allAirflowRoles) { @@ -166,58 +142,4 @@ private void leavePublicRoleIfNoneOthersSet(AirflowUserResponse airflowUser, Lis } } - private void updateUser(AirflowUserResponse airflowUser, AirflowClient airflowClient) throws IOException, URISyntaxException { - if (airflowUser == null) { - return; - } - AirflowUserRolesUpdate airflowUserRolesUpdate = new AirflowUserRolesUpdate(); - airflowUserRolesUpdate.setRoles(airflowUser.getRoles()); - airflowUserRolesUpdate.setEmail(airflowUser.getEmail()); - airflowUserRolesUpdate.setUsername(airflowUser.getUsername()); - airflowUserRolesUpdate.setPassword(airflowUser.getPassword() == null ? "" : airflowUser.getPassword()); - airflowUserRolesUpdate.setFirstName(airflowUser.getFirstName()); - airflowUserRolesUpdate.setLastName(airflowUser.getLastName()); - airflowClient.updateUser(airflowUserRolesUpdate, airflowUser.getUsername()); - airflowUserResourceProviderService.publishUsers(); - } - - private void removeAllDataDomainRolesFromUser(AirflowUser airflowUser) { - if (airflowUser == null) { - return; - } - List airflowUserRoles = CollectionUtils.emptyIfNull(airflowUser.getRoles()) - .stream() - .filter(airflowRole -> !airflowRole.getName().startsWith(DATA_DOMAIN_ROLE_PREFIX)) - .filter(airflowRole -> !airflowRole.getName().equalsIgnoreCase(AF_OPERATOR_ROLE_NAME)) - .toList(); - airflowUser.setRoles(new ArrayList<>(airflowUserRoles)); - } - - private void addRoleToUser(AirflowUserResponse airflowUser, String name, List allAirflowRoles) { - Optional roleResult = CollectionUtils.emptyIfNull(allAirflowRoles) - .stream() - .filter(airflowRole -> airflowRole.getName().equalsIgnoreCase(name)) - .map(airflowRole -> new AirflowUserRole(airflowRole.getName())) - .findFirst(); - if (roleResult.isPresent()) { - AirflowUserRole airflowUserRoleToBeAdded = roleResult.get(); - if (!CollectionUtils.emptyIfNull(airflowUser.getRoles()).contains(airflowUserRoleToBeAdded)) { - airflowUser.getRoles().add(airflowUserRoleToBeAdded); - } - } - } - - private void removeRoleFromUser(AirflowUserResponse airflowUser, String name, List allAirflowRoles) { - Optional roleResult = CollectionUtils.emptyIfNull(allAirflowRoles) - .stream() - .filter(airflowUserRole -> airflowUserRole.getName().equalsIgnoreCase(name)) - .map(airflowRole -> new AirflowUserRole(airflowRole.getName())) - .findFirst(); - if (roleResult.isPresent()) { - AirflowUserRole airflowUserRoleToBeRemoved = roleResult.get(); - if (CollectionUtils.emptyIfNull(airflowUser.getRoles()).contains(airflowUserRoleToBeRemoved)) { - airflowUser.getRoles().remove(airflowUserRoleToBeRemoved); - } - } - } } diff --git a/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserUtil.java b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserUtil.java new file mode 100644 index 00000000..92e79a01 --- /dev/null +++ b/hello-data-sidecars/hello-data-sidecar-airflow/src/main/java/ch/bedag/dap/hellodata/sidecars/airflow/service/user/AirflowUserUtil.java @@ -0,0 +1,114 @@ +package ch.bedag.dap.hellodata.sidecars.airflow.service.user; + +import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUserUpdate; +import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.UserContextRoleUpdate; +import ch.bedag.dap.hellodata.sidecars.airflow.client.AirflowClient; +import ch.bedag.dap.hellodata.sidecars.airflow.client.user.response.*; +import ch.bedag.dap.hellodata.sidecars.airflow.service.resource.AirflowUserResourceProviderService; +import lombok.experimental.UtilityClass; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.collections4.CollectionUtils; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Log4j2 +@UtilityClass +public class AirflowUserUtil { + + public static final String PUBLIC_ROLE_NAME = "Public"; + public static final String DATA_DOMAIN_ROLE_PREFIX = "DD_"; + public static final String VIEWER_ROLE_NAME = "Viewer"; + public static final String ADMIN_ROLE_NAME = "Admin"; + public static final String AF_OPERATOR_ROLE_NAME = "AF_OPERATOR"; + + public static AirflowUser toAirflowUser(SubsystemUserUpdate supersetUserCreate) { + AirflowUser airflowUser = new AirflowUser(); + airflowUser.setEmail(supersetUserCreate.getEmail()); + airflowUser.setRoles(new ArrayList<>()); // Default User-Roles are defined in Airflow-Config + airflowUser.setUsername(supersetUserCreate.getUsername()); + airflowUser.setFirstName(supersetUserCreate.getFirstName()); + airflowUser.setLastName(supersetUserCreate.getLastName()); + airflowUser.setPassword(supersetUserCreate.getUsername()); // Login will be handled by Keycloak + return airflowUser; + } + + public static void addRoleToUser(AirflowUserResponse airflowUser, String roleName, List allAirflowRoles) { + Optional roleResult = CollectionUtils.emptyIfNull(allAirflowRoles) + .stream() + .filter(airflowRole -> airflowRole.getName().equalsIgnoreCase(roleName)) + .map(airflowRole -> new AirflowUserRole(airflowRole.getName())) + .findFirst(); + if (roleResult.isPresent()) { + AirflowUserRole airflowUserRoleToBeAdded = roleResult.get(); + if (!CollectionUtils.emptyIfNull(airflowUser.getRoles()).contains(airflowUserRoleToBeAdded)) { + airflowUser.getRoles().add(airflowUserRoleToBeAdded); + } + } + } + + public void removeRoleFromUser(AirflowUserResponse airflowUser, String name, List allAirflowRoles) { + Optional roleResult = CollectionUtils.emptyIfNull(allAirflowRoles) + .stream() + .filter(airflowUserRole -> airflowUserRole.getName().equalsIgnoreCase(name)) + .map(airflowRole -> new AirflowUserRole(airflowRole.getName())) + .findFirst(); + if (roleResult.isPresent()) { + AirflowUserRole airflowUserRoleToBeRemoved = roleResult.get(); + if (CollectionUtils.emptyIfNull(airflowUser.getRoles()).contains(airflowUserRoleToBeRemoved)) { + airflowUser.getRoles().remove(airflowUserRoleToBeRemoved); + } + } + } + + public static void updateUser(AirflowUserResponse airflowUser, AirflowClient airflowClient, AirflowUserResourceProviderService airflowUserResourceProviderService) throws IOException, URISyntaxException { + if (airflowUser == null) { + return; + } + AirflowUserRolesUpdate airflowUserRolesUpdate = new AirflowUserRolesUpdate(); + airflowUserRolesUpdate.setRoles(airflowUser.getRoles()); + airflowUserRolesUpdate.setEmail(airflowUser.getEmail()); + airflowUserRolesUpdate.setUsername(airflowUser.getUsername()); + airflowUserRolesUpdate.setPassword(airflowUser.getPassword() == null ? "" : airflowUser.getPassword()); + airflowUserRolesUpdate.setFirstName(airflowUser.getFirstName()); + airflowUserRolesUpdate.setLastName(airflowUser.getLastName()); + airflowClient.updateUser(airflowUserRolesUpdate, airflowUser.getUsername()); + airflowUserResourceProviderService.publishUsers(); + } + + public static AirflowUserResponse getAirflowUser(UserContextRoleUpdate userContextRoleUpdate, AirflowClient airflowClient) throws URISyntaxException, IOException { + List usersByEmail = + airflowClient.users().getUsers().stream().filter(user -> userContextRoleUpdate.getEmail().equalsIgnoreCase(user.getEmail()) && userContextRoleUpdate.getUsername().equalsIgnoreCase(user.getUsername())).toList(); + if (CollectionUtils.isNotEmpty(usersByEmail) && usersByEmail.size() > 1) { + log.warn("[Found more than one user by an email] --- {} has usernames: [{}]", userContextRoleUpdate.getEmail(), + usersByEmail.stream().map(AirflowUser::getUsername).collect(Collectors.joining(","))); + for (AirflowUserResponse airflowUserResponse : usersByEmail) { + if (!airflowUserResponse.getEmail().equalsIgnoreCase(airflowUserResponse.getUsername())) { + log.warn("[Found more than one user by an email] --- returning user with username != email"); + return airflowUserResponse; + } + } + } + if (CollectionUtils.isEmpty(usersByEmail)) { + return null; + } + return usersByEmail.get(0); + } + + public static void removeAllDataDomainRolesFromUser(AirflowUser airflowUser) { + if (airflowUser == null) { + return; + } + List airflowUserRoles = CollectionUtils.emptyIfNull(airflowUser.getRoles()) + .stream() + .filter(airflowRole -> !airflowRole.getName().startsWith(DATA_DOMAIN_ROLE_PREFIX)) + .filter(airflowRole -> !airflowRole.getName().equalsIgnoreCase(AF_OPERATOR_ROLE_NAME)) + .toList(); + airflowUser.setRoles(new ArrayList<>(airflowUserRoles)); + } + +} diff --git a/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java b/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java new file mode 100644 index 00000000..7220d0eb --- /dev/null +++ b/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java @@ -0,0 +1,53 @@ +package ch.bedag.dap.hellodata.sidecars.superset.service.user; + +import ch.bedag.dap.hellodata.commons.nats.annotation.JetStreamSubscribe; +import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUser; +import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUserDelete; +import ch.bedag.dap.hellodata.sidecars.superset.client.SupersetClient; +import ch.bedag.dap.hellodata.sidecars.superset.client.data.SupersetUsersResponse; +import ch.bedag.dap.hellodata.sidecars.superset.service.client.SupersetClientProvider; +import ch.bedag.dap.hellodata.sidecars.superset.service.resource.UserResourceProviderService; +import ch.bedag.dap.hellodata.sidecars.superset.service.user.data.SupersetUserActiveUpdate; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import static ch.bedag.dap.hellodata.commons.sidecars.events.HDEvent.DISABLE_USER; + +@Log4j2 +@Service +@RequiredArgsConstructor +@SuppressWarnings("java:S3516") +public class SupersetDisableUserConsumer { + + private final UserResourceProviderService userResourceProviderService; + private final SupersetClientProvider supersetClientProvider; + + @SuppressWarnings("unused") + @JetStreamSubscribe(event = DISABLE_USER) + public CompletableFuture disableUser(SubsystemUserDelete subsystemUserDelete) { + try { + log.info("------- Received superset user deletion request {}", subsystemUserDelete); + SupersetClient supersetClient = supersetClientProvider.getSupersetClientInstance(); + SupersetUsersResponse users = supersetClient.users(); + Optional supersetUserResult = users.getResult().stream().filter(user -> user.getEmail().equalsIgnoreCase(subsystemUserDelete.getEmail())).findFirst(); + if (supersetUserResult.isEmpty()) { + log.info("User {} doesn't exist in instance, omitting deletion", subsystemUserDelete.getEmail()); + return null;//NOSONAR + } + log.info("Going to delete user with email: {}", subsystemUserDelete.getEmail()); + SupersetUserActiveUpdate supersetUserActiveUpdate = new SupersetUserActiveUpdate(); + supersetUserActiveUpdate.setActive(false); + supersetClient.updateUsersActiveFlag(supersetUserActiveUpdate, supersetUserResult.get().getId()); + userResourceProviderService.publishUsers(); + } catch (URISyntaxException | IOException e) { + log.error("Could not delete user {}", subsystemUserDelete.getEmail(), e); + } + return null;//NOSONAR + } +} From d4be873b574e05f67ce65a94785c7573a397ccd5 Mon Sep 17 00:00:00 2001 From: Slawomir Wieczorek Date: Mon, 16 Sep 2024 11:58:26 +0200 Subject: [PATCH 2/3] HELLODATA-1689 - enable/disable superset user --- ...r.java => SupersetCreateUserConsumer.java} | 64 +++++++++++++------ .../user/SupersetDisableUserConsumer.java | 13 ++-- 2 files changed, 53 insertions(+), 24 deletions(-) rename hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/{SuperserCreateUserConsumer.java => SupersetCreateUserConsumer.java} (65%) diff --git a/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SuperserCreateUserConsumer.java b/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetCreateUserConsumer.java similarity index 65% rename from hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SuperserCreateUserConsumer.java rename to hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetCreateUserConsumer.java index e93ef0e7..dac02f02 100644 --- a/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SuperserCreateUserConsumer.java +++ b/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetCreateUserConsumer.java @@ -36,22 +36,25 @@ import ch.bedag.dap.hellodata.sidecars.superset.service.resource.UserResourceProviderService; import ch.bedag.dap.hellodata.sidecars.superset.service.user.data.SupersetUserActiveUpdate; import ch.bedag.dap.hellodata.sidecars.superset.service.user.data.SupersetUserRolesUpdate; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + import java.io.IOException; import java.net.URISyntaxException; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; + import static ch.bedag.dap.hellodata.commons.sidecars.events.HDEvent.CREATE_USER; +import static ch.bedag.dap.hellodata.commons.sidecars.events.HDEvent.ENABLE_USER; import static ch.bedag.dap.hellodata.sidecars.superset.service.user.RoleUtil.PUBLIC_ROLE_NAME; @Log4j2 @Service @SuppressWarnings("java:S3516") @RequiredArgsConstructor -public class SuperserCreateUserConsumer { +public class SupersetCreateUserConsumer { private final UserResourceProviderService userResourceProviderService; private final SupersetClientProvider supersetClientProvider; @@ -63,14 +66,15 @@ public CompletableFuture createUser(SubsystemUserUpdate supersetUserCreate log.info("------- Received superset user creation request {}", supersetUserCreate); SupersetClient supersetClient = supersetClientProvider.getSupersetClientInstance(); Optional aPublicRoleId = supersetClient.roles() - .getResult() - .stream() - .filter(supersetRole -> supersetRole.getName().equalsIgnoreCase(PUBLIC_ROLE_NAME)) - .map(SupersetRole::getId) - .findFirst(); + .getResult() + .stream() + .filter(supersetRole -> supersetRole.getName().equalsIgnoreCase(PUBLIC_ROLE_NAME)) + .map(SupersetRole::getId) + .findFirst(); SupersetUsersResponse users = supersetClient.users(); Optional supersetUserResult = users.getResult().stream().filter(user -> user.getEmail().equalsIgnoreCase(supersetUserCreate.getEmail())).findFirst(); if (supersetUserResult.isPresent()) { + log.info("User {} already exists in instance, omitting creation", supersetUserCreate.getEmail()); enableUser(supersetUserCreate, supersetUserResult.get(), supersetClient, aPublicRoleId); } else { createUser(supersetUserCreate, aPublicRoleId, supersetClient); @@ -82,23 +86,38 @@ public CompletableFuture createUser(SubsystemUserUpdate supersetUserCreate return null;//NOSONAR } - private static void createUser(SubsystemUserUpdate supersetUserCreate, Optional aPublicRoleId, SupersetClient supersetClient) throws URISyntaxException, IOException { + @SuppressWarnings("unused") + @JetStreamSubscribe(event = ENABLE_USER) + public CompletableFuture enableUser(SubsystemUserUpdate subsystemUserUpdate) { + try { + log.info("------- Received superset user enable request {}", subsystemUserUpdate); + SupersetClient supersetClient = supersetClientProvider.getSupersetClientInstance(); + SupersetUsersResponse users = supersetClient.users(); + Optional supersetUserResult = users.getResult().stream().filter(user -> user.getEmail().equalsIgnoreCase(subsystemUserUpdate.getEmail())).findFirst(); + if (supersetUserResult.isPresent()) { + enableUser(subsystemUserUpdate, supersetUserResult.get(), supersetClient); + } else { + log.warn("Couldn't find user {}", subsystemUserUpdate.getEmail()); + } + userResourceProviderService.publishUsers(); + } catch (URISyntaxException | IOException e) { + log.error("Could not enable user {}", subsystemUserUpdate.getEmail(), e); + } + return null;//NOSONAR + } + + private void createUser(SubsystemUserUpdate supersetUserCreate, Optional aPublicRoleId, SupersetClient supersetClient) throws URISyntaxException, IOException { aPublicRoleId.ifPresentOrElse(roleId -> supersetUserCreate.setRoles(List.of(roleId)), - () -> log.warn("Couldn't find a Public role to set for created/enabled user {}", supersetUserCreate.getEmail())); + () -> log.warn("Couldn't find a Public role to set for created/enabled user {}", supersetUserCreate.getEmail())); // logging with keycloak changes the password in the superset DB supersetUserCreate.setPassword(supersetUserCreate.getFirstName()); supersetClient.createUser(supersetUserCreate); } - private static void enableUser(SubsystemUserUpdate supersetUserCreate, SubsystemUser supersetUser, SupersetClient supersetClient, Optional aPublicRoleId) throws + private void enableUser(SubsystemUserUpdate supersetUserCreate, SubsystemUser supersetUser, SupersetClient supersetClient, Optional aPublicRoleId) throws URISyntaxException, IOException { - log.info("User {} already exists in instance, omitting creation", supersetUserCreate.getEmail()); if (!supersetUser.isActive()) { - log.info("User {} has been activated", supersetUserCreate.getEmail()); - SupersetUserActiveUpdate supersetUserActiveUpdate = new SupersetUserActiveUpdate(); - supersetUserActiveUpdate.setActive(true); - int supersetUserId = supersetUser.getId(); - supersetClient.updateUsersActiveFlag(supersetUserActiveUpdate, supersetUserId); + int supersetUserId = enableUser(supersetUserCreate, supersetUser, supersetClient); if (aPublicRoleId.isPresent()) { int roleId = aPublicRoleId.get(); SupersetUserRolesUpdate supersetUserRolesUpdate = new SupersetUserRolesUpdate(); @@ -109,4 +128,13 @@ private static void enableUser(SubsystemUserUpdate supersetUserCreate, Subsystem } } } + + private int enableUser(SubsystemUserUpdate supersetUserCreate, SubsystemUser supersetUser, SupersetClient supersetClient) throws URISyntaxException, IOException { + log.info("User {} has been activated", supersetUserCreate.getEmail()); + SupersetUserActiveUpdate supersetUserActiveUpdate = new SupersetUserActiveUpdate(); + supersetUserActiveUpdate.setActive(true); + int supersetUserId = supersetUser.getId(); + supersetClient.updateUsersActiveFlag(supersetUserActiveUpdate, supersetUserId); + return supersetUserId; + } } diff --git a/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java b/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java index 7220d0eb..d5a25df1 100644 --- a/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java +++ b/hello-data-sidecars/hello-data-sidecar-superset/src/main/java/ch/bedag/dap/hellodata/sidecars/superset/service/user/SupersetDisableUserConsumer.java @@ -3,6 +3,7 @@ import ch.bedag.dap.hellodata.commons.nats.annotation.JetStreamSubscribe; import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUser; import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUserDelete; +import ch.bedag.dap.hellodata.commons.sidecars.resources.v1.user.data.SubsystemUserUpdate; import ch.bedag.dap.hellodata.sidecars.superset.client.SupersetClient; import ch.bedag.dap.hellodata.sidecars.superset.client.data.SupersetUsersResponse; import ch.bedag.dap.hellodata.sidecars.superset.service.client.SupersetClientProvider; @@ -30,23 +31,23 @@ public class SupersetDisableUserConsumer { @SuppressWarnings("unused") @JetStreamSubscribe(event = DISABLE_USER) - public CompletableFuture disableUser(SubsystemUserDelete subsystemUserDelete) { + public CompletableFuture disableUser(SubsystemUserUpdate subsystemUserUpdate) { try { - log.info("------- Received superset user deletion request {}", subsystemUserDelete); + log.info("------- Received superset user disable request {}", subsystemUserUpdate); SupersetClient supersetClient = supersetClientProvider.getSupersetClientInstance(); SupersetUsersResponse users = supersetClient.users(); - Optional supersetUserResult = users.getResult().stream().filter(user -> user.getEmail().equalsIgnoreCase(subsystemUserDelete.getEmail())).findFirst(); + Optional supersetUserResult = users.getResult().stream().filter(user -> user.getEmail().equalsIgnoreCase(subsystemUserUpdate.getEmail())).findFirst(); if (supersetUserResult.isEmpty()) { - log.info("User {} doesn't exist in instance, omitting deletion", subsystemUserDelete.getEmail()); + log.info("User {} doesn't exist in instance, omitting disable action", subsystemUserUpdate.getEmail()); return null;//NOSONAR } - log.info("Going to delete user with email: {}", subsystemUserDelete.getEmail()); SupersetUserActiveUpdate supersetUserActiveUpdate = new SupersetUserActiveUpdate(); supersetUserActiveUpdate.setActive(false); supersetClient.updateUsersActiveFlag(supersetUserActiveUpdate, supersetUserResult.get().getId()); userResourceProviderService.publishUsers(); + log.info("User with email: {} disabled", subsystemUserUpdate.getEmail()); } catch (URISyntaxException | IOException e) { - log.error("Could not delete user {}", subsystemUserDelete.getEmail(), e); + log.error("Could not disable user {}", subsystemUserUpdate.getEmail(), e); } return null;//NOSONAR } From 9605e6acdf12c47e89b2a95c9e102ccfbf7bffb1 Mon Sep 17 00:00:00 2001 From: Slawomir Wieczorek Date: Mon, 16 Sep 2024 12:41:59 +0200 Subject: [PATCH 3/3] HELLODATA-1689 - fix tests --- .../portal/user/service/UserService.java | 44 ++++++++++--------- .../portal/user/service/UserServiceTest.java | 27 +++++------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java b/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java index 2ed35a9c..2eb9060e 100644 --- a/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java +++ b/hello-data-portal/hello-data-portal-api/src/main/java/ch/bedag/dap/hellodata/portal/user/service/UserService.java @@ -185,11 +185,11 @@ public List getAllUsers() { } } return userRepresentationList.stream() - .filter(userRepresentation -> userRepresentation.getEmail() != null) - .map(userRepresentation -> modelMapper.map(userRepresentation, UserDto.class)) - .map(userDto -> fetchAdditionalDataFromPortal(userDto, - allPortalUsers.stream().filter(userEntity -> idEquals(userDto, userEntity)).findFirst())) - .toList(); + .filter(userRepresentation -> userRepresentation.getEmail() != null) + .map(userRepresentation -> modelMapper.map(userRepresentation, UserDto.class)) + .map(userDto -> fetchAdditionalDataFromPortal(userDto, + allPortalUsers.stream().filter(userEntity -> idEquals(userDto, userEntity)).findFirst())) + .toList(); } @Nullable @@ -246,8 +246,7 @@ public void createUserInSubsystems(String userId) { natsSenderService.publishMessageToJetStream(HDEvent.CREATE_USER, createUser); } - private SubsystemUserUpdate getSubsystemUserUpdate(String userId) { - UserRepresentation representation = getUserRepresentation(userId); + private SubsystemUserUpdate getSubsystemUserUpdate(UserRepresentation representation) { SubsystemUserUpdate createUser = new SubsystemUserUpdate(); createUser.setFirstName(representation.getFirstName()); createUser.setLastName(representation.getLastName()); @@ -257,6 +256,11 @@ private SubsystemUserUpdate getSubsystemUserUpdate(String userId) { return createUser; } + private SubsystemUserUpdate getSubsystemUserUpdate(String userId) { + UserRepresentation representation = getUserRepresentation(userId); + return getSubsystemUserUpdate(representation); + } + @Transactional public void disableUserById(String userId) { UUID dbId = UUID.fromString(userId); @@ -266,7 +270,7 @@ public void disableUserById(String userId) { representation.setEnabled(false); userResource.update(representation); userResource.logout(); - SubsystemUserUpdate subsystemUserUpdate = getSubsystemUserUpdate(userId); + SubsystemUserUpdate subsystemUserUpdate = getSubsystemUserUpdate(representation); subsystemUserUpdate.setActive(false); natsSenderService.publishMessageToJetStream(HDEvent.DISABLE_USER, subsystemUserUpdate); emailNotificationService.notifyAboutUserDeactivation(representation.getFirstName(), representation.getEmail()); @@ -280,7 +284,7 @@ public void enableUserById(String userId) { UserRepresentation representation = userResource.toRepresentation(); representation.setEnabled(true); userResource.update(representation); - SubsystemUserUpdate subsystemUserUpdate = getSubsystemUserUpdate(userId); + SubsystemUserUpdate subsystemUserUpdate = getSubsystemUserUpdate(representation); subsystemUserUpdate.setActive(true); natsSenderService.publishMessageToJetStream(HDEvent.ENABLE_USER, subsystemUserUpdate); UserEntity userEntity = userRepository.getByIdOrAuthId(userId); @@ -349,13 +353,13 @@ private void updateContextRoles(UUID userId, UpdateContextRolesForUserDto update private void setRoleForAllRemainingDataDomainsToNone(UpdateContextRolesForUserDto updateContextRolesForUserDto, UserEntity userEntity) { List allDataDomains = contextRepository.findAllByTypeIn(List.of(HdContextType.DATA_DOMAIN)); List ddDomainsWithoutRoleForUser = allDataDomains.stream() - .filter(availableDD -> updateContextRolesForUserDto.getDataDomainRoles() - .stream() - .noneMatch(ddRole -> ddRole.getContext() - .getContextKey() - .equalsIgnoreCase( - availableDD.getContextKey()))) - .toList(); + .filter(availableDD -> updateContextRolesForUserDto.getDataDomainRoles() + .stream() + .noneMatch(ddRole -> ddRole.getContext() + .getContextKey() + .equalsIgnoreCase( + availableDD.getContextKey()))) + .toList(); if (!ddDomainsWithoutRoleForUser.isEmpty()) { Optional first = roleService.getAll().stream().filter(roleDto -> HdRoleName.NONE.name().equalsIgnoreCase(roleDto.getName())).findFirst(); if (first.isPresent()) { @@ -557,10 +561,10 @@ public Set getCurrentUserDataDomainRolesWithoutNone() { } Optional userEntity = Optional.of(getUserEntity(currentUserId)); return userEntity.map(user -> user.getContextRoles() - .stream() - .filter(userContextRoleEntity -> HdContextType.DATA_DOMAIN.equals(userContextRoleEntity.getRole().getContextType())) - .filter(userContextRoleEntity -> !HdRoleName.NONE.equals(userContextRoleEntity.getRole().getName())) - .collect(Collectors.toSet())).orElse(Collections.emptySet()); + .stream() + .filter(userContextRoleEntity -> HdContextType.DATA_DOMAIN.equals(userContextRoleEntity.getRole().getContextType())) + .filter(userContextRoleEntity -> !HdRoleName.NONE.equals(userContextRoleEntity.getRole().getName())) + .collect(Collectors.toSet())).orElse(Collections.emptySet()); } @Transactional(readOnly = true) diff --git a/hello-data-portal/hello-data-portal-api/src/test/java/ch/bedag/dap/hellodata/portal/user/service/UserServiceTest.java b/hello-data-portal/hello-data-portal-api/src/test/java/ch/bedag/dap/hellodata/portal/user/service/UserServiceTest.java index 185881c8..0f3e4132 100644 --- a/hello-data-portal/hello-data-portal-api/src/test/java/ch/bedag/dap/hellodata/portal/user/service/UserServiceTest.java +++ b/hello-data-portal/hello-data-portal-api/src/test/java/ch/bedag/dap/hellodata/portal/user/service/UserServiceTest.java @@ -41,32 +41,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.nats.client.Connection; import jakarta.ws.rs.NotFoundException; -import java.util.Collections; -import java.util.List; -import java.util.UUID; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.representations.idm.UserRepresentation; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.Spy; +import org.mockito.*; import org.mockito.junit.jupiter.MockitoExtension; import org.modelmapper.ModelMapper; import org.springframework.http.HttpStatus; import org.springframework.web.server.ResponseStatusException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @Log4j2 @SuppressWarnings("unused") @@ -150,6 +142,7 @@ public void testSyncAllUsers() { when(userRepresentation.getFirstName()).thenReturn(firstName); when(userRepresentation.getLastName()).thenReturn(lastName); when(userRepresentation.getId()).thenReturn(createdUserId); + when(userRepresentation.isEnabled()).thenReturn(true); when(keycloakService.getUserRepresentationById(any())).thenReturn(userRepresentation); when(userRepository.existsByIdOrAuthId(any(UUID.class), any(String.class))).thenReturn(false); when(userRepository.findAll()).thenReturn(List.of(userEntity)); @@ -220,6 +213,7 @@ public void testDisableUserById_UserFound() { UserResource userResourceMock = mock(UserResource.class, Mockito.RETURNS_DEEP_STUBS); UserRepresentation userRepresentation = new UserRepresentation(); userRepresentation.setEnabled(true); + userRepresentation.setEmail("some_email@example.com"); UserEntity userEntity = new UserEntity(); userEntity.setId(uuid); @@ -260,6 +254,7 @@ public void testEnableUserById_UserFound() { UserResource userResourceMock = mock(UserResource.class, Mockito.RETURNS_DEEP_STUBS); UserRepresentation userRepresentation = new UserRepresentation(); userRepresentation.setEnabled(false); + userRepresentation.setEmail("some_email@example.com"); UserEntity userEntity = new UserEntity(); userEntity.setId(uuid); when(userRepository.getByIdOrAuthId(any(String.class))).thenReturn(userEntity);