From ab993c43afc759dad3435f9bb1fabcaee12c5d5f Mon Sep 17 00:00:00 2001 From: sgiannopoulos Date: Thu, 4 Apr 2024 16:39:40 +0300 Subject: [PATCH] multitenant changes --- .../inbox/InboxRepositoryImpl.java | 5 + ...iesRemovalIntegrationEventHandlerImpl.java | 130 ++++---- ...iesTouchedIntegrationEventHandlerImpl.java | 169 +++++----- ...antRemovalIntegrationEventHandlerImpl.java | 106 +++---- ...antTouchedIntegrationEventHandlerImpl.java | 83 ++--- ...serRemovalIntegrationEventHandlerImpl.java | 132 ++++---- ...serTouchedIntegrationEventHandlerImpl.java | 113 +++---- .../eu/eudat/authorization/Permission.java | 6 + .../inbox/InboxRepositoryImpl.java | 42 ++- .../inbox/IntegrationEventProperties.java | 10 + ...antTouchedIntegrationEventHandlerImpl.java | 2 +- .../storage/StorageFileCleanupTask.java | 5 - .../src/main/resources/config/permissions.yml | 20 +- .../web/src/main/resources/config/queue.yml | 4 +- .../00.01.028_add_ntf_Notification.sql | 4 +- ...00.01.036_add_ntf_NotificationTemplate.sql | 9 +- .../updates/00.01.037_add_ntf_QueueInbox.sql | 2 +- .../updates/00.01.038_add_ntf_QueueOutbox.sql | 2 +- .../00.01.039_add_ntf_InAppNotification.sql | 4 +- .../notification-template.ts | 7 +- ...otification-template-editor.component.html | 20 +- .../notification-template-editor.model.ts | 20 +- .../notification-template-editor.resolver.ts | 5 +- .../notification/web/WebConfiguration.java | 2 +- .../web/config/SecurityConfiguration.java | 4 +- .../web/controllers/PrincipalController.java | 13 - .../web/interceptors/UserInterceptor.java | 103 ------ .../UserInterceptorCacheOptions.java | 10 - .../UserInterceptorCacheService.java | 68 ---- .../tenant/TenantByCodeCacheService.java | 94 +++--- .../scope/tenant/TenantByIdCacheService.java | 91 +++--- .../web/scope/tenant/TenantInterceptor.java | 96 +++--- .../tenant/TenantScopeClaimInterceptor.java | 293 +++++++++--------- .../tenant/TenantScopeHeaderInterceptor.java | 68 ++-- .../scope/tenant/TenantScopeProperties.java | 1 + .../tenant/UserAllowedTenantCacheOptions.java | 2 + .../tenant/UserAllowedTenantCacheService.java | 113 +++---- .../web/scope/user/UserInterceptor.java | 235 +++++--------- .../user/UserInterceptorCacheOptions.java | 20 +- .../user/UserInterceptorCacheService.java | 144 ++++----- .../src/main/resources/config/errors.yml | 8 +- .../src/main/resources/config/idpclaims.yml | 21 +- .../src/main/resources/config/permissions.yml | 56 ++-- .../src/main/resources/config/queue.yml | 3 - .../main/resources/config/tenant-devel.yml | 7 + .../src/main/resources/config/tenant.yml | 3 +- .../authorization/ClaimNames.java | 9 + .../scope/tenant/MultitenancyProperties.java | 13 + .../common/scope/tenant/TenantScope.java | 101 +++--- .../notification/data/LanguageEntity.java | 94 ------ .../data/NotificationTemplateEntity.java | 14 +- .../data/TenantEntityManager.java | 116 +++++++ .../data/TenantScopedEntityManager.java | 97 ------ .../data/tenant/TenantFilterAspect.java | 31 +- .../data/tenant/TenantListener.java | 57 ++-- .../data/tenant/TenantScopedBaseEntity.java | 29 +- .../errorcode/ErrorThesaurusProperties.java | 17 + .../OutboxIntegrationEventConfigurer.java | 7 +- .../inbox/InboxPrincipal.java | 17 +- .../inbox/InboxRepositoryImpl.java | 48 ++- .../inbox/IntegrationEventProperties.java | 10 + .../inbox/notify/NotifyIntegrationEvent.java | 10 - .../NotifyIntegrationEventHandlerImpl.java | 111 +++---- .../TenantRemovalIntegrationEventHandler.java | 1 + ...antRemovalIntegrationEventHandlerImpl.java | 104 +++---- .../TenantTouchedIntegrationEvent.java | 1 + ...antTouchedIntegrationEventHandlerImpl.java | 76 ++--- .../UserRemovalConsistencyPredicates.java | 1 + .../UserRemovalIntegrationEvent.java | 9 - .../UserRemovalIntegrationEventHandler.java | 1 + ...serRemovalIntegrationEventHandlerImpl.java | 126 ++++---- .../UserTouchedIntegrationEvent.java | 190 ++++++++++-- .../UserTouchedIntegrationEventHandler.java | 1 + ...serTouchedIntegrationEventHandlerImpl.java | 111 +++---- .../outbox/OutboxProperties.java | 41 +-- .../outbox/OutboxRepositoryImpl.java | 62 ++-- .../outbox/OutboxService.java | 5 + .../outbox/OutboxServiceImpl.java | 34 ++ .../gr/cite/notification/model/Language.java | 93 ------ .../model/NotificationTemplate.java | 12 +- .../cite/notification/model/TenantUser.java | 98 ++++++ .../model/builder/LanguageBuilder.java | 65 ---- .../builder/NotificationTemplateBuilder.java | 37 +-- .../deleter/InAppNotificationDeleter.java | 11 +- .../model/deleter/NotificationDeleter.java | 6 +- .../deleter/NotificationTemplateDeleter.java | 6 +- .../deleter/TenantConfigurationDeleter.java | 6 +- .../model/deleter/TenantDeleter.java | 6 +- .../model/deleter/TenantUserDeleter.java | 71 +++++ .../model/deleter/UserContactInfoDeleter.java | 6 +- .../model/deleter/UserCredentialDeleter.java | 6 +- .../model/deleter/UserDeleter.java | 6 +- .../persist/NotificationTemplatePersist.java | 12 +- .../notification/query/LanguageQuery.java | 190 ------------ .../query/NotificationTemplateQuery.java | 40 +-- .../notification/query/TenantUserQuery.java | 209 +++++++++++++ .../query/lookup/LanguageLookup.java | 76 ----- .../query/utils/BuildSubQueryInput.java | 90 ++++++ .../query/utils/QueryUtilsService.java | 7 + .../query/utils/QueryUtilsServiceImpl.java | 18 ++ .../InAppNotificationServiceImpl.java | 8 +- .../message/builder/EmailMessageBuilder.java | 12 +- .../message/builder/InAppMessageBuilder.java | 3 +- .../notification/NotificationServiceImpl.java | 6 +- .../NotificationSchedulingService.java | 1 - .../NotificationSchedulingServiceImpl.java | 215 +++++++++---- .../NotificationServiceTemplateImpl.java | 17 +- .../NotificationTemplateService.java | 2 +- .../service/tenant/TenantServiceImpl.java | 7 +- .../TenantConfigurationServiceImpl.java | 6 +- .../service/user/UserServiceImpl.java | 202 +++++++----- ...UserNotificationPreferenceServiceImpl.java | 6 +- 112 files changed, 2639 insertions(+), 2816 deletions(-) delete mode 100644 notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptor.java delete mode 100644 notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheOptions.java delete mode 100644 notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheService.java create mode 100644 notification-service/notification-web/src/main/resources/config/tenant-devel.yml create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/authorization/ClaimNames.java delete mode 100644 notification-service/notification/src/main/java/gr/cite/notification/data/LanguageEntity.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java delete mode 100644 notification-service/notification/src/main/java/gr/cite/notification/data/TenantScopedEntityManager.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxService.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxServiceImpl.java delete mode 100644 notification-service/notification/src/main/java/gr/cite/notification/model/Language.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/model/TenantUser.java delete mode 100644 notification-service/notification/src/main/java/gr/cite/notification/model/builder/LanguageBuilder.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java delete mode 100644 notification-service/notification/src/main/java/gr/cite/notification/query/LanguageQuery.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/query/TenantUserQuery.java delete mode 100644 notification-service/notification/src/main/java/gr/cite/notification/query/lookup/LanguageLookup.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/query/utils/BuildSubQueryInput.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsService.java create mode 100644 notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsServiceImpl.java diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java index 2a06b0eb6..ce2831fd8 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java @@ -4,6 +4,7 @@ import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.fake.FakeRequestScope; import gr.cite.annotation.data.QueueInboxEntity; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.integrationevent.TrackedEvent; import gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval.AnnotationEntitiesRemovalIntegrationEventHandler; import gr.cite.annotation.integrationevent.inbox.annotationentitiestouch.AnnotationEntitiesTouchedIntegrationEventHandler; @@ -278,6 +279,10 @@ public class InboxRepositoryImpl implements InboxRepository { entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); + + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + transaction.begin(); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java index 2629f5134..35214a3d1 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java @@ -32,6 +32,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.util.*; @Component @@ -44,15 +45,26 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann private final ValidatorFactory validatorFactory; - private final ApplicationContext applicationContext; private final QueryFactory queryFactory; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; + private final DeleterFactory deleterFactory; + private final TenantScope tenantScope; - public AnnotationEntitiesRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) { + + public AnnotationEntitiesRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, AuditService auditService, TenantEntityManager tenantEntityManager, DeleterFactory deleterFactory, TenantScope tenantScope) { this.jsonHandlingService = jsonHandlingService; this.validatorFactory = validatorFactory; - this.applicationContext = applicationContext; this.queryFactory = queryFactory; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; + this.deleterFactory = deleterFactory; + this.tenantScope = tenantScope; } @Override @@ -65,83 +77,53 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann this.validatorFactory.validator(AnnotationEntitiesRemovalIntegrationEvent.AnnotationEntitiesRemovalIntegrationEventValidator.class).validateForce(event); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && properties.getTenantId() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(properties.getTenantId(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { // logger.error("missing tenant from event message"); // return EventProcessingStatus.Error; - scope.setTenant(null, scope.getDefaultTenantCode()); - } - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - - ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - - TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); - tenantEntityManager.setEntityManager(entityManager); - tenantEntityManager.disableTenantFilters(); - EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); - List items = entityUserQuery - .entityIds(event.getEntityIds()) - .isActive(IsActive.Active) - .collect(); - - - deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(items); - - entityManager.flush(); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.User_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", event) - )); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + + tenantEntityManager.disableTenantFilters(); + EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); + List items = entityUserQuery + .entityIds(event.getEntityIds()) + .isActive(IsActive.Active) + .collect(); + + + deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(items); + + + auditService.track(AuditableAction.User_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", event) + )); + + } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); + try { + this.tenantEntityManager.enableTenantFilters(); + } catch (InvalidApplicationException e) { + } } - return EventProcessingStatus.Success; + return status; } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java index c96b4273c..9f88c1455 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java @@ -14,6 +14,7 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.model.Tenant; import gr.cite.annotation.query.EntityUserQuery; import gr.cite.annotation.query.TenantQuery; +import gr.cite.annotation.service.tenant.TenantService; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.tools.auditing.AuditService; @@ -32,6 +33,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @@ -46,15 +48,24 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann private final ValidatorFactory validatorFactory; - private final ApplicationContext applicationContext; - private final QueryFactory queryFactory; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; + private final DeleterFactory deleterFactory; + private final TenantScope tenantScope; - public AnnotationEntitiesTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) { + public AnnotationEntitiesTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, AuditService auditService, TenantEntityManager tenantEntityManager, DeleterFactory deleterFactory, TenantScope tenantScope) { this.jsonHandlingService = jsonHandlingService; this.validatorFactory = validatorFactory; - this.applicationContext = applicationContext; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; this.queryFactory = queryFactory; + this.deleterFactory = deleterFactory; + this.tenantScope = tenantScope; } @Override @@ -67,102 +78,72 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann this.validatorFactory.validator(AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntitiesTouchedIntegrationEventValidator.class).validateForce(event); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && properties.getTenantId() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(properties.getTenantId(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { // logger.error("missing tenant from event message"); // return EventProcessingStatus.Error; - scope.setTenant(null, scope.getDefaultTenantCode()); - } - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - - ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); - tenantEntityManager.setEntityManager(entityManager); - tenantEntityManager.disableTenantFilters(); - for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) { - - EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); - List items = entityUserQuery - .entityIds(entityEvent.getEntityId()) - .isActive(IsActive.Active) - .collect(); - List updatedCreatedIds = new ArrayList<>(); - for (UUID user : entityEvent.getUserIds()) { - EntityUserEntity data = items.stream().filter(x -> x.getUserId().equals(user)).findFirst().orElse(null); - if (data == null) { - data = new EntityUserEntity(); - data.setId(UUID.randomUUID()); - data.setEntityId(entityEvent.getEntityId()); - data.setUserId(user); - data.setTenantId(properties.getTenantId()); - data.setCreatedAt(Instant.now()); - data.setUpdatedAt(Instant.now()); - data.setIsActive(IsActive.Active); - - entityManager.persist(data); - } - updatedCreatedIds.add(data.getId()); - } - - List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); - deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(toDelete); - - entityManager.flush(); - } - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.User_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", event) - )); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + tenantEntityManager.disableTenantFilters(); + for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) { + + EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); + List items = entityUserQuery + .entityIds(entityEvent.getEntityId()) + .isActive(IsActive.Active) + .collect(); + List updatedCreatedIds = new ArrayList<>(); + for (UUID user : entityEvent.getUserIds()) { + EntityUserEntity data = items.stream().filter(x -> x.getUserId().equals(user)).findFirst().orElse(null); + if (data == null) { + data = new EntityUserEntity(); + data.setId(UUID.randomUUID()); + data.setEntityId(entityEvent.getEntityId()); + data.setUserId(user); + data.setTenantId(properties.getTenantId()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + + } + updatedCreatedIds.add(data.getId()); + } + + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(toDelete); + + tenantEntityManager.flush(); + + + auditService.track(AuditableAction.User_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", event) + )); + } + } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); + try { + this.tenantEntityManager.enableTenantFilters(); + } catch (InvalidApplicationException e) { + } } - return EventProcessingStatus.Success; + + return status; } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java index 86a264349..161694ab9 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java @@ -1,26 +1,18 @@ package gr.cite.annotation.integrationevent.inbox.tenantremoval; -import gr.cite.annotation.data.TenantEntityManager; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; -import gr.cite.annotation.common.scope.fake.FakeRequestScope; -import gr.cite.annotation.errorcode.ErrorThesaurusProperties; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.service.tenant.TenantService; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.logging.LoggerService; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -34,13 +26,20 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantRemovalIntegrationEventHandlerImpl.class)); private final JsonHandlingService jsonHandlingService; - - private final ApplicationContext applicationContext; - - - public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext) { + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final TenantService tenantService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; + private final TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler; + public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager, TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.tenantService = tenantService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; + this.tenantRemovalConsistencyHandler = tenantRemovalConsistencyHandler; } @Override @@ -49,65 +48,34 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn if (event == null) return EventProcessingStatus.Error; - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); + EventProcessingStatus status = EventProcessingStatus.Success; + try { - ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler = this.applicationContext.getBean(TenantRemovalConsistencyHandler.class); - if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) - return EventProcessingStatus.Postponed; - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); - tenantEntityManager.setEntityManager(entityManager); - tenantEntityManager.disableTenantFilters(); - TenantService tenantService = this.applicationContext.getBean(TenantService.class); - tenantService.deleteAndSave(event.getId()); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( - new AbstractMap.SimpleEntry("id", event.getId()) - )); - //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - - transaction.commit(); - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) { + status = EventProcessingStatus.Postponed; + currentPrincipalResolver.pop(); + return status; } + + tenantEntityManager.disableTenantFilters(); + tenantService.deleteAndSave(event.getId()); + + + auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); } - return null; + return status; } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java index 373758c7c..a3e8edbaa 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java @@ -1,27 +1,20 @@ package gr.cite.annotation.integrationevent.inbox.tenanttouch; -import gr.cite.annotation.data.TenantEntityManager; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; -import gr.cite.annotation.common.scope.fake.FakeRequestScope; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.model.persist.TenantTouchedIntegrationEventPersist; import gr.cite.annotation.service.tenant.TenantService; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.validation.ValidatorFactory; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -34,15 +27,23 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantTouchedIntegrationEventHandlerImpl.class)); - protected final ApplicationContext applicationContext; private final JsonHandlingService jsonHandlingService; private final ValidatorFactory validatorFactory; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final TenantService tenantService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; - public TenantTouchedIntegrationEventHandlerImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory) { - this.applicationContext = applicationContext; + public TenantTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager) { this.jsonHandlingService = jsonHandlingService; this.validatorFactory = validatorFactory; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.tenantService = tenantService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -56,59 +57,25 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn model.setCode(event.getCode()); this.validatorFactory.validator(TenantTouchedIntegrationEventPersist.TenantTouchedIntegrationEventPersistValidator.class).validateForce(model); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + EventProcessingStatus status = EventProcessingStatus.Success; + tenantEntityManager.disableTenantFilters(); + try { + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); + tenantService.persist(model, null); - transaction = entityManager.getTransaction(); - transaction.begin(); + auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model) + )); - try { - TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); - tenantEntityManager.setEntityManager(entityManager); - tenantEntityManager.disableTenantFilters(); - TenantService tenantService = this.applicationContext.getBean(TenantService.class); - tenantService.persist(model, null); - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", model) - )); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - - transaction.commit(); - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); - } } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); } - return null; + return status; } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java index 08527936d..b5d7dc499 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -1,12 +1,10 @@ package gr.cite.annotation.integrationevent.inbox.userremoval; -import gr.cite.annotation.data.TenantEntityManager; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; -import gr.cite.annotation.common.scope.fake.FakeRequestScope; import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.data.TenantEntity; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; @@ -14,19 +12,15 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.model.Tenant; import gr.cite.annotation.query.TenantQuery; import gr.cite.annotation.service.user.UserService; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; import org.springframework.context.i18n.LocaleContextHolder; @@ -43,22 +37,35 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr private final JsonHandlingService jsonHandlingService; - private final ApplicationContext applicationContext; private final ErrorThesaurusProperties errors; private final MessageSource messageSource; + private final QueryFactory queryFactory; + private final TenantScope tenantScope; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final UserRemovalConsistencyHandler userRemovalConsistencyHandler; + private final UserService userService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; public UserRemovalIntegrationEventHandlerImpl( JsonHandlingService jsonHandlingService, - ApplicationContext applicationContext, ErrorThesaurusProperties errors, - MessageSource messageSource + MessageSource messageSource, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserRemovalConsistencyHandler userRemovalConsistencyHandler, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager ) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; this.errors = errors; this.messageSource = messageSource; + this.queryFactory = queryFactory; + this.tenantScope = tenantScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.userRemovalConsistencyHandler = userRemovalConsistencyHandler; + this.userService = userService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -71,77 +78,44 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName()); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && properties.getTenantId() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(properties.getTenantId(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { // logger.error("missing tenant from event message"); - scope.setTenant(null, scope.getDefaultTenantCode()); - } - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - - ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - - UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); - if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) - return EventProcessingStatus.Postponed; - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); - tenantEntityManager.setEntityManager(entityManager); - UserService userService = this.applicationContext.getBean(UserService.class); - userService.deleteAndSave(event.getUserId()); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.User_Delete, Map.ofEntries( - new AbstractMap.SimpleEntry("id", event.getUserId()) - )); - //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - - transaction.commit(); - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); +// return EventProcessingStatus.Error; + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) { + status = EventProcessingStatus.Postponed; + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); + return status; + } + + userService.deleteAndSave(event.getUserId()); + + auditService.track(AuditableAction.User_Delete, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getUserId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); } - return EventProcessingStatus.Success; + + return status; } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java index 0962369d0..a338db5f7 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java @@ -4,7 +4,6 @@ import gr.cite.annotation.data.TenantEntityManager; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; -import gr.cite.annotation.common.scope.fake.FakeRequestScope; import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.data.TenantEntity; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; @@ -19,13 +18,8 @@ import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.validation.ValidatorFactory; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -38,19 +32,30 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class)); - protected final ApplicationContext applicationContext; private final JsonHandlingService jsonHandlingService; private final ValidatorFactory validatorFactory; + private final QueryFactory queryFactory; + private final TenantScope tenantScope; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final UserService userService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; public UserTouchedIntegrationEventHandlerImpl( JsonHandlingService jsonHandlingService, - ApplicationContext applicationContext, - ValidatorFactory validatorFactory) { + ValidatorFactory validatorFactory, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; this.validatorFactory = validatorFactory; + this.queryFactory = queryFactory; + this.tenantScope = tenantScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.userService = userService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -63,71 +68,37 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && properties.getTenantId() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(properties.getTenantId(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { // logger.error("missing tenant from event message"); // return EventProcessingStatus.Error; - scope.setTenant(null, scope.getDefaultTenantCode()); - } - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - - ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); - tenantEntityManager.setEntityManager(entityManager); - UserService userService = this.applicationContext.getBean(UserService.class); - userService.persist(event, null); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.User_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", event) - )); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + userService.persist(event, null); + + auditService.track(AuditableAction.User_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", event) + )); + } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); } - return EventProcessingStatus.Success; + + return status; } -} +} \ No newline at end of file diff --git a/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java b/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java index b7de28665..ce571ec85 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java +++ b/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java @@ -36,6 +36,12 @@ public final class Permission { public static String EditLanguage = "EditLanguage"; public static String DeleteLanguage = "DeleteLanguage"; + + //NotificationTemplate + public static String BrowseNotificationTemplate = "BrowseNotificationTemplate"; + public static String EditNotificationTemplate = "EditNotificationTemplate"; + public static String DeleteNotificationTemplate = "DeleteNotificationTemplate"; + //Language public static String BrowseStatistics = "BrowseStatistics"; public static String BrowsePublicStatistics = "BrowsePublicStatistics"; diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java index 630d25439..372d429da 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java @@ -4,6 +4,7 @@ import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.fake.FakeRequestScope; import eu.eudat.data.QueueInboxEntity; +import eu.eudat.data.TenantEntityManager; import eu.eudat.integrationevent.TrackedEvent; import eu.eudat.query.QueueInboxQuery; import gr.cite.queueinbox.entity.QueueInbox; @@ -28,12 +29,10 @@ import java.util.List; import java.util.UUID; import java.util.function.Function; -public class InboxRepositoryImpl implements InboxRepository { +public class InboxRepositoryImpl implements InboxRepository { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InboxRepositoryImpl.class)); protected final ApplicationContext applicationContext; - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InboxRepositoryImpl.class)); - private final JsonHandlingService jsonHandlingService; private final InboxProperties inboxProperties; @@ -234,10 +233,17 @@ public class InboxRepositoryImpl implements InboxRepository { } private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) { + QueueInboxEntity queueMessage = new QueueInboxEntity(); queueMessage.setId(UUID.randomUUID()); Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null; if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId); + else if (tenantId instanceof String) { + try { + queueMessage.setTenantId(UUID.fromString((String) tenantId)); + } catch (Exception e) { + } + } queueMessage.setExchange(this.inboxProperties.getExchange()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setQueue(inboxCreatorParams.getQueueName()); @@ -265,6 +271,10 @@ public class InboxRepositoryImpl implements InboxRepository { entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); + + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + transaction.begin(); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); @@ -274,7 +284,7 @@ public class InboxRepositoryImpl implements InboxRepository { logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId()); } else { - EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); + EventProcessingStatus status = this.processMessage(queueInboxMessage); switch (status) { case Success: { queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); @@ -317,29 +327,39 @@ public class InboxRepositoryImpl implements InboxRepository { return success; } - private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { + + + private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) { IntegrationEventHandler handler = null; + logger.debug("Processing message with routing key '{}'", queueInboxMessage.getRoute()); +// if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantRemovalTopic())) +// handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); +// else { +// logger.error("No handler found for message routing key '{}'. Discarding.", queueInboxMessage.getRoute()); +// handler = null; +// } if (handler == null) return EventProcessingStatus.Discard; IntegrationEventProperties properties = new IntegrationEventProperties(); - properties.setAppId(appId); - properties.setMessageId(messageId); + properties.setAppId(queueInboxMessage.getApplicationId()); + properties.setMessageId(queueInboxMessage.getMessageId().toString()); + properties.setTenantId(queueInboxMessage.getTenantId()); - TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, message); + TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, queueInboxMessage.getMessage()); // using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag)) // { try { - return handler.handle(properties, message); + return handler.handle(properties, queueInboxMessage.getMessage()); } catch (Exception ex) { - logger.error("problem handling event from routing key " + routingKey + ". Setting nack and continuing...", ex); + logger.error("problem handling event from routing key " + queueInboxMessage.getRoute() + ". Setting nack and continuing...", ex); return EventProcessingStatus.Error; } // } } - private Boolean RoutingKeyMatched(String routingKey, List topics) { + private Boolean routingKeyMatched(String routingKey, List topics) { if (topics == null || topics.isEmpty()) return false; return topics.stream().anyMatch(x -> x.equals(routingKey)); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/IntegrationEventProperties.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/IntegrationEventProperties.java index b63c9b185..f046dda4d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/IntegrationEventProperties.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/IntegrationEventProperties.java @@ -1,10 +1,13 @@ package eu.eudat.integrationevent.inbox; +import java.util.UUID; + public class IntegrationEventProperties { private String messageId; private String appId; + private UUID tenantId; public String getMessageId() { return messageId; @@ -22,4 +25,11 @@ public class IntegrationEventProperties { this.appId = appId; } + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java index 8d788478c..90a227c44 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java @@ -23,7 +23,7 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.TENANT_TOUCH); - message.setTenantId(event.getId()); +// message.setTenantId(event.getId()); //Hack Can not Queue inbox before tenant created message.setEvent(event); this.outboxService.publish(message); diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java b/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java index 0a6161186..bf952bf94 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java @@ -167,11 +167,6 @@ public class StorageFileCleanupTask implements Closeable, ApplicationListener {{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.LANGUAGE' | translate}} - - + + {{language.code}} - {{formGroup.get('languageId').getError('backendError').message}} - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + {{formGroup.get('languageCode').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
@@ -123,7 +123,7 @@ {{formGroup.get('value').get('bodyKey').getError('backendError').message}} {{'GENERAL.VALIDATION.REQUIRED' | translate}} -
+
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.BODY-TEXT' | translate}} {{formGroup.get('value').get('bodyText').getError('backendError').message}} {{'GENERAL.VALIDATION.REQUIRED' | translate}} @@ -191,7 +191,7 @@ cancel - cancel - cancel - {{formGroup.get('value').get('extraDataKeys').getError('backendError').message}}
- + diff --git a/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.model.ts b/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.model.ts index 26cf61d1b..b5e0c44a9 100644 --- a/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.model.ts +++ b/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.model.ts @@ -15,7 +15,7 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements channel: NotificationTemplateChannel; notificationType: NotificationType; kind: NotificationTemplateKind; - languageId: Guid; + languageCode: string; value: NotificationTemplateValueEditorModel = new NotificationTemplateValueEditorModel(); permissions: string[]; @@ -30,10 +30,10 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements this.channel = item.channel; this.notificationType = item.notificationType; this.kind = item.kind; - if(item.language && item.language.id) this.languageId = item.language.id; + if(item.languageCode && item.languageCode) this.languageCode = item.languageCode; if (item.value) { this.value = new NotificationTemplateValueEditorModel(this.validationErrorModel).fromModel(item.value); } } - + return this; } @@ -45,7 +45,7 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements channel: [{ value: this.channel, disabled: disabled }, context.getValidation('channel').validators], notificationType: [{ value: this.notificationType, disabled: disabled }, context.getValidation('notificationType').validators], kind: [{ value: this.kind, disabled: disabled }, context.getValidation('kind').validators], - languageId: [{ value: this.languageId, disabled: disabled }, context.getValidation('languageId').validators], + languageCode: [{ value: this.languageCode, disabled: disabled }, context.getValidation('languageCode').validators], value: this.value.buildForm({ rootPath: `value.` }), @@ -60,7 +60,7 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements baseValidationArray.push({ key: 'channel', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'channel')] }); baseValidationArray.push({ key: 'notificationType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'notificationType')] }); baseValidationArray.push({ key: 'kind', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'kind')] }); - baseValidationArray.push({ key: 'languageId', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'languageId')] }); + baseValidationArray.push({ key: 'languageCode', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'languageCode')] }); baseValidationArray.push({ key: 'value', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'value')] }); baseValidationArray.push({ key: 'hash', validators: [] }); @@ -175,7 +175,7 @@ export class NotificationTemplateValueEditorModel implements NotificationTemplat baseValidationArray.push({ key: 'bcc', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}bcc`)] }); baseValidationArray.push({ key: 'bccMode', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}bccMode`)] }); baseValidationArray.push({ key: 'extraDataKeys', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}extraDataKeys`)] }); - + baseContext.validation = baseValidationArray; return baseContext; } @@ -243,7 +243,7 @@ export class NotificationFieldOptionsEditorModel implements NotificationFieldOpt validationErrorModel: this.validationErrorModel, rootPath }); - } + } return this.formBuilder.group({ mandatory: [{ value: this.mandatory, disabled: disabled }, context.getValidation('mandatory').validators], @@ -256,7 +256,7 @@ export class NotificationFieldOptionsEditorModel implements NotificationFieldOpt ), context.getValidation('optional').validators ), }); - + } static createValidationContext(params: { @@ -350,11 +350,11 @@ export class NotificationFieldInfoEditorModel implements NotificationFieldInfoPe const baseContext: ValidationContext = new ValidationContext(); const baseValidationArray: Validation[] = new Array(); - + baseValidationArray.push({ key: 'key', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}key`)] }); baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); baseValidationArray.push({ key: 'value', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); - + baseContext.validation = baseValidationArray; return baseContext; } diff --git a/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.resolver.ts b/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.resolver.ts index 069042b61..b9400e695 100644 --- a/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.resolver.ts +++ b/dmp-frontend/src/app/ui/admin/notification-template/editor/notification-template-editor.resolver.ts @@ -24,9 +24,8 @@ export class NotificationTemplateEditorResolver extends BaseEditorResolver { nameof(x => x.notificationType), nameof(x => x.kind), - [nameof(x => x.language),nameof(x => x.id)].join('.'), - [nameof(x => x.language),nameof(x => x.code)].join('.'), - + nameof(x => x.languageCode), + [nameof(x => x.value),nameof(x => x.subjectText)].join('.'), [nameof(x => x.value),nameof(x => x.subjectKey)].join('.'), [nameof(x => x.value),nameof(x => x.subjectFieldOptions), nameof(x => x.mandatory)].join('.'), diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/WebConfiguration.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/WebConfiguration.java index 617ff3c0f..8f0f8367e 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/WebConfiguration.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/WebConfiguration.java @@ -3,7 +3,7 @@ package gr.cite.notification.web; import gr.cite.notification.web.scope.tenant.TenantInterceptor; import gr.cite.notification.web.scope.tenant.TenantScopeClaimInterceptor; import gr.cite.notification.web.scope.tenant.TenantScopeHeaderInterceptor; -import gr.cite.notification.web.interceptors.UserInterceptor; +import gr.cite.notification.web.scope.user.UserInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/config/SecurityConfiguration.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/config/SecurityConfiguration.java index 2e17e3de6..a82ec7a23 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/config/SecurityConfiguration.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/config/SecurityConfiguration.java @@ -26,6 +26,8 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen import jakarta.servlet.Filter; import jakarta.servlet.http.HttpServletRequest; + +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -98,7 +100,7 @@ public class SecurityConfiguration { //In the example below, the default client handler will be ignored by the resolver @Override public List>> disableHandlers() { - return List.of(PermissionClientAuthorizationHandler.class); + return new ArrayList<>(); } }; } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/controllers/PrincipalController.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/controllers/PrincipalController.java index c8ff2443d..b51055511 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/controllers/PrincipalController.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/controllers/PrincipalController.java @@ -75,17 +75,4 @@ public class PrincipalController { } - @GetMapping("my-tenants") - public List myTenants() { - logger.debug("my-tenants"); - - MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); - List tenants = this.claimExtractor.asStrings(principal, TenantScope.TenantCodesClaimName); - - this.auditService.track(AuditableAction.Tenants_Lookup); - //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - return tenants == null ? null : tenants.stream().distinct().collect(Collectors.toList()); - } - } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptor.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptor.java deleted file mode 100644 index 565b141b7..000000000 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptor.java +++ /dev/null @@ -1,103 +0,0 @@ -package gr.cite.notification.web.interceptors; - - -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; -import gr.cite.notification.common.JsonHandlingService; -import gr.cite.notification.common.lock.LockByKeyManager; -import gr.cite.notification.common.scope.user.UserScope; -import gr.cite.notification.data.UserCredentialEntity; -import gr.cite.notification.model.UserCredential; -import gr.cite.notification.query.UserCredentialQuery; -import gr.cite.tools.data.query.QueryFactory; -import gr.cite.tools.exception.MyForbiddenException; -import gr.cite.tools.fieldset.BaseFieldSet; -import gr.cite.tools.logging.LoggerService; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.ui.ModelMap; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.context.request.WebRequestInterceptor; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; - -import java.util.UUID; - -@Component -public class UserInterceptor implements WebRequestInterceptor { - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class)); - private final UserScope userScope; - private final ClaimExtractor claimExtractor; - private final CurrentPrincipalResolver currentPrincipalResolver; - private final PlatformTransactionManager transactionManager; - private final UserInterceptorCacheService userInterceptorCacheService; - private final JsonHandlingService jsonHandlingService; - private final QueryFactory queryFactory; - private final LockByKeyManager lockByKeyManager; - @PersistenceContext - public EntityManager entityManager; - - @Autowired - public UserInterceptor( - UserScope userScope, - ClaimExtractor claimExtractor, - CurrentPrincipalResolver currentPrincipalResolver, - PlatformTransactionManager transactionManager, - UserInterceptorCacheService userInterceptorCacheService, - JsonHandlingService jsonHandlingService, - QueryFactory queryFactory, - LockByKeyManager lockByKeyManager) { - this.userScope = userScope; - this.currentPrincipalResolver = currentPrincipalResolver; - this.claimExtractor = claimExtractor; - this.transactionManager = transactionManager; - this.userInterceptorCacheService = userInterceptorCacheService; - this.jsonHandlingService = jsonHandlingService; - this.queryFactory = queryFactory; - this.lockByKeyManager = lockByKeyManager; - } - - @Override - public void preHandle(WebRequest request) { - UUID userId = null; - if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { - String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); - if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed"); - - UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); - if (cacheValue != null) { - userId = cacheValue.getUserId(); - } else { - userId = this.findExistingUserFromDbForce(subjectId); - - cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId); - - this.userInterceptorCacheService.put(cacheValue); - } - } - this.userScope.setUserId(userId); - } - - private UUID findExistingUserFromDbForce(String subjectId){ - UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user)); - if (userCredential != null) { - return userCredential.getUserId(); - } else { - throw new MyForbiddenException("User not created try again."); - } - } - - @Override - public void postHandle(@NonNull WebRequest request, ModelMap model) { - this.userScope.setUserId(null); - } - - @Override - public void afterCompletion(@NonNull WebRequest request, Exception ex) { - } -} - diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheOptions.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheOptions.java deleted file mode 100644 index adc161299..000000000 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheOptions.java +++ /dev/null @@ -1,10 +0,0 @@ -package gr.cite.notification.web.interceptors; - -import gr.cite.tools.cache.CacheOptions; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConfigurationProperties(prefix = "cache.user-by-subject-id") -public class UserInterceptorCacheOptions extends CacheOptions { -} diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheService.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheService.java deleted file mode 100644 index e293401f3..000000000 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/interceptors/UserInterceptorCacheService.java +++ /dev/null @@ -1,68 +0,0 @@ -package gr.cite.notification.web.interceptors; - -import gr.cite.notification.convention.ConventionService; -import gr.cite.tools.cache.CacheService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.HashMap; -import java.util.List; -import java.util.UUID; - -@Service -public class UserInterceptorCacheService extends CacheService { - - public static class UserInterceptorCacheValue { - - public UserInterceptorCacheValue() { - } - - public UserInterceptorCacheValue(String subjectId, UUID userId) { - this.subjectId = subjectId; - this.userId = userId; - } - - private String subjectId; - - public String getSubjectId() { - return subjectId; - } - - public void setSubjectId(String subjectId) { - this.subjectId = subjectId; - } - - private UUID userId; - public UUID getUserId() { - return userId; - } - - public void setUserId(UUID userId) { - this.userId = userId; - } - - } - - - @Autowired - public UserInterceptorCacheService(UserInterceptorCacheOptions options) { - super(options); - } - - @Override - protected Class valueClass() { - return UserInterceptorCacheValue.class; - } - - @Override - public String keyOf(UserInterceptorCacheValue value) { - return this.buildKey(value.getSubjectId()); - } - - - public String buildKey(String subject) { - HashMap keyParts = new HashMap<>(); - keyParts.put("$subject$", subject); - return this.generateKey(keyParts); - } -} diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java index 6c2d2107a..660677b06 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByCodeCacheService.java @@ -13,64 +13,66 @@ import java.util.UUID; @Service public class TenantByCodeCacheService extends CacheService { - public static class TenantByCodeCacheValue { + public static class TenantByCodeCacheValue { - public TenantByCodeCacheValue() { - } + public TenantByCodeCacheValue() { + } - public TenantByCodeCacheValue(String tenantCode, UUID tenantId) { - this.tenantCode = tenantCode; - this.tenantId = tenantId; - } + public TenantByCodeCacheValue(String tenantCode, UUID tenantId) { + this.tenantCode = tenantCode; + this.tenantId = tenantId; + } - private String tenantCode; + private String tenantCode; - public String getTenantCode() { - return tenantCode; - } + public String getTenantCode() { + return tenantCode; + } - public void setTenantCode(String tenantCode) { - this.tenantCode = tenantCode; - } + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } - private UUID tenantId; + private UUID tenantId; - public UUID getTenantId() { - return tenantId; - } + public UUID getTenantId() { + return tenantId; + } - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - } + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + } - private final ConventionService conventionService; + private final ConventionService conventionService; - @Autowired - public TenantByCodeCacheService(TenantByCodeCacheOptions options, ConventionService conventionService) { - super(options); - this.conventionService = conventionService; - } + @Autowired + public TenantByCodeCacheService(TenantByCodeCacheOptions options, ConventionService conventionService) { + super(options); + this.conventionService = conventionService; + } - @EventListener - public void handleTenantTouchedEvent(TenantTouchedEvent event) { - if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantCode())); - if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode())) this.evict(this.buildKey(event.getPreviousTenantCode())); - } + @EventListener + public void handleTenantTouchedEvent(TenantTouchedEvent event) { + if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) + this.evict(this.buildKey(event.getTenantCode())); + if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode())) + this.evict(this.buildKey(event.getPreviousTenantCode())); + } - @Override - protected Class valueClass() { - return TenantByCodeCacheValue.class; - } + @Override + protected Class valueClass() { + return TenantByCodeCacheValue.class; + } - @Override - public String keyOf(TenantByCodeCacheValue value) { - return this.buildKey(value.getTenantCode()); - } + @Override + public String keyOf(TenantByCodeCacheValue value) { + return this.buildKey(value.getTenantCode()); + } - public String buildKey(String code) { - return this.generateKey(new HashMap<>() {{ - put("$code$", code); - }}); - } + public String buildKey(String code) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$code$", code); + return this.generateKey(keyParts); + } } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByIdCacheService.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByIdCacheService.java index 89ab6c35d..9f2aaeb2a 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByIdCacheService.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantByIdCacheService.java @@ -14,63 +14,64 @@ import java.util.UUID; @Service public class TenantByIdCacheService extends CacheService { - public static class TenantByIdCacheValue { + public static class TenantByIdCacheValue { - public TenantByIdCacheValue() { - } + public TenantByIdCacheValue() { + } - public TenantByIdCacheValue(String tenantCode, UUID tenantId) { - this.tenantCode = tenantCode; - this.tenantId = tenantId; - } + public TenantByIdCacheValue(String tenantCode, UUID tenantId) { + this.tenantCode = tenantCode; + this.tenantId = tenantId; + } - private String tenantCode; + private String tenantCode; - public String getTenantCode() { - return tenantCode; - } + public String getTenantCode() { + return tenantCode; + } - public void setTenantCode(String tenantCode) { - this.tenantCode = tenantCode; - } + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } - private UUID tenantId; + private UUID tenantId; - public UUID getTenantId() { - return tenantId; - } + public UUID getTenantId() { + return tenantId; + } - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - } + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + } - private final ConventionService conventionService; + private final ConventionService conventionService; - @Autowired - public TenantByIdCacheService(TenantByIdCacheOptions options, ConventionService conventionService) { - super(options); - this.conventionService = conventionService; - } + @Autowired + public TenantByIdCacheService(TenantByIdCacheOptions options, ConventionService conventionService) { + super(options); + this.conventionService = conventionService; + } - @EventListener - public void handleTenantTouchedEvent(TenantTouchedEvent event) { - if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantId())); - } + @EventListener + public void handleTenantTouchedEvent(TenantTouchedEvent event) { + if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) + this.evict(this.buildKey(event.getTenantId())); + } - @Override - protected Class valueClass() { - return TenantByIdCacheValue.class; - } + @Override + protected Class valueClass() { + return TenantByIdCacheValue.class; + } - @Override - public String keyOf(TenantByIdCacheValue value) { - return this.buildKey(value.getTenantId()); - } + @Override + public String keyOf(TenantByIdCacheValue value) { + return this.buildKey(value.getTenantId()); + } - public String buildKey(UUID id) { - return this.generateKey(new HashMap<>() {{ - put("$tenantId$", id.toString().toLowerCase(Locale.ROOT)); - }}); - } + public String buildKey(UUID id) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$tenantId$", id.toString().toLowerCase(Locale.ROOT)); + return this.generateKey(keyParts); + } } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantInterceptor.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantInterceptor.java index e28f0a5ec..afac21279 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantInterceptor.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantInterceptor.java @@ -4,6 +4,7 @@ package gr.cite.notification.web.scope.tenant; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; +import gr.cite.notification.authorization.ClaimNames; import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.scope.tenant.TenantScope; @@ -12,9 +13,17 @@ import gr.cite.notification.data.TenantUserEntity; import gr.cite.notification.data.UserEntity; import gr.cite.notification.data.tenant.TenantScopedBaseEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; +import gr.cite.notification.query.utils.BuildSubQueryInput; +import gr.cite.notification.query.utils.QueryUtilsService; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; import org.hibernate.Session; +import org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -26,15 +35,9 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; import javax.management.InvalidApplicationException; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Tuple; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; -import jakarta.persistence.criteria.Subquery; import java.util.List; import java.util.Locale; +import java.util.UUID; @Component public class TenantInterceptor implements WebRequestInterceptor { @@ -45,11 +48,10 @@ public class TenantInterceptor implements WebRequestInterceptor { private final CurrentPrincipalResolver currentPrincipalResolver; private final ClaimExtractor claimExtractor; private final ApplicationContext applicationContext; - private final ErrorThesaurusProperties errorThesaurusProperties; private final TenantScopeProperties tenantScopeProperties; private final UserAllowedTenantCacheService userAllowedTenantCacheService; private final ErrorThesaurusProperties errors; - + private final QueryUtilsService queryUtilsService; @PersistenceContext public EntityManager entityManager; @@ -60,60 +62,70 @@ public class TenantInterceptor implements WebRequestInterceptor { CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor, ApplicationContext applicationContext, - ErrorThesaurusProperties errorThesaurusProperties, TenantScopeProperties tenantScopeProperties, UserAllowedTenantCacheService userAllowedTenantCacheService, - ErrorThesaurusProperties errors) { + ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService) { this.tenantScope = tenantScope; this.userScope = userScope; this.currentPrincipalResolver = currentPrincipalResolver; this.claimExtractor = claimExtractor; this.applicationContext = applicationContext; - this.errorThesaurusProperties = errorThesaurusProperties; this.tenantScopeProperties = tenantScopeProperties; this.userAllowedTenantCacheService = userAllowedTenantCacheService; this.errors = errors; + this.queryUtilsService = queryUtilsService; } @Override - public void preHandle(WebRequest request) throws InvalidApplicationException { + public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException, InterruptedException { if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.tenantScope.isMultitenant()) return; boolean isAllowedNoTenant = this.applicationContext.getBean(AuthorizationService.class).authorize(Permission.AllowNoTenant); if (tenantScope.isSet() && this.entityManager != null) { - List currentPrincipalTenantCodes = this.claimExtractor.asStrings(this.currentPrincipalResolver.currentPrincipal(), TenantScope.TenantCodesClaimName); + List currentPrincipalTenantCodes = this.claimExtractor.asStrings(this.currentPrincipalResolver.currentPrincipal(), ClaimNames.TenantCodesClaimName); if ((currentPrincipalTenantCodes == null || !currentPrincipalTenantCodes.contains(tenantScope.getTenantCode())) && !isAllowedNoTenant) { logger.warn("tenant not allowed {}", this.tenantScope.getTenant()); - //throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); + throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); } boolean isUserAllowedTenant = false; - UserAllowedTenantCacheService.UserAllowedTenantCacheValue cacheValue = this.userAllowedTenantCacheService.lookup(this.userAllowedTenantCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.getTenant())); - if (cacheValue != null) { - isUserAllowedTenant = cacheValue.isAllowed(); + if (this.tenantScope.isDefaultTenant()){ + isUserAllowedTenant = true; } else { - isUserAllowedTenant = this.isUserAllowedTenant(); - this.userAllowedTenantCacheService.put(new UserAllowedTenantCacheService.UserAllowedTenantCacheValue(this.userScope.getUserId(), this.tenantScope.getTenant(), isUserAllowedTenant)); + UserAllowedTenantCacheService.UserAllowedTenantCacheValue cacheValue = this.userAllowedTenantCacheService.lookup(this.userAllowedTenantCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.getTenant())); + if (cacheValue != null) { + isUserAllowedTenant = cacheValue.isAllowed(); + } else { + isUserAllowedTenant = this.isUserAllowedTenant(); + this.userAllowedTenantCacheService.put(new UserAllowedTenantCacheService.UserAllowedTenantCacheValue(this.userScope.getUserId(), this.tenantScope.getTenant(), isUserAllowedTenant)); + } } if (isUserAllowedTenant) { - this.entityManager - .unwrap(Session.class) - .enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); + if(!tenantScope.isDefaultTenant()) { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString()); + } else { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } else { if (isAllowedNoTenant || this.isWhiteListedEndpoint(request)) { tenantScope.setTenant(null, null); } else { logger.warn("tenant not allowed {}", this.tenantScope.getTenant()); - //throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); + throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); } } } else { if (!isAllowedNoTenant) { if (!this.isWhiteListedEndpoint(request)) { logger.warn("tenant scope not provided"); - throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); + throw new MyForbiddenException(this.errors.getMissingTenant().getCode(), this.errors.getMissingTenant().getMessage()); } } } @@ -131,26 +143,36 @@ public class TenantInterceptor implements WebRequestInterceptor { return false; } - private boolean isUserAllowedTenant() throws InvalidApplicationException { + private boolean isUserAllowedTenant() throws InvalidApplicationException, InterruptedException { if (userScope.isSet()) { CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); + CriteriaQuery query = criteriaBuilder.createQuery(UserEntity.class); Root root = query.from(UserEntity.class); - Subquery subQuery = query.subquery(TenantUserEntity.class); - Root subQueryRoot = subQuery.from(TenantUserEntity.class); query.where(criteriaBuilder.and( criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active), - criteriaBuilder.in(root.get(UserEntity._id)).value(subQuery.where( - criteriaBuilder.and( - criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()), - criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()), - criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active) - )).select(subQueryRoot.get(TenantUserEntity._userId)).distinct(true) + criteriaBuilder.in(root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class) + .query(query) + .criteriaBuilder(criteriaBuilder) + .keyPathFunc((subQueryRoot) -> subQueryRoot.get(TenantUserEntity._userId)) + .filterFunc((subQueryRoot, cb) -> + { + try { + return cb.and( + criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()), + criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()), + criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active) + ); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } + ) + )) ) )); query.multiselect(root.get(UserEntity._id).alias(UserEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); - if (results.size() > 0) return true; + List results = this.entityManager.createQuery(query).getResultList(); + return !results.isEmpty(); } return false; diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeClaimInterceptor.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeClaimInterceptor.java index 138b9661f..700d0288d 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeClaimInterceptor.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeClaimInterceptor.java @@ -5,6 +5,7 @@ import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; +import gr.cite.notification.authorization.ClaimNames; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.convention.ConventionService; @@ -12,6 +13,11 @@ import gr.cite.notification.data.TenantEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; import org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -22,176 +28,157 @@ import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; import javax.management.InvalidApplicationException; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Tuple; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; import java.util.List; import java.util.UUID; @Component public class TenantScopeClaimInterceptor implements WebRequestInterceptor { - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScopeClaimInterceptor.class)); - private final TenantScope tenantScope; - private final ConventionService conventionService; - private final TenantScopeProperties tenantScopeProperties; - private final ErrorThesaurusProperties errorThesaurusProperties; - private final ClaimExtractor claimExtractor; - private final CurrentPrincipalResolver currentPrincipalResolver; - private final String clientTenantClaimName; - private final ClaimExtractorContext claimExtractorContext; - private final TenantByCodeCacheService tenantByCodeCacheService; - private final TenantByIdCacheService tenantByIdCacheService; - @PersistenceContext - public EntityManager entityManager; + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScopeClaimInterceptor.class)); + private final TenantScope tenantScope; + private final ConventionService conventionService; + private final TenantScopeProperties tenantScopeProperties; + private final ErrorThesaurusProperties errorThesaurusProperties; + private final ClaimExtractor claimExtractor; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final String clientTenantClaimName; + private final ClaimExtractorContext claimExtractorContext; + private final TenantByCodeCacheService tenantByCodeCacheService; + private final TenantByIdCacheService tenantByIdCacheService; + @PersistenceContext + public EntityManager entityManager; - @Autowired - public TenantScopeClaimInterceptor( - TenantScope tenantScope, - ConventionService conventionService, - ClaimExtractor claimExtractor, - CurrentPrincipalResolver currentPrincipalResolver, - ErrorThesaurusProperties errorThesaurusProperties, - TenantScopeProperties tenantScopeProperties, - ClaimExtractorContext claimExtractorContext, - TenantByCodeCacheService tenantByCodeCacheService, - TenantByIdCacheService tenantByIdCacheService - ) { - this.tenantScope = tenantScope; - this.conventionService = conventionService; - this.currentPrincipalResolver = currentPrincipalResolver; - this.claimExtractor = claimExtractor; - this.errorThesaurusProperties = errorThesaurusProperties; - this.tenantScopeProperties = tenantScopeProperties; - this.claimExtractorContext = claimExtractorContext; - this.tenantByCodeCacheService = tenantByCodeCacheService; - this.tenantByIdCacheService = tenantByIdCacheService; - this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + TenantScope.TenantClaimName; - } + @Autowired + public TenantScopeClaimInterceptor( + TenantScope tenantScope, + ConventionService conventionService, + ClaimExtractor claimExtractor, + CurrentPrincipalResolver currentPrincipalResolver, + ErrorThesaurusProperties errorThesaurusProperties, + TenantScopeProperties tenantScopeProperties, + ClaimExtractorContext claimExtractorContext, + TenantByCodeCacheService tenantByCodeCacheService, + TenantByIdCacheService tenantByIdCacheService + ) { + this.tenantScope = tenantScope; + this.conventionService = conventionService; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractor = claimExtractor; + this.errorThesaurusProperties = errorThesaurusProperties; + this.tenantScopeProperties = tenantScopeProperties; + this.claimExtractorContext = claimExtractorContext; + this.tenantByCodeCacheService = tenantByCodeCacheService; + this.tenantByIdCacheService = tenantByIdCacheService; + this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + ClaimNames.TenantClaimName; + } - @Override - public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException { - if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; - if (!this.tenantScope.isMultitenant()) return; + @Override + public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException { + if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; + if (!this.tenantScope.isMultitenant()) return; - MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); - if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) { - Boolean scoped = this.scopeByPrincipal(this.tenantScope, principal); - if (!scoped) scoped = this.scopeByClient(this.tenantScope, principal); - if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant()) throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); - } - } + MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); + if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) { + boolean scoped = this.scopeByPrincipal(principal); + if (!scoped) scoped = this.scopeByClient(principal); + if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant()) + throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); + } + } - private Boolean scopeByPrincipal(TenantScope scope, MyPrincipal principal) { - String tenantCode = this.claimExtractor.tenantString(principal); - if (tenantCode == null || tenantCode.isBlank()) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); + private boolean scopeByPrincipal(MyPrincipal principal) { + String tenantCode = this.claimExtractor.tenantString(principal); + if (this.conventionService.isNullOrEmpty(tenantCode)) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); + if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return false; - UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); - if (tenantId == null && tenantCode == null) return false; - if (tenantId == null) { - TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); - if (cacheValue != null) { - tenantId = cacheValue.getTenantId(); - } else { - tenantId = this.getTenantIdFromDatabase(tenantCode); - this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); - this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); - } - } else { - logger.debug("tenant claim was set to {}", tenantId); - TenantByIdCacheService.TenantByIdCacheValue cacheValue = this.tenantByIdCacheService.lookup(this.tenantByIdCacheService.buildKey(tenantId)); + if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){ + logger.debug("parsed tenant header and set tenant to default tenant"); + this.tenantScope.setTenant(null, tenantCode); + this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); + return true; + } + + UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); + if (tenantId == null) { + TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); + if (cacheValue != null) { + tenantId = cacheValue.getTenantId(); + } else { + tenantId = this.getTenantIdFromDatabase(tenantCode); + this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); + this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); + } + } else { + logger.debug("tenant claim was set to {}", tenantId); + TenantByIdCacheService.TenantByIdCacheValue cacheValue = this.tenantByIdCacheService.lookup(this.tenantByIdCacheService.buildKey(tenantId)); - if (cacheValue != null) { - tenantCode = cacheValue.getTenantCode(); - } else { - tenantCode = this.getTenantCodeFromDatabase(tenantId); - this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); - this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); - } - } + if (cacheValue != null) { + tenantCode = cacheValue.getTenantCode(); + } else { + tenantCode = this.getTenantCodeFromDatabase(tenantId); + this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); + this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); + } + } - if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { - logger.debug("parsed tenant header and set tenant id to {}", tenantId); - this.tenantScope.setTenant(tenantId, tenantCode); - this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); - } - return tenantId != null; - } + if (tenantId != null) { + logger.debug("parsed tenant header and set tenant id to {}", tenantId); + this.tenantScope.setTenant(tenantId, tenantCode); + this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); + return true; + } + return false; + } - private Boolean scopeByClient(TenantScope scope, MyPrincipal principal) throws InvalidApplicationException { - String client = this.claimExtractor.client(principal); + private boolean scopeByClient(MyPrincipal principal) throws InvalidApplicationException { + String client = this.claimExtractor.client(principal); - Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client); - logger.debug("client is whitelisted : {}, scope is set: {}, with value {}", isWhiteListed, scope.isSet(), (scope.isSet() ? scope.getTenant() : null)); + Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client); + logger.debug("client is whitelisted : {}, scope is set: {}, with value {}", isWhiteListed, this.tenantScope.isSet(), (this.tenantScope.isSet() ? this.tenantScope.getTenant() : null)); - return isWhiteListed && scope.isSet(); - } + return isWhiteListed && this.tenantScope.isSet(); + } - private UUID getTenantIdFromDatabase(String tenantCode) { - CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); - Root root = query.from(TenantEntity.class); - query = query.where( - criteriaBuilder.and( - criteriaBuilder.equal(root.get(TenantEntity._code), tenantCode), - criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) - ) - ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); - if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._id); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return (UUID) o; - } catch (ClassCastException e) { - return null; - } - } - return null; - } + private UUID getTenantIdFromDatabase(String tenantCode) { + CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); + Root root = query.from(TenantEntity.class); + query = query.where( + criteriaBuilder.and( + criteriaBuilder.equal(root.get(TenantEntity._code), tenantCode), + criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) + ) + ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); + List results = this.entityManager.createQuery(query).getResultList(); + if (results.size() == 1) { + return results.getFirst().getId(); + } + return null; + } - private String getTenantCodeFromDatabase(UUID tenantId) { - CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); - Root root = query.from(TenantEntity.class); - query = query.where( - criteriaBuilder.and( - criteriaBuilder.equal(root.get(TenantEntity._id), tenantId), - criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) - ) - ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); - List results = this.entityManager.createQuery(query).getResultList(); - if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._code); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return (String) o; - } catch (ClassCastException e) { - return null; - } - } - return null; - } + private String getTenantCodeFromDatabase(UUID tenantId) { + CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); + Root root = query.from(TenantEntity.class); + query = query.where( + criteriaBuilder.and( + criteriaBuilder.equal(root.get(TenantEntity._id), tenantId), + criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) + ) + ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); + List results = this.entityManager.createQuery(query).getResultList(); + if (results.size() == 1) { + return results.getFirst().getCode(); + } + return null; + } - @Override - public void postHandle(@NonNull WebRequest request, ModelMap model) { - this.tenantScope.setTenant(null, null); - this.claimExtractorContext.removeReplaceParameter(TenantScope.TenantReplaceParameter); - } + @Override + public void postHandle(@NonNull WebRequest request, ModelMap model) { + this.tenantScope.setTenant(null, null); + this.claimExtractorContext.removeReplaceParameter(TenantScope.TenantReplaceParameter); + } - @Override - public void afterCompletion(@NonNull WebRequest request, Exception ex) { - } + @Override + public void afterCompletion(@NonNull WebRequest request, Exception ex) { + } } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeHeaderInterceptor.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeHeaderInterceptor.java index 3a5b2ca20..5058b130d 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeHeaderInterceptor.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeHeaderInterceptor.java @@ -3,11 +3,18 @@ package gr.cite.notification.web.scope.tenant; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; +import gr.cite.notification.authorization.ClaimNames; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.TenantEntity; import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; @@ -16,13 +23,6 @@ import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; -import javax.management.InvalidApplicationException; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Tuple; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; import java.util.List; import java.util.UUID; @@ -56,16 +56,22 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { } @Override - public void preHandle(WebRequest request) throws InvalidApplicationException { + public void preHandle(@NotNull WebRequest request) { if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.tenantScope.isMultitenant()) return; - String tenantCode = request.getHeader(TenantScope.TenantClaimName); - logger.debug("retrieved request tenant header is: {header}", tenantCode); - if (this.conventionService.isNullOrEmpty(tenantCode)) return; + String tenantCode = request.getHeader(ClaimNames.TenantClaimName); + logger.debug("retrieved request tenant header is: {}", tenantCode); + if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return; + + if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){ + logger.debug("parsed tenant header and set tenant to default tenant"); + this.tenantScope.setTenant(null, tenantCode); + this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); + return; + } UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); - if (tenantId == null && tenantCode == null) return; if (tenantId == null) { TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); if (cacheValue != null) { @@ -86,8 +92,8 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { } } - if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { - logger.debug("parsed tenant header and set tenant id to {tenant}", tenantId); + if (tenantId != null) { + logger.debug("parsed tenant header and set tenant id to {}", tenantId); this.tenantScope.setTenant(tenantId, tenantCode); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); } @@ -95,7 +101,7 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { private UUID getTenantIdFromDatabase(String tenantCode) { CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); Root root = query.from(TenantEntity.class); query = query.where( criteriaBuilder.and( @@ -103,27 +109,16 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) ) ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); + List results = this.entityManager.createQuery(query).getResultList(); if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._id); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return UUID.class.cast(o); - } catch (ClassCastException e) { - return null; - } + return results.getFirst().getId(); } return null; } private String getTenantCodeFromDatabase(UUID tenantId) { CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); Root root = query.from(TenantEntity.class); query = query.where( criteriaBuilder.and( @@ -131,20 +126,9 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) ) ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); - List results = this.entityManager.createQuery(query).getResultList(); + List results = this.entityManager.createQuery(query).getResultList(); if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._code); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return String.class.cast(o); - } catch (ClassCastException e) { - return null; - } + return results.getFirst().getCode(); } return null; } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeProperties.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeProperties.java index 6ed29645b..7e60e5f50 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeProperties.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/TenantScopeProperties.java @@ -40,4 +40,5 @@ public class TenantScopeProperties { public void setEnforceTrustedTenant(Boolean enforceTrustedTenant) { this.enforceTrustedTenant = enforceTrustedTenant; } + } diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheOptions.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheOptions.java index 30606754c..0973b33e7 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheOptions.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheOptions.java @@ -8,3 +8,5 @@ import org.springframework.context.annotation.Configuration; @ConfigurationProperties(prefix = "cache.user-allowed-tenant") public class UserAllowedTenantCacheOptions extends CacheOptions { } + + diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheService.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheService.java index 0cebe00ea..4a51ba9b0 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheService.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/tenant/UserAllowedTenantCacheService.java @@ -14,77 +14,78 @@ import java.util.UUID; @Service public class UserAllowedTenantCacheService extends CacheService { - public static class UserAllowedTenantCacheValue { + public static class UserAllowedTenantCacheValue { - public UserAllowedTenantCacheValue() { - } + public UserAllowedTenantCacheValue() { + } - public UserAllowedTenantCacheValue(UUID userId, UUID tenantId, boolean isAllowed) { - this.userId = userId; - this.tenantId = tenantId; - this.isAllowed = isAllowed; - } + public UserAllowedTenantCacheValue(UUID userId, UUID tenantId, boolean isAllowed) { + this.userId = userId; + this.tenantId = tenantId; + this.isAllowed = isAllowed; + } - private UUID userId; + private UUID userId; - public UUID getUserId() { - return userId; - } + public UUID getUserId() { + return userId; + } - public void setUserId(UUID userId) { - this.userId = userId; - } + public void setUserId(UUID userId) { + this.userId = userId; + } - private UUID tenantId; + private UUID tenantId; - public UUID getTenantId() { - return tenantId; - } + public UUID getTenantId() { + return tenantId; + } - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } - private boolean isAllowed; + private boolean isAllowed; - public boolean isAllowed() { - return isAllowed; - } + public boolean isAllowed() { + return isAllowed; + } - public void setAllowed(boolean allowed) { - isAllowed = allowed; - } - } + public void setAllowed(boolean allowed) { + isAllowed = allowed; + } + } - @Autowired - public UserAllowedTenantCacheService(UserAllowedTenantCacheOptions options) { - super(options); - } + @Autowired + public UserAllowedTenantCacheService(UserAllowedTenantCacheOptions options) { + super(options); + } - @EventListener - public void handleUserRemovedFromTenantEvent(UserRemovedFromTenantEvent event) { - this.evict(this.buildKey(event.getUserId(), event.getTenantId())); - } + @EventListener + public void handleUserRemovedFromTenantEvent(UserRemovedFromTenantEvent event) { + this.evict(this.buildKey(event.getUserId(), event.getTenantId())); + } - @EventListener - public void handleUserAddedToTenantEvent(UserAddedToTenantEvent event) { - this.evict(this.buildKey(event.getUserId(), event.getTenantId())); - } + @EventListener + public void handleUserAddedToTenantEvent(UserAddedToTenantEvent event) { + this.evict(this.buildKey(event.getUserId(), event.getTenantId())); + } - @Override - protected Class valueClass() { - return UserAllowedTenantCacheValue.class; - } + @Override + protected Class valueClass() { + return UserAllowedTenantCacheValue.class; + } - @Override - public String keyOf(UserAllowedTenantCacheValue value) { - return this.buildKey(value.getUserId(), value.getTenantId()); - } + @Override + public String keyOf(UserAllowedTenantCacheValue value) { + return this.buildKey(value.getUserId(), value.getTenantId()); + } - public String buildKey(UUID userId, UUID tenantId) { - return this.generateKey(new HashMap<>() {{ - put("$user_id$", userId.toString().toLowerCase(Locale.ROOT)); - put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT)); - }}); - } + public String buildKey(UUID userId, UUID tenantId) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$user_id$", userId.toString().toLowerCase(Locale.ROOT)); + keyParts.put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT)); + return this.generateKey(keyParts); + } } + diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptor.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptor.java index 63ac88829..033211e89 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptor.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptor.java @@ -1,154 +1,81 @@ -//package gr.cite.notification.web.scope.user; -// -// -//import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -//import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; -//import gr.cite.notification.common.enums.IsActive; -//import gr.cite.notification.common.scope.user.UserScope; -//import gr.cite.notification.data.UserEntity; -//import gr.cite.notification.locale.LocaleService; -//import gr.cite.tools.logging.LoggerService; -//import org.slf4j.LoggerFactory; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.lang.NonNull; -//import org.springframework.stereotype.Component; -//import org.springframework.transaction.PlatformTransactionManager; -//import org.springframework.transaction.TransactionDefinition; -//import org.springframework.transaction.TransactionStatus; -//import org.springframework.transaction.support.DefaultTransactionDefinition; -//import org.springframework.ui.ModelMap; -//import org.springframework.web.context.request.WebRequest; -//import org.springframework.web.context.request.WebRequestInterceptor; -// -//import javax.management.InvalidApplicationException; -//import jakarta.persistence.EntityManager; -//import jakarta.persistence.PersistenceContext; -//import jakarta.persistence.Tuple; -//import jakarta.persistence.criteria.CriteriaBuilder; -//import jakarta.persistence.criteria.CriteriaQuery; -//import jakarta.persistence.criteria.Root; -//import java.time.Instant; -//import java.util.List; -//import java.util.UUID; -// -//@Component -//public class UserInterceptor implements WebRequestInterceptor { -// private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class)); -// private final UserScope userScope; -// private final ClaimExtractor claimExtractor; -// private final CurrentPrincipalResolver currentPrincipalResolver; -// private final LocaleService localeService; -// private final PlatformTransactionManager transactionManager; -// private final UserInterceptorCacheService userInterceptorCacheService; -// @PersistenceContext -// public EntityManager entityManager; -// -// @Autowired -// public UserInterceptor( -// UserScope userScope, -// LocaleService localeService, -// ClaimExtractor claimExtractor, -// CurrentPrincipalResolver currentPrincipalResolver, -// PlatformTransactionManager transactionManager, -// UserInterceptorCacheService userInterceptorCacheService -// ) { -// this.userScope = userScope; -// this.localeService = localeService; -// this.currentPrincipalResolver = currentPrincipalResolver; -// this.claimExtractor = claimExtractor; -// this.transactionManager = transactionManager; -// this.userInterceptorCacheService = userInterceptorCacheService; -// } -// -// @Override -// public void preHandle(WebRequest request) throws InvalidApplicationException { -// UUID userId = null; -// if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { -// String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); -// -// UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); -// if (cacheValue != null) { -// userId = cacheValue.getUserId(); -// } else { -// userId = this.getUserIdFromDatabase(subjectId); -// if (userId == null) userId = this.createUser(subjectId); -// -// this.userInterceptorCacheService.put(new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId)); -// } -// } -// this.userScope.setUserId(userId); -// } -// -// private UUID getUserIdFromDatabase(String subjectId) throws InvalidApplicationException { -// CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); -// CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); -// Root root = query.from(UserEntity.class); -// query.where( -// criteriaBuilder.and( -//// criteriaBuilder.equal(root.get(UserEntity._subjectId), subjectId), -// criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active) -// )); -// -// query.multiselect(root.get(UserEntity._id).alias(UserEntity._id)); -// -// List results = this.entityManager.createQuery(query).getResultList(); -// if (results.size() == 1) { -// Object o; -// try { -// o = results.get(0).get(UserEntity._id); -// } catch (IllegalArgumentException e) { -// return null; -// } -// if (o == null) return null; -// try { -// return UUID.class.cast(o); -// } catch (ClassCastException e) { -// return null; -// } -// } -// return null; -// } -// -// private UUID createUser(String subjectId) { -// String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal()); -// String familyName = this.claimExtractor.familyName(this.currentPrincipalResolver.currentPrincipal()); -// if (name == null) name = subjectId; -// UserEntity user = new UserEntity(); -// user.setId(UUID.randomUUID()); -// user.setCreatedAt(Instant.now()); -// user.setUpdatedAt(Instant.now()); -// user.setName(name); -//// user.setLastName(familyName == null ? name : familyName); -// user.setIsActive(IsActive.Active); -//// user.setSubjectId(subjectId); -//// user.setCulture(this.localeService.cultureName()); -//// user.setTimezone(this.localeService.timezoneName()); -//// user.setLanguage(this.localeService.language()); -// -// DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); -// definition.setName(UUID.randomUUID().toString()); -// definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); -// definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); -// TransactionStatus status = null; -// try { -// status = transactionManager.getTransaction(definition); -// this.entityManager.persist(user); -// -// this.entityManager.flush(); -// transactionManager.commit(status); -// } catch (Exception ex) { -// if (status != null) transactionManager.rollback(status); -// throw ex; -// } -// return user.getId(); -// } -// -// @Override -// public void postHandle(@NonNull WebRequest request, ModelMap model) { -// this.userScope.setUserId(null); -// } -// -// @Override -// public void afterCompletion(@NonNull WebRequest request, Exception ex) { -// } -//} +package gr.cite.notification.web.scope.user; + + +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; +import gr.cite.notification.common.scope.user.UserScope; +import gr.cite.notification.data.UserCredentialEntity; +import gr.cite.notification.model.UserCredential; +import gr.cite.notification.query.UserCredentialQuery; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.fieldset.BaseFieldSet; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.ui.ModelMap; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.WebRequestInterceptor; + +import java.util.UUID; + +@Component +public class UserInterceptor implements WebRequestInterceptor { + private final UserScope userScope; + private final ClaimExtractor claimExtractor; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final UserInterceptorCacheService userInterceptorCacheService; + private final QueryFactory queryFactory; + + @Autowired + public UserInterceptor( + UserScope userScope, + ClaimExtractor claimExtractor, + CurrentPrincipalResolver currentPrincipalResolver, + UserInterceptorCacheService userInterceptorCacheService, + QueryFactory queryFactory) { + this.userScope = userScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractor = claimExtractor; + this.userInterceptorCacheService = userInterceptorCacheService; + this.queryFactory = queryFactory; + } + + @Override + public void preHandle(@NotNull WebRequest request) { + UUID userId = null; + if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { + String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); + if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed"); + + UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); + if (cacheValue != null) { + userId = cacheValue.getUserId(); + } else { + userId = this.findExistingUserFromDb(subjectId); + if (userId != null) { + cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId); + this.userInterceptorCacheService.put(cacheValue); + } + } + } + this.userScope.setUserId(userId); + } + private UUID findExistingUserFromDb(String subjectId) { + UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user)); + if (userCredential != null) { + return userCredential.getUserId(); + } + return null; + } + @Override + public void postHandle(@NonNull WebRequest request, ModelMap model) { + this.userScope.setUserId(null); + } + + @Override + public void afterCompletion(@NonNull WebRequest request, Exception ex) { + } +} + diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheOptions.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheOptions.java index ceaf7996e..8a35750a6 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheOptions.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheOptions.java @@ -1,10 +1,10 @@ -//package gr.cite.notification.web.scope.user; -// -//import gr.cite.tools.cache.CacheOptions; -//import org.springframework.boot.context.properties.ConfigurationProperties; -//import org.springframework.context.annotation.Configuration; -// -//@Configuration -//@ConfigurationProperties(prefix = "cache.user-by-subject-id") -//public class UserInterceptorCacheOptions extends CacheOptions { -//} +package gr.cite.notification.web.scope.user; + +import gr.cite.tools.cache.CacheOptions; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "cache.user-by-subject-id") +public class UserInterceptorCacheOptions extends CacheOptions { +} diff --git a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheService.java b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheService.java index 338a69f03..b367fa167 100644 --- a/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheService.java +++ b/notification-service/notification-web/src/main/java/gr/cite/notification/web/scope/user/UserInterceptorCacheService.java @@ -1,77 +1,67 @@ -//package gr.cite.notification.web.scope.user; -// -//import gr.cite.notification.convention.ConventionService; -//import gr.cite.notification.event.UserTouchedEvent; -//import gr.cite.tools.cache.CacheService; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.context.event.EventListener; -//import org.springframework.stereotype.Service; -// -//import java.util.HashMap; -//import java.util.UUID; -// -//@Service -//public class UserInterceptorCacheService extends CacheService { -// -// public static class UserInterceptorCacheValue { -// -// public UserInterceptorCacheValue() { -// } -// -// public UserInterceptorCacheValue(String subjectId, UUID userId) { -// this.subjectId = subjectId; -// this.userId = userId; -// } -// -// private String subjectId; -// -// public String getSubjectId() { -// return subjectId; -// } -// -// public void setSubjectId(String subjectId) { -// this.subjectId = subjectId; -// } -// -// private UUID userId; -// -// public UUID getUserId() { -// return userId; -// } -// -// public void setUserId(UUID userId) { -// this.userId = userId; -// } -// } -// -// private final ConventionService conventionService; -// -// @Autowired -// public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) { -// super(options); -// this.conventionService = conventionService; -// } -// -// @EventListener -// public void handleUserTouchedEvent(UserTouchedEvent event) { -// if (!this.conventionService.isNullOrEmpty(event.getSubjectId())) this.evict(this.buildKey(event.getSubjectId())); -// if (!this.conventionService.isNullOrEmpty(event.getPreviousSubjectId())) this.evict(this.buildKey(event.getPreviousSubjectId())); -// } -// -// @Override -// protected Class valueClass() { -// return UserInterceptorCacheValue.class; -// } -// -// @Override -// public String keyOf(UserInterceptorCacheValue value) { -// return this.buildKey(value.getSubjectId()); -// } -// -// -// public String buildKey(String subject) { -// return this.generateKey(new HashMap<>() {{ -// put("$subject$", subject); -// }}); -// } -//} +package gr.cite.notification.web.scope.user; + +import gr.cite.notification.convention.ConventionService; +import gr.cite.tools.cache.CacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.UUID; + +@Service +public class UserInterceptorCacheService extends CacheService { + + public static class UserInterceptorCacheValue { + + public UserInterceptorCacheValue() { + } + + public UserInterceptorCacheValue(String subjectId, UUID userId) { + this.subjectId = subjectId; + this.userId = userId; + } + + + public String getSubjectId() { + return subjectId; + } + + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } + + private String subjectId; + private UUID userId; + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + } + + + @Autowired + public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) { + super(options); + } + + @Override + protected Class valueClass() { + return UserInterceptorCacheValue.class; + } + + @Override + public String keyOf(UserInterceptorCacheValue value) { + return this.buildKey(value.getSubjectId()); + } + + + public String buildKey(String subject) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$subject$", subject); + return this.generateKey(keyParts); + } +} diff --git a/notification-service/notification-web/src/main/resources/config/errors.yml b/notification-service/notification-web/src/main/resources/config/errors.yml index 79762a9e7..4094e5aed 100644 --- a/notification-service/notification-web/src/main/resources/config/errors.yml +++ b/notification-service/notification-web/src/main/resources/config/errors.yml @@ -26,8 +26,11 @@ error-thesaurus: non-person-principal: code: 108 message: the operation is available only to person users - blocking-consent: + tenant-not-allowed: code: 113 + message: tenant not allowed + blocking-consent: + code: 114 message: user consents are not sufficient to complete the operation single-tenant-configuration-per-type-supported: code: 116 @@ -41,3 +44,6 @@ error-thesaurus: overlapping-tenant-configuration-notifier-list: code: 119 message: Overlapping Tenant Configuration Notifier List + tenant-tampering: + code: 123 + message: Tenant tampering diff --git a/notification-service/notification-web/src/main/resources/config/idpclaims.yml b/notification-service/notification-web/src/main/resources/config/idpclaims.yml index 97ff4a10e..26e00f2a1 100644 --- a/notification-service/notification-web/src/main/resources/config/idpclaims.yml +++ b/notification-service/notification-web/src/main/resources/config/idpclaims.yml @@ -20,11 +20,25 @@ idpclient: Roles: - type: resource_access path: dmp_web.roles + - type: tenant_roles + filterBy: "(.*):::TenantCode::" + extractByExpression: "(.*):(.*)" + extractExpressionValue: "[[g1]]" + GlobalRoles: + - type: resource_access + path: dmp_web.roles + TenantRoles: + - type: tenant_roles + filterBy: "(.*):::TenantCode::" + extractByExpression: "(.*):(.*)" + extractExpressionValue: "[[g1]]" Scope: - type: scope AccessToken: - type: x-access-token visibility: SENSITIVE + Tenant: + - type: x-tenant IssuedAt: - type: iat Issuer: @@ -37,5 +51,8 @@ idpclient: - type: azp Authorities: - type: authorities - ExternalProviderName: - - type: identity_provider \ No newline at end of file + TenantCodes: + - type: tenant_roles + filterBy: "(.*):(.*)" + extractByExpression: "(.*):(.*)" + extractExpressionValue: "[[g2]]" \ No newline at end of file diff --git a/notification-service/notification-web/src/main/resources/config/permissions.yml b/notification-service/notification-web/src/main/resources/config/permissions.yml index a6d600777..2eb495a02 100644 --- a/notification-service/notification-web/src/main/resources/config/permissions.yml +++ b/notification-service/notification-web/src/main/resources/config/permissions.yml @@ -12,14 +12,14 @@ permissions: EditTenant: roles: - Admin - clients: [ ] + clients: [ "opendmp-api-dev" ] allowAnonymous: false allowAuthenticated: false DeleteTenant: roles: - Admin claims: [ ] - clients: [ ] + clients: [ "opendmp-api-dev" ] allowAnonymous: false allowAuthenticated: false AllowNoTenant: @@ -32,85 +32,85 @@ permissions: # Users BrowseUser: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: true allowAuthenticated: false EditUser: roles: - - Admin - clients: [ ] + - TenantAdmin + clients: [ "opendmp-api-dev" ] allowAnonymous: false allowAuthenticated: false DeleteUser: roles: - - Admin + - TenantAdmin claims: [ ] - clients: [ ] + clients: [ "opendmp-api-dev" ] allowAnonymous: false allowAuthenticated: false # UserContactInfo BrowseUserContactInfo: roles: - - Admin - clients: [ ] + - TenantAdmin + clients: [ "opendmp-api-dev" ] allowAnonymous: true allowAuthenticated: false EditUserContactInfo: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false DeleteUserContactInfo: roles: - - Admin + - TenantAdmin claims: [ ] - clients: [ ] + clients: [ "opendmp-api-dev" ] allowAnonymous: false allowAuthenticated: false #Notification BrowseNotification: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: true allowAuthenticated: false EditNotification: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: true allowAuthenticated: false DeleteNotification: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false #Tenant Configuration BrowseTenantConfiguration: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false EditTenantConfiguration: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false #User Notification Preference BrowseUserNotificationPreference: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: true allowAuthenticated: false EditUserNotificationPreference: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false @@ -118,25 +118,25 @@ permissions: # ViewPage Permissions ViewNotificationPage: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false ViewNotificationEventRulePage: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false ViewInAppNotificationPage: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false ViewNotificationTemplatePage: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false @@ -144,19 +144,19 @@ permissions: # Notification Template Permissions BrowseNotificationTemplate: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false EditNotificationTemplate: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false DeleteNotificationTemplate: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false @@ -164,13 +164,13 @@ permissions: # In App Notification Permissions BrowseInAppNotification: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false DeleteInAppNotification: roles: - - Admin + - TenantAdmin clients: [ ] allowAnonymous: false allowAuthenticated: false \ No newline at end of file diff --git a/notification-service/notification-web/src/main/resources/config/queue.yml b/notification-service/notification-web/src/main/resources/config/queue.yml index 063ca478d..accbbca49 100644 --- a/notification-service/notification-web/src/main/resources/config/queue.yml +++ b/notification-service/notification-web/src/main/resources/config/queue.yml @@ -25,9 +25,6 @@ queue: enable: false options: exchange: null - forget-me-completed-topic: forgetme.completed - what-you-know-about-me-completed-topic: whatyouknowaboutme.completed - generate-file-topic: generate.file rabbitmq: enable: false interval-seconds: 30 diff --git a/notification-service/notification-web/src/main/resources/config/tenant-devel.yml b/notification-service/notification-web/src/main/resources/config/tenant-devel.yml new file mode 100644 index 000000000..f2149021a --- /dev/null +++ b/notification-service/notification-web/src/main/resources/config/tenant-devel.yml @@ -0,0 +1,7 @@ +tenant: + multitenancy: + is-multitenant: true + default-tenant-code: default + interceptor: + client-claims-prefix: client_ + enforce-trusted-tenant: false \ No newline at end of file diff --git a/notification-service/notification-web/src/main/resources/config/tenant.yml b/notification-service/notification-web/src/main/resources/config/tenant.yml index 7fbc2f91c..1b389e623 100644 --- a/notification-service/notification-web/src/main/resources/config/tenant.yml +++ b/notification-service/notification-web/src/main/resources/config/tenant.yml @@ -2,7 +2,6 @@ tenant: multitenancy: is-multitenant: false interceptor: - client-claims-prefix: client_ white-listed-clients: [ ] enforce-trusted-tenant: false - white-listed-endpoints: [ '/api/principal/my-tenants', '/api/principal/me','/api/user/user-settings', '/error', '/api/tenant-request' ] + white-listed-endpoints: [ '/api/principal/me' ] \ No newline at end of file diff --git a/notification-service/notification/src/main/java/gr/cite/notification/authorization/ClaimNames.java b/notification-service/notification/src/main/java/gr/cite/notification/authorization/ClaimNames.java new file mode 100644 index 000000000..c59d6a58e --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/authorization/ClaimNames.java @@ -0,0 +1,9 @@ +package gr.cite.notification.authorization; + +public class ClaimNames { + public static final String ExternalProviderName = "ExternalProviderName"; + public static final String TenantCodesClaimName = "TenantCodes"; + public static final String TenantClaimName = "x-tenant"; + public static final String GlobalRolesClaimName = "GlobalRoles"; + public static final String TenantRolesClaimName = "TenantRoles"; +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/MultitenancyProperties.java b/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/MultitenancyProperties.java index 56f8dd353..7fedb1b92 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/MultitenancyProperties.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/MultitenancyProperties.java @@ -5,6 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "tenant.multitenancy") public class MultitenancyProperties { private boolean isMultitenant; + private String defaultTenantCode; public boolean isMultitenant() { return isMultitenant; @@ -13,4 +14,16 @@ public class MultitenancyProperties { public void setIsMultitenant(boolean multitenant) { isMultitenant = multitenant; } + + public void setMultitenant(boolean multitenant) { + isMultitenant = multitenant; + } + + public String getDefaultTenantCode() { + return defaultTenantCode; + } + + public void setDefaultTenantCode(String defaultTenantCode) { + this.defaultTenantCode = defaultTenantCode; + } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/TenantScope.java b/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/TenantScope.java index 99111183a..473fa321a 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/TenantScope.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/common/scope/tenant/TenantScope.java @@ -1,34 +1,25 @@ package gr.cite.notification.common.scope.tenant; import gr.cite.notification.data.tenant.TenantScopedBaseEntity; -import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; import org.hibernate.Session; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.boot.BootstrapRegistry; -import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; import javax.management.InvalidApplicationException; -import jakarta.persistence.EntityManager; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; @Component @RequestScope public class TenantScope { public static final String TenantReplaceParameter = "::TenantCode::"; - public static final String TenantCodesClaimName = "TenantCodes"; - public static final String TenantClaimName = "x-tenant"; - - private MultitenancyProperties multitenancy; - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScope.class)); - private UUID tenant = null; - private String tenantCode = null; - private UUID initialTenant = null; - private String initialTenantCode = null; + private final MultitenancyProperties multitenancy; + private final AtomicReference tenant = new AtomicReference<>(); + private final AtomicReference tenantCode = new AtomicReference<>(); + private final AtomicReference initialTenant = new AtomicReference<>(); + private final AtomicReference initialTenantCode = new AtomicReference<>(); @Autowired public TenantScope(MultitenancyProperties multitenancy) { @@ -39,49 +30,79 @@ public class TenantScope { return multitenancy.isMultitenant(); } + public String getDefaultTenantCode() { + return multitenancy.getDefaultTenantCode(); + } + public Boolean isSet() { - if (!this.isMultitenant()) return true; - return this.tenant != null; + if (!this.isMultitenant()) + return Boolean.TRUE; + return this.tenant.get() != null || this.isDefaultTenant(); + } + + public Boolean isDefaultTenant() { + if (!this.isMultitenant()) + return Boolean.TRUE; + return this.multitenancy.getDefaultTenantCode().equalsIgnoreCase(this.tenantCode.get()); } public UUID getTenant() throws InvalidApplicationException { - if (!this.isMultitenant()) return null; - if (this.tenant == null) throw new InvalidApplicationException("tenant not set"); - return this.tenant; + if (!this.isMultitenant()) + return null; + if (this.tenant.get() == null && !this.isDefaultTenant()) + throw new InvalidApplicationException("tenant not set"); + return this.isDefaultTenant() ? null : this.tenant.get(); } public String getTenantCode() throws InvalidApplicationException { - if (!this.isMultitenant()) return null; - if (this.tenant == null) throw new InvalidApplicationException("tenant not set"); - return this.tenantCode; + if (!this.isMultitenant()) + return null; + if (this.tenantCode.get() == null) + throw new InvalidApplicationException("tenant not set"); + return this.tenantCode.get(); } public void setTempTenant(EntityManager entityManager, UUID tenant, String tenantCode) { - this.tenant = tenant; + this.tenant.set(tenant); + this.tenantCode.set(tenantCode); - if(this.tenant != null) { - entityManager - .unwrap(Session.class) - .enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.tenant.toString()); + if (this.tenant.get() != null && !this.isDefaultTenant()) { + if(!this.isDefaultTenant()) { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, this.tenant.get().toString()); + } else { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } } - public void removeTempTenant(EntityManager entityManager) { - this.tenant = this.initialTenant; - this.tenantCode = this.initialTenantCode; - if(this.initialTenant != null) { - entityManager - .unwrap(Session.class) - .enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.initialTenant.toString()); + public void removeTempTenant(EntityManager entityManager) { + this.tenant.set(this.initialTenant.get()); + this.tenantCode.set(this.initialTenantCode.get()); + if (this.initialTenant.get() != null && !this.isDefaultTenant()) { + if(!this.isDefaultTenant()) { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, this.tenant.get().toString()); + } else { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } } public void setTenant(UUID tenant, String tenantCode) { if (this.isMultitenant()) { - this.tenant = tenant; - this.initialTenant = tenant; - this.tenantCode = tenantCode; - this.initialTenantCode = tenantCode; + this.tenant.set(tenant); + this.initialTenant.set(tenant); + this.tenantCode.set(tenantCode); + this.initialTenantCode.set(tenantCode); } } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/LanguageEntity.java b/notification-service/notification/src/main/java/gr/cite/notification/data/LanguageEntity.java deleted file mode 100644 index d7ef04cec..000000000 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/LanguageEntity.java +++ /dev/null @@ -1,94 +0,0 @@ -package gr.cite.notification.data; - -import gr.cite.notification.common.enums.IsActive; -import gr.cite.notification.data.conventers.IsActiveConverter; -import gr.cite.notification.data.tenant.TenantScopedBaseEntity; -import jakarta.persistence.*; - -import java.time.Instant; -import java.util.UUID; - -@Entity -@Table(name = "\"Language\"") -public class LanguageEntity extends TenantScopedBaseEntity { - - @Id - @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) - private UUID id; - - public static final String _id = "id"; - - @Column(name = "code", length = 20, nullable = false) - private String code; - - public static final String _code = "code"; - - @Column(name = "ordinal") - private Integer ordinal; - - public static final String _ordinal = "ordinal"; - - @Column(name = "\"created_at\"", nullable = false) - private Instant createdAt; - - public static final String _createdAt = "createdAt"; - - @Column(name = "\"updated_at\"", nullable = false) - private Instant updatedAt; - - public static final String _updatedAt = "updatedAt"; - - @Column(name = "is_active", nullable = false) - @Convert(converter = IsActiveConverter.class) - private IsActive isActive; - - public static final String _isActive = "isActive"; - - public UUID getId() { - return id; - } - - public void setId(UUID id) { - this.id = id; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public Integer getOrdinal() { - return ordinal; - } - - public void setOrdinal(Integer ordinal) { - this.ordinal = ordinal; - } - - public Instant getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Instant createdAt) { - this.createdAt = createdAt; - } - - public Instant getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(Instant updatedAt) { - this.updatedAt = updatedAt; - } - - public IsActive getIsActive() { - return isActive; - } - - public void setIsActive(IsActive isActive) { - this.isActive = isActive; - } -} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/NotificationTemplateEntity.java b/notification-service/notification/src/main/java/gr/cite/notification/data/NotificationTemplateEntity.java index 20f1c9f71..f8b4db388 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/NotificationTemplateEntity.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/NotificationTemplateEntity.java @@ -39,10 +39,10 @@ public class NotificationTemplateEntity extends TenantScopedBaseEntity { public static final String _kind = "kind"; - @Column(name = "\"language\"", columnDefinition = "uuid", nullable = false) - private UUID languageId; + @Column(name = "\"language_code\"", nullable = false, length = 200) + private String languageCode; - public static final String _languageId = "languageId"; + public static final String _languageCode = "languageCode"; @Column(name = "\"value\"", nullable = false) private String value; @@ -97,12 +97,12 @@ public class NotificationTemplateEntity extends TenantScopedBaseEntity { this.kind = kind; } - public UUID getLanguageId() { - return languageId; + public String getLanguageCode() { + return languageCode; } - public void setLanguageId(UUID languageId) { - this.languageId = languageId; + public void setLanguageCode(String languageCode) { + this.languageCode = languageCode; } public String getValue() { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java b/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java new file mode 100644 index 000000000..fd7d63266 --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java @@ -0,0 +1,116 @@ +package gr.cite.notification.data; + +import gr.cite.notification.common.scope.tenant.TenantScope; +import gr.cite.notification.common.scope.tenant.TenantScoped; +import gr.cite.notification.data.tenant.TenantScopedBaseEntity; +import gr.cite.notification.errorcode.ErrorThesaurusProperties; +import gr.cite.tools.exception.MyForbiddenException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.PersistenceContext; +import org.hibernate.Session; +import org.springframework.stereotype.Service; +import org.springframework.web.context.annotation.RequestScope; + +import javax.management.InvalidApplicationException; + +@Service +@RequestScope +public class TenantEntityManager { + @PersistenceContext + private EntityManager entityManager; + private final TenantScope tenantScope; + private final ErrorThesaurusProperties errors; + + public TenantEntityManager(TenantScope tenantScope, ErrorThesaurusProperties errors) { + this.tenantScope = tenantScope; + this.errors = errors; + } + + + public void persist(Object entity) { + this.entityManager.persist(entity); + } + + public T merge(T entity) throws InvalidApplicationException { + if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!tenantScope.isDefaultTenant()) { + if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } else if (tenantScopedEntity.getTenantId() != null) { + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + } + return this.entityManager.merge(entity); + } + + public void remove(Object entity) throws InvalidApplicationException { + if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!tenantScope.isDefaultTenant()) { + if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } else if (tenantScopedEntity.getTenantId() != null) { + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + } + this.entityManager.remove(entity); + } + + public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { + T entity = this.entityManager.find(entityClass, primaryKey); + + if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) return null; + } + return entity; + } + + public void flush() { + this.entityManager.flush(); + } + + + public void setFlushMode(FlushModeType flushMode) { + this.entityManager.setFlushMode(flushMode); + + } + + public FlushModeType getFlushMode() { + return this.entityManager.getFlushMode(); + } + + public void clear() { + this.entityManager.clear(); + } + + public void enableTenantFilters() throws InvalidApplicationException { + if (!tenantScope.isSet()) return; + if(!tenantScope.isDefaultTenant()) { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString()); + } else { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } + } + + public void disableTenantFilters(){ + this.entityManager + .unwrap(Session.class) + .disableFilter(TenantScopedBaseEntity.TENANT_FILTER); + + this.entityManager + .unwrap(Session.class) + .disableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } + + public EntityManager getEntityManager() { + return entityManager; + } + + public void setEntityManager(EntityManager entityManager) { + this.entityManager = entityManager; + } + +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/TenantScopedEntityManager.java b/notification-service/notification/src/main/java/gr/cite/notification/data/TenantScopedEntityManager.java deleted file mode 100644 index b5c1a866f..000000000 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/TenantScopedEntityManager.java +++ /dev/null @@ -1,97 +0,0 @@ -package gr.cite.notification.data; - -import gr.cite.commons.web.authz.service.AuthorizationService; -import gr.cite.notification.authorization.Permission; -import gr.cite.notification.common.scope.tenant.TenantScope; -import gr.cite.notification.common.scope.tenant.TenantScoped; -import gr.cite.tools.exception.MyForbiddenException; -import org.hibernate.Session; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Service; - -import javax.management.InvalidApplicationException; -import jakarta.persistence.EntityManager; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Query; -import java.util.UUID; - -@Service -@Scope -public class TenantScopedEntityManager { - @PersistenceContext - private EntityManager entityManager; - private final AuthorizationService authorizationService; - - private final TenantScope tenantScope; - - public TenantScopedEntityManager(AuthorizationService authorizationService, TenantScope tenantScope) { - this.authorizationService = authorizationService; - this.tenantScope = tenantScope; - } - - public int getBulkSize() { - Session session = this.entityManager.unwrap(Session.class); - return session.getJdbcBatchSize(); - } - - public void setBulkSize(int size) { - Session session = this.entityManager.unwrap(Session.class); - session.setJdbcBatchSize(size); - } - public Query createQuery(String query){ - return this.entityManager.createQuery(query); - } - public void persist(Object entity) { - this.entityManager.persist(entity); - } - - public T merge(T entity) throws InvalidApplicationException { - if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { - boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant); - - final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; - if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering"); - } - return this.entityManager.merge(entity); - } - - public void remove(Object entity) throws InvalidApplicationException { - if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { - final UUID tenantId = tenantScope.getTenant(); - if (!tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering"); - } - this.entityManager.remove(entity); - } - - public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { - T entity = this.entityManager.find(entityClass, primaryKey); - - if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { - boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant); - - final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; - if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) return null; - } - return entity; - } - - public void flush() { - this.entityManager.flush(); - } - - - public void setFlushMode(FlushModeType flushMode) { - this.entityManager.setFlushMode(flushMode); - - } - - public FlushModeType getFlushMode() { - return this.entityManager.getFlushMode(); - } - - public void clear() { - this.entityManager.clear(); - } - -} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantFilterAspect.java b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantFilterAspect.java index 468755369..2ac3d8397 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantFilterAspect.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantFilterAspect.java @@ -1,48 +1,43 @@ package gr.cite.notification.data.tenant; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.notification.common.scope.tenant.TenantScope; +import jakarta.persistence.EntityManager; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import javax.management.InvalidApplicationException; -import jakarta.persistence.EntityManager; @Aspect @Component public class TenantFilterAspect { private final TenantScope tenantScope; - private final ClaimExtractor claimExtractor; - private final CurrentPrincipalResolver currentPrincipalResolver; - private final ApplicationContext applicationContext; @Autowired public TenantFilterAspect( - TenantScope tenantScope, - ClaimExtractor claimExtractor, - CurrentPrincipalResolver currentPrincipalResolver, - ApplicationContext applicationContext + TenantScope tenantScope ) { this.tenantScope = tenantScope; - this.currentPrincipalResolver = currentPrincipalResolver; - this.claimExtractor = claimExtractor; - this.applicationContext = applicationContext; } @AfterReturning( - pointcut="bean(entityManagerFactory) && execution(* createEntityManager(..))", - returning="retVal") + pointcut = "bean(entityManagerFactory) && execution(* createEntityManager(..))", + returning = "retVal") public void getSessionAfter(JoinPoint joinPoint, Object retVal) throws InvalidApplicationException { - if (retVal != null && EntityManager.class.isInstance(retVal) && tenantScope.isSet() && tenantScope.isMultitenant()) { + if (retVal instanceof EntityManager && tenantScope.isSet()) { Session session = ((EntityManager) retVal).unwrap(Session.class); - session.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); + if(!tenantScope.isDefaultTenant()) { + session + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString()); + } else { + session + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java index a7c9901d3..04b6ad2fb 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java @@ -2,33 +2,44 @@ package gr.cite.notification.data.tenant; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.common.scope.tenant.TenantScoped; +import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreRemove; +import jakarta.persistence.PreUpdate; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import javax.management.InvalidApplicationException; -import jakarta.persistence.PrePersist; -import jakarta.persistence.PreRemove; -import jakarta.persistence.PreUpdate; import java.util.UUID; public class TenantListener { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantListener.class)); private final TenantScope tenantScope; + private final ErrorThesaurusProperties errors; + + @Autowired public TenantListener( - TenantScope tenantScope + TenantScope tenantScope, ErrorThesaurusProperties errors ) { this.tenantScope = tenantScope; + this.errors = errors; } @PrePersist public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { if (tenantScope.isMultitenant()) { - final UUID tenantId = tenantScope.getTenant(); - entity.setTenantId(tenantId); + if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) { + logger.error("somebody tried to set not login tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + if (!tenantScope.isDefaultTenant()) { + final UUID tenantId = tenantScope.getTenant(); + entity.setTenantId(tenantId); + } } else { entity.setTenantId(null); } @@ -38,22 +49,30 @@ public class TenantListener { @PreRemove public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { if (tenantScope.isMultitenant()) { - if (entity.getTenantId() == null) { - logger.error("somebody tried to set null tenant"); - throw new MyForbiddenException("tenant tampering"); - } - if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { - logger.error("somebody tried to change an entries tenant"); - throw new MyForbiddenException("tenant tampering"); - } + if (!tenantScope.isDefaultTenant()) { + if (entity.getTenantId() == null) { + logger.error("somebody tried to set null tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { + logger.error("somebody tried to change an entries tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } - final UUID tenantId = tenantScope.getTenant(); - entity.setTenantId(tenantId); + final UUID tenantId = tenantScope.getTenant(); + entity.setTenantId(tenantId); + } else { + if (entity.getTenantId() != null) { + logger.error("somebody tried to set null tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + } } else { - if (entity.getTenantId() != null) { - logger.error("somebody tried to set non null tenant"); - throw new MyForbiddenException("tenant tampering"); + if (entity.getTenantId() != null && (!this.tenantScope.isDefaultTenant() ||entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) { + logger.error("somebody tried to change an entries tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } } + } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantScopedBaseEntity.java b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantScopedBaseEntity.java index 8feaf544b..57b499883 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantScopedBaseEntity.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantScopedBaseEntity.java @@ -1,15 +1,13 @@ package gr.cite.notification.data.tenant; import gr.cite.notification.common.scope.tenant.TenantScoped; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; import org.hibernate.annotations.Filter; import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.ParamDef; -import jakarta.persistence.Column; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.MappedSuperclass; - -import java.io.Serial; import java.io.Serializable; import java.util.UUID; @@ -17,23 +15,20 @@ import java.util.UUID; //@Getter //@Setter //@NoArgsConstructor -@FilterDef(name = TenantScopedBaseEntity.tenantFilter, parameters = {@ParamDef(name = TenantScopedBaseEntity.tenantFilterTenantParam, type = String.class)}) -@Filter(name = "tenantFilter", condition = "tenant = (cast(:tenantId as uuid))") +@FilterDef(name = TenantScopedBaseEntity.TENANT_FILTER, parameters = {@ParamDef(name = TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, type = String.class)}) +@FilterDef(name = TenantScopedBaseEntity.DEFAULT_TENANT_FILTER) +@Filter(name = TenantScopedBaseEntity.DEFAULT_TENANT_FILTER, condition = "(tenant = tenant is null)") +@Filter(name = TenantScopedBaseEntity.TENANT_FILTER, condition = "(tenant = (cast(:tenantId as uuid)) or tenant is null)") @EntityListeners(TenantListener.class) public abstract class TenantScopedBaseEntity implements TenantScoped, Serializable { - - @Serial private static final long serialVersionUID = 1L; + public static final String TENANT_FILTER = "tenantFilter"; + public static final String DEFAULT_TENANT_FILTER = "defaultTenantFilter"; + public static final String TENANT_FILTER_TENANT_PARAM = "tenantId"; - public static final String tenantFilter = "tenantFilter"; - - public static final String tenantFilterTenantParam = "tenantId"; - - @Column(name = "tenant", columnDefinition = "uuid") + @Column(name = "tenant", columnDefinition = "uuid", nullable = true) private UUID tenantId; - public static final String _tenantId = "tenantId"; - public UUID getTenantId() { return tenantId; } @@ -42,5 +37,5 @@ public abstract class TenantScopedBaseEntity implements TenantScoped, Serializab public void setTenantId(UUID tenantId) { this.tenantId = tenantId; } - } + diff --git a/notification-service/notification/src/main/java/gr/cite/notification/errorcode/ErrorThesaurusProperties.java b/notification-service/notification/src/main/java/gr/cite/notification/errorcode/ErrorThesaurusProperties.java index d19b62512..87fa3ea60 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/errorcode/ErrorThesaurusProperties.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/errorcode/ErrorThesaurusProperties.java @@ -15,6 +15,8 @@ public class ErrorThesaurusProperties { private ErrorDescription singleTenantConfigurationPerTypeSupported; private ErrorDescription incompatibleTenantConfigurationTypes; private ErrorDescription overlappingTenantConfigurationNotifierList; + private ErrorDescription tenantNotAllowed; + private ErrorDescription tenantTampering; public ErrorDescription getHashConflict() { return hashConflict; @@ -89,4 +91,19 @@ public class ErrorThesaurusProperties { this.overlappingTenantConfigurationNotifierList = overlappingTenantConfigurationNotifierList; } + public ErrorDescription getTenantNotAllowed() { + return tenantNotAllowed; + } + + public void setTenantNotAllowed(ErrorDescription tenantNotAllowed) { + this.tenantNotAllowed = tenantNotAllowed; + } + + public ErrorDescription getTenantTampering() { + return tenantTampering; + } + + public void setTenantTampering(ErrorDescription tenantTampering) { + this.tenantTampering = tenantTampering; + } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/OutboxIntegrationEventConfigurer.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/OutboxIntegrationEventConfigurer.java index 70ebc5361..efbaab157 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/OutboxIntegrationEventConfigurer.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/OutboxIntegrationEventConfigurer.java @@ -1,5 +1,6 @@ package gr.cite.notification.integrationevent; +import gr.cite.notification.data.QueueOutboxEntity; import gr.cite.notification.integrationevent.outbox.OutboxProperties; import gr.cite.notification.integrationevent.outbox.OutboxRepositoryImpl; import gr.cite.queueoutbox.IntegrationEventContextCreator; @@ -56,7 +57,11 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer { @Bean public IntegrationEventContextCreator integrationEventContextCreator() { - return (message) -> new IntegrationEventContextImpl(); + return (message) -> { + IntegrationEventContextImpl integrationEventContext = new IntegrationEventContextImpl(); + if (message instanceof QueueOutboxEntity) integrationEventContext.setTenant(((QueueOutboxEntity)message).getTenantId()); + return integrationEventContext; + }; } @Bean diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java index 9fbc1521e..288c11095 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxPrincipal.java @@ -1,6 +1,7 @@ package gr.cite.notification.integrationevent.inbox; import gr.cite.commons.web.oidc.principal.MyPrincipal; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import org.springframework.security.oauth2.core.ClaimAccessor; import org.springframework.security.oauth2.jwt.JwtClaimNames; @@ -22,12 +23,15 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor { this.isAuthenticated = isAuthenticated; } - public static InboxPrincipal build(IntegrationEventProperties properties) { + public static InboxPrincipal build(IntegrationEventProperties properties, ClaimExtractorProperties claimExtractorProperties) { InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId"); - inboxPrincipal.put("client_id", properties.getAppId()); + List clientKey = claimExtractorProperties.getMapping().getOrDefault("Client", null); + inboxPrincipal.put(clientKey != null && clientKey.getFirst() != null ? clientKey.getFirst().getType() : "client_id", properties.getAppId()); inboxPrincipal.put("active", "true"); - inboxPrincipal.put("nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString()); - inboxPrincipal.put("exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString()); + List notBeforeKey = claimExtractorProperties.getMapping().getOrDefault("NotBefore", null); + inboxPrincipal.put(notBeforeKey != null && notBeforeKey.getFirst() != null ? notBeforeKey.getFirst().getType() :"nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString()); + List expiresAt = claimExtractorProperties.getMapping().getOrDefault("ExpiresAt", null); + inboxPrincipal.put(expiresAt != null && expiresAt.getFirst() != null ? expiresAt.getFirst().getType() :"exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString()); return inboxPrincipal; } @@ -45,7 +49,10 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor { public List getClaimAsStringList(String claim) { if (claims == null) return null; - return this.getClaimAsStringList(claim); + if (this.claims.containsKey(claim)){ + return List.of(this.claims.get(claim).toString()); + } + return null; } @Override diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java index 148da2082..890b03f27 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/InboxRepositoryImpl.java @@ -4,6 +4,7 @@ import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.scope.fake.FakeRequestScope; import gr.cite.notification.data.QueueInboxEntity; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.integrationevent.TrackedEvent; import gr.cite.notification.integrationevent.inbox.notify.NotifyIntegrationEventHandler; import gr.cite.notification.integrationevent.inbox.tenantremoval.TenantRemovalIntegrationEventHandler; @@ -16,6 +17,7 @@ import gr.cite.queueinbox.entity.QueueInboxStatus; import gr.cite.queueinbox.repository.CandidateInfo; import gr.cite.queueinbox.repository.InboxRepository; import gr.cite.queueinbox.task.MessageOptions; +import gr.cite.rabbitmq.IntegrationEventMessageConstants; import gr.cite.rabbitmq.consumer.InboxCreatorParams; import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.QueryFactory; @@ -241,7 +243,14 @@ public class InboxRepositoryImpl implements InboxRepository { QueueInboxEntity queueMessage = new QueueInboxEntity(); queueMessage.setId(UUID.randomUUID()); - queueMessage.setTenantId(null); + Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null; + if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId); + else if (tenantId instanceof String) { + try { + queueMessage.setTenantId(UUID.fromString((String) tenantId)); + } catch (Exception e) { + } + } queueMessage.setExchange(this.inboxProperties.getExchange()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setQueue(inboxCreatorParams.getQueueName()); @@ -269,6 +278,10 @@ public class InboxRepositoryImpl implements InboxRepository { entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); + + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + transaction.begin(); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); @@ -278,7 +291,7 @@ public class InboxRepositoryImpl implements InboxRepository { logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId()); } else { - EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); + EventProcessingStatus status = this.processMessage(queueInboxMessage); switch (status) { case Success: { queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); @@ -321,38 +334,39 @@ public class InboxRepositoryImpl implements InboxRepository { return success; } - private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { + private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) { IntegrationEventHandler handler; - logger.debug("Processing message with routing key '{}'", routingKey); - if (this.routingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic())) - handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic())) + logger.debug("Processing message with routing key '{}'", queueInboxMessage.getRoute()); + if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantRemovalTopic())) handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantTouchedTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantTouchedTopic())) handler = this.applicationContext.getBean(TenantTouchedIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserRemovalTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getUserRemovalTopic())) handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchedTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getUserTouchedTopic())) handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getNotifyTopic())) + handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class); else { - logger.error("No handler found for message routing key '{}'. Discarding.", routingKey); + logger.error("No handler found for message routing key '{}'. Discarding.", queueInboxMessage.getRoute()); handler = null; } - + if (handler == null) return EventProcessingStatus.Discard; IntegrationEventProperties properties = new IntegrationEventProperties(); - properties.setAppId(appId); - properties.setMessageId(messageId); + properties.setAppId(queueInboxMessage.getApplicationId()); + properties.setMessageId(queueInboxMessage.getMessageId().toString()); + properties.setTenantId(queueInboxMessage.getTenantId()); - TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, message); + TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, queueInboxMessage.getMessage()); // using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag)) // { try { - return handler.handle(properties, message); + return handler.handle(properties, queueInboxMessage.getMessage()); } catch (Exception ex) { - logger.error("problem handling event from routing key " + routingKey + ". Setting nack and continuing...", ex); + logger.error("problem handling event from routing key " + queueInboxMessage.getRoute() + ". Setting nack and continuing...", ex); return EventProcessingStatus.Error; } // } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java index 9d5fcea93..cb07b4c41 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/IntegrationEventProperties.java @@ -1,10 +1,13 @@ package gr.cite.notification.integrationevent.inbox; +import java.util.UUID; + public class IntegrationEventProperties { private String messageId; private String appId; + private UUID tenantId; public String getMessageId() { return messageId; @@ -22,4 +25,11 @@ public class IntegrationEventProperties { this.appId = appId; } + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java index b07162793..f11558abd 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEvent.java @@ -9,8 +9,6 @@ public class NotifyIntegrationEvent extends TrackedEvent { private UUID userId; - private UUID tenantId; - private UUID notificationType; private NotificationContactType contactTypeHint; @@ -32,14 +30,6 @@ public class NotifyIntegrationEvent extends TrackedEvent { this.userId = userId; } - public UUID getTenantId() { - return tenantId; - } - - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - public UUID getNotificationType() { return notificationType; } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java index 8a5b37d03..622185c5b 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/notify/NotifyIntegrationEventHandlerImpl.java @@ -1,6 +1,7 @@ package gr.cite.notification.integrationevent.inbox.notify; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.notification.audit.AuditableAction; import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.enums.NotificationNotifyState; @@ -9,6 +10,7 @@ import gr.cite.notification.common.enums.NotificationTrackingState; import gr.cite.notification.common.scope.fake.FakeRequestScope; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.InboxPrincipal; @@ -46,17 +48,30 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent private final JsonHandlingService jsonHandlingService; - private final ApplicationContext applicationContext; private final ErrorThesaurusProperties errors; private final MessageSource messageSource; - - public NotifyIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) { + private final QueryFactory queryFactory; + private final TenantScope tenantScope; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final NotifyConsistencyHandler notifyConsistencyHandler; + private final NotificationService notificationService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; + public NotifyIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ErrorThesaurusProperties errors, MessageSource messageSource, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, NotifyConsistencyHandler notifyConsistencyHandler, NotificationService notificationService, AuditService auditService, TenantEntityManager tenantEntityManager) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; this.errors = errors; this.messageSource = messageSource; + this.queryFactory = queryFactory; + this.tenantScope = tenantScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.notifyConsistencyHandler = notifyConsistencyHandler; + this.notificationService = notificationService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -84,72 +99,42 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent model.setProvenanceRef(event.getProvenanceRef()); model.setNotifiedAt(Instant.now()); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && event.getTenantId() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(event.getTenantId(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { logger.error("missing tenant from event message"); return EventProcessingStatus.Error; } - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties)); - - NotifyConsistencyHandler notifyConsistencyHandler = this.applicationContext.getBean(NotifyConsistencyHandler.class); - if (!(notifyConsistencyHandler.isConsistent(new NotifyConsistencyPredicates(event.getUserId(), event.getContactTypeHint(), event.getContactHint())))) - return EventProcessingStatus.Postponed; - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - NotificationService notificationService = this.applicationContext.getBean(NotificationService.class); - notificationService.persist(model, new BaseFieldSet()); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.Notification_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("id", event.getUserId()) - )); - //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { +// logger.error("missing tenant from event message"); +// return EventProcessingStatus.Error; + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + if (!(notifyConsistencyHandler.isConsistent(new NotifyConsistencyPredicates(event.getUserId(), event.getContactTypeHint(), event.getContactHint())))) { + status = EventProcessingStatus.Postponed; + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); + return status; + } + notificationService.persist(model, new BaseFieldSet()); + + auditService.track(AuditableAction.Notification_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getUserId()) + )); } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); } - return EventProcessingStatus.Success; + return status; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java index a6e5d27ed..7f4a6e121 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandler.java @@ -1,5 +1,6 @@ package gr.cite.notification.integrationevent.inbox.tenantremoval; + import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; public interface TenantRemovalIntegrationEventHandler extends IntegrationEventHandler { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java index c27824f3f..c53db00f5 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java @@ -1,24 +1,18 @@ package gr.cite.notification.integrationevent.inbox.tenantremoval; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.notification.audit.AuditableAction; import gr.cite.notification.common.JsonHandlingService; -import gr.cite.notification.common.scope.fake.FakeRequestScope; -import gr.cite.notification.errorcode.ErrorThesaurusProperties; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; import gr.cite.notification.service.tenant.TenantService; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.logging.LoggerService; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -32,18 +26,20 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantRemovalIntegrationEventHandlerImpl.class)); private final JsonHandlingService jsonHandlingService; - - private final ApplicationContext applicationContext; - - private final ErrorThesaurusProperties errors; - - private final MessageSource messageSource; - - public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) { + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final TenantService tenantService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; + private final TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler; + public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager, TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; - this.errors = errors; - this.messageSource = messageSource; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.tenantService = tenantService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; + this.tenantRemovalConsistencyHandler = tenantRemovalConsistencyHandler; } @Override @@ -52,60 +48,34 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn if (event == null) return EventProcessingStatus.Error; - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties)); + EventProcessingStatus status = EventProcessingStatus.Success; + try { - TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler = this.applicationContext.getBean(TenantRemovalConsistencyHandler.class); - if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) - return EventProcessingStatus.Postponed; + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - TenantService tenantService = this.applicationContext.getBean(TenantService.class); - tenantService.deleteAndSave(event.getId()); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( - new AbstractMap.SimpleEntry("id", event.getId()) - )); - //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - - transaction.commit(); - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) { + status = EventProcessingStatus.Postponed; + currentPrincipalResolver.pop(); + return status; } + + tenantEntityManager.disableTenantFilters(); + tenantService.deleteAndSave(event.getId()); + + + auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); + + } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); } - return null; + return status; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java index 899d22db8..03419f001 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEvent.java @@ -1,5 +1,6 @@ package gr.cite.notification.integrationevent.inbox.tenanttouched; + import gr.cite.notification.integrationevent.TrackedEvent; import java.util.UUID; diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java index 013a2698f..deb8506d9 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java @@ -1,9 +1,10 @@ package gr.cite.notification.integrationevent.inbox.tenanttouched; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.notification.audit.AuditableAction; import gr.cite.notification.common.JsonHandlingService; -import gr.cite.notification.common.scope.fake.FakeRequestScope; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; @@ -12,13 +13,8 @@ import gr.cite.notification.service.tenant.TenantService; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.validation.ValidatorFactory; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -31,15 +27,23 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantTouchedIntegrationEventHandlerImpl.class)); - protected final ApplicationContext applicationContext; private final JsonHandlingService jsonHandlingService; private final ValidatorFactory validatorFactory; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final TenantService tenantService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; - public TenantTouchedIntegrationEventHandlerImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory) { - this.applicationContext = applicationContext; + public TenantTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager) { this.jsonHandlingService = jsonHandlingService; this.validatorFactory = validatorFactory; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.tenantService = tenantService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -53,55 +57,25 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn model.setCode(event.getCode()); this.validatorFactory.validator(TenantTouchedIntegrationEventPersist.TenantTouchedIntegrationEventPersistValidator.class).validateForce(model); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties)); + EventProcessingStatus status = EventProcessingStatus.Success; + tenantEntityManager.disableTenantFilters(); + try { + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); + tenantService.persist(model, null); - transaction = entityManager.getTransaction(); - transaction.begin(); + auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", model) + )); - try { - TenantService tenantService = this.applicationContext.getBean(TenantService.class); - tenantService.persist(model, null); - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", model) - )); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - - transaction.commit(); - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); - } } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); } - return null; + return status; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java index 2c9974a7d..6762a943c 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalConsistencyPredicates.java @@ -1,5 +1,6 @@ package gr.cite.notification.integrationevent.inbox.userremoval; + import gr.cite.notification.integrationevent.inbox.ConsistencyPredicates; import java.util.UUID; diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java index 2ce9f613d..6e8a61805 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java @@ -8,7 +8,6 @@ public class UserRemovalIntegrationEvent extends TrackedEvent { private UUID userId; - private UUID tenant; public UUID getUserId() { return userId; @@ -17,12 +16,4 @@ public class UserRemovalIntegrationEvent extends TrackedEvent { public void setUserId(UUID userId) { this.userId = userId; } - - public UUID getTenant() { - return tenant; - } - - public void setTenant(UUID tenant) { - this.tenant = tenant; - } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java index 7bc8d1245..70a9504e5 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandler.java @@ -1,5 +1,6 @@ package gr.cite.notification.integrationevent.inbox.userremoval; + import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; public interface UserRemovalIntegrationEventHandler extends IntegrationEventHandler { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java index 326325373..426c77352 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -1,11 +1,12 @@ package gr.cite.notification.integrationevent.inbox.userremoval; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.notification.audit.AuditableAction; import gr.cite.notification.common.JsonHandlingService; -import gr.cite.notification.common.scope.fake.FakeRequestScope; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.InboxPrincipal; @@ -18,13 +19,8 @@ import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; import org.springframework.context.i18n.LocaleContextHolder; @@ -41,22 +37,35 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr private final JsonHandlingService jsonHandlingService; - private final ApplicationContext applicationContext; private final ErrorThesaurusProperties errors; private final MessageSource messageSource; + private final QueryFactory queryFactory; + private final TenantScope tenantScope; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final UserRemovalConsistencyHandler userRemovalConsistencyHandler; + private final UserService userService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; public UserRemovalIntegrationEventHandlerImpl( - JsonHandlingService jsonHandlingService, - ApplicationContext applicationContext, - ErrorThesaurusProperties errors, - MessageSource messageSource + JsonHandlingService jsonHandlingService, + ErrorThesaurusProperties errors, + MessageSource messageSource, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserRemovalConsistencyHandler userRemovalConsistencyHandler, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager ) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; this.errors = errors; this.messageSource = messageSource; + this.queryFactory = queryFactory; + this.tenantScope = tenantScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.userRemovalConsistencyHandler = userRemovalConsistencyHandler; + this.userService = userService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -69,73 +78,44 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName()); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && event.getTenant() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(event.getTenant(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { logger.error("missing tenant from event message"); return EventProcessingStatus.Error; } - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties)); - - UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); - if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) - return EventProcessingStatus.Postponed; - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - UserService userService = this.applicationContext.getBean(UserService.class); - userService.deleteAndSave(event.getUserId()); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.User_Delete, Map.ofEntries( - new AbstractMap.SimpleEntry("id", event.getUserId()) - )); - //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - - transaction.commit(); - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { +// logger.error("missing tenant from event message"); +// return EventProcessingStatus.Error; + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) { + status = EventProcessingStatus.Postponed; + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); + return status; + } + + userService.deleteAndSave(event.getUserId()); + + auditService.track(AuditableAction.User_Delete, Map.ofEntries( + new AbstractMap.SimpleEntry("id", event.getUserId()) + )); + //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); } - return EventProcessingStatus.Success; + + return status; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java index daa3d9a54..02daacb66 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEvent.java @@ -5,7 +5,7 @@ import gr.cite.notification.common.validation.BaseValidator; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.integrationevent.TrackedEvent; -import gr.cite.notification.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.specification.Specification; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.MessageSource; @@ -23,22 +23,24 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { public static final String _id = "id"; - private UUID tenant; - private String name; public static final String _name = "name"; public static final int _nameLength = 200; - private String subjectId; - - public static final String _subjectId = "subjectId"; - private UserProfile profile; private List userContactInfo; + public static final String _userContactInfo = "userContactInfo"; + private List tenantUsers; + + public static final String _tenantUsers = "tenantUsers"; + private List credentials; + + public static final String _credentials = "credentials"; + public UUID getId() { return id; } @@ -47,14 +49,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.id = id; } - public UUID getTenant() { - return tenant; - } - - public void setTenant(UUID tenant) { - this.tenant = tenant; - } - public String getName() { return name; } @@ -63,12 +57,20 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.name = name; } - public String getSubjectId() { - return subjectId; + public List getTenantUsers() { + return tenantUsers; } - public void setSubjectId(String subjectId) { - this.subjectId = subjectId; + public void setTenantUsers(List tenantUsers) { + this.tenantUsers = tenantUsers; + } + + public List getCredentials() { + return credentials; + } + + public void setCredentials(List credentials) { + this.credentials = credentials; } public UserProfile getProfile() { @@ -124,9 +126,15 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { private ContactInfoType type; + public static final String _type = "type"; + private String value; - private int ordinal; + public static final String _value = "value"; + + private Integer ordinal; + + public static final String _ordinal = "ordinal"; public ContactInfoType getType() { return type; @@ -144,16 +152,135 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.value = value; } - public int getOrdinal() { + public Integer getOrdinal() { return ordinal; } - public void setOrdinal(int ordinal) { + public void setOrdinal(Integer ordinal) { this.ordinal = ordinal; } + + @Component(UserTouchedIntegrationUserContactInfoEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationUserContactInfoEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationUserContactInfoEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationUserContactInfoEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return UserContactInfo.class; + } + + @Override + protected List specifications(UserContactInfo item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isEmpty(item.getValue())) + .failOn(UserContactInfo._value).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._value}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getType())) + .failOn(UserContactInfo._type).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._type}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getOrdinal())) + .failOn(UserContactInfo._ordinal).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._ordinal}, LocaleContextHolder.getLocale())) + ); + } + } } - @Component(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.ValidatorName) + public static class UserCredential { + + private String subjectId; + + public static final String _subjectId = "subjectId"; + + public String getSubjectId() { + return subjectId; + } + + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } + + @Component(UserTouchedIntegrationUserCredentialEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationUserCredentialEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationUserCredentialEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationUserCredentialEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return UserCredential.class; + } + + @Override + protected List specifications(UserCredential item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isEmpty(item.getSubjectId())) + .failOn(UserCredential._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserCredential._subjectId}, LocaleContextHolder.getLocale())) + ); + } + } + } + + public static class TenantUser { + + private UUID tenant; + public static final String _tenant = "tenant"; + + public UUID getTenant() { + return tenant; + } + + public void setTenant(UUID tenant) { + this.tenant = tenant; + } + + @Component(UserTouchedIntegrationTenantUserEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationTenantUserEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationTenantUserEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationTenantUserEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return TenantUser.class; + } + + @Override + protected List specifications(TenantUser item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isNull(item.getTenant())) + .failOn(TenantUser._tenant).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantUser._tenant}, LocaleContextHolder.getLocale())) + ); + } + } + } + + @Component(UserTouchedIntegrationEventValidator.ValidatorName) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public static class UserTouchedIntegrationEventValidator extends BaseValidator { @@ -161,9 +288,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { private final MessageSource messageSource; - protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + private final ValidatorFactory validatorFactory; + + protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { super(conventionService, errors); this.messageSource = messageSource; + this.validatorFactory = validatorFactory; } @Override @@ -183,12 +313,14 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), this.spec() .iff(() -> !this.isEmpty(item.getName())) - .must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEventPersist._nameLength)) + .must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEvent._nameLength)) .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), - this.spec() - .must(() -> !this.isEmpty(item.getSubjectId())) - .failOn(UserTouchedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale())) - ); + this.navSpec() + .iff(() -> !this.isListNullOrEmpty(item.getUserContactInfo())) + .on(UserTouchedIntegrationEvent._userContactInfo) + .over(item.getUserContactInfo()) + .using((i) -> this.validatorFactory.validator(UserContactInfo.UserTouchedIntegrationUserContactInfoEventValidator.class)) + ); } } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java index 24b11d5f3..38912cfce 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandler.java @@ -1,5 +1,6 @@ package gr.cite.notification.integrationevent.inbox.usertouched; + import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; public interface UserTouchedIntegrationEventHandler extends IntegrationEventHandler { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java index 4b0bafa27..6f2ae39d5 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java @@ -1,11 +1,12 @@ package gr.cite.notification.integrationevent.inbox.usertouched; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.notification.audit.AuditableAction; import gr.cite.notification.common.JsonHandlingService; -import gr.cite.notification.common.scope.fake.FakeRequestScope; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; @@ -17,13 +18,8 @@ import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.validation.ValidatorFactory; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.EntityTransaction; -import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -36,19 +32,30 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class)); - protected final ApplicationContext applicationContext; private final JsonHandlingService jsonHandlingService; private final ValidatorFactory validatorFactory; + private final QueryFactory queryFactory; + private final TenantScope tenantScope; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final ClaimExtractorProperties claimExtractorProperties; + private final UserService userService; + private final AuditService auditService; + private final TenantEntityManager tenantEntityManager; public UserTouchedIntegrationEventHandlerImpl( - JsonHandlingService jsonHandlingService, - ApplicationContext applicationContext, - ValidatorFactory validatorFactory) { + JsonHandlingService jsonHandlingService, + ValidatorFactory validatorFactory, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager) { this.jsonHandlingService = jsonHandlingService; - this.applicationContext = applicationContext; this.validatorFactory = validatorFactory; + this.queryFactory = queryFactory; + this.tenantScope = tenantScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractorProperties = claimExtractorProperties; + this.userService = userService; + this.auditService = auditService; + this.tenantEntityManager = tenantEntityManager; } @Override @@ -61,69 +68,37 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event); - EntityManager entityManager = null; - EntityTransaction transaction = null; - try (FakeRequestScope ignored = new FakeRequestScope()) { - try { - QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); - TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && event.getTenant() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - scope.setTenant(event.getTenant(), tenant.getCode()); - } else if (scope.isMultitenant()) { + EventProcessingStatus status = EventProcessingStatus.Success; + try { + if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { logger.error("missing tenant from event message"); return EventProcessingStatus.Error; } -// -// ValidationService validator = this.applicationContext.getBean(ValidationService.class); -// validator.validateForce(model); - - CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); - currentPrincipalResolver.push(InboxPrincipal.build(properties)); - - EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); - entityManager = entityManagerFactory.createEntityManager(); - - transaction = entityManager.getTransaction(); - transaction.begin(); - - try { - UserService userService = this.applicationContext.getBean(UserService.class); - userService.persist(event, null); - - AuditService auditService = this.applicationContext.getBean(AuditService.class); - - auditService.track(AuditableAction.User_Persist, Map.ofEntries( - new AbstractMap.SimpleEntry("model", event) - )); - - transaction.commit(); - } catch (Exception e) { - transaction.rollback(); - throw e; - } finally { - currentPrincipalResolver.pop(); - } - } catch (OptimisticLockException ex) { - // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working - logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) - transaction.rollback(); - } catch (Exception ex) { - logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) - transaction.rollback(); - } finally { - if (entityManager != null) - entityManager.close(); + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode()); + } else if (this.tenantScope.isMultitenant()) { +// logger.error("missing tenant from event message"); +// return EventProcessingStatus.Error; + this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode()); } + + currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); + + userService.persist(event, null); + + auditService.track(AuditableAction.User_Persist, Map.ofEntries( + new AbstractMap.SimpleEntry("model", event) + )); + } catch (Exception ex) { + status = EventProcessingStatus.Error; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); + } finally { + currentPrincipalResolver.pop(); + tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager()); } - return EventProcessingStatus.Success; + + return status; } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java index af5e77173..adeb3f62a 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxProperties.java @@ -7,52 +7,13 @@ public class OutboxProperties { private final String exchange; - private final String tenantTouchTopic; - private final String tenantRemovalTopic; - - private final String userTouchTopic; - - private final String userRemovalTopic; - - private final String notifyTopic; - - public OutboxProperties(String exchange, - String tenantTouchTopic, - String tenantRemovalTopic, - String userTouchTopic, - String userRemovalTopic, - String notifyTopic + public OutboxProperties(String exchange ) { this.exchange = exchange; - this.tenantTouchTopic = tenantTouchTopic; - this.tenantRemovalTopic = tenantRemovalTopic; - this.userTouchTopic = userTouchTopic; - this.userRemovalTopic = userRemovalTopic; - this.notifyTopic = notifyTopic; } public String getExchange() { return exchange; } - - public String getTenantTouchTopic() { - return tenantTouchTopic; - } - - public String getTenantRemovalTopic() { - return tenantRemovalTopic; - } - - public String getUserTouchTopic() { - return userTouchTopic; - } - - public String getUserRemovalTopic() { - return userRemovalTopic; - } - - public String getNotifyTopic() { - return notifyTopic; - } } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxRepositoryImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxRepositoryImpl.java index 8bdc8fdb0..2b4926dea 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxRepositoryImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxRepositoryImpl.java @@ -20,11 +20,9 @@ import jakarta.persistence.EntityTransaction; import jakarta.persistence.OptimisticLockException; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; import java.time.Instant; import java.util.List; -import java.util.Random; import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -32,9 +30,11 @@ import java.util.stream.Collectors; public class OutboxRepositoryImpl implements OutboxRepository { protected final ApplicationContext applicationContext; - private final Random random = new Random(); + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class)); + private final JsonHandlingService jsonHandlingService; + private final OutboxProperties outboxProperties; public OutboxRepositoryImpl( @@ -88,14 +88,17 @@ public class OutboxRepositoryImpl implements OutboxRepository { } catch (OptimisticLockException ex) { // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); candidate = null; } catch (Exception ex) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); candidate = null; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); @@ -137,10 +140,12 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); success = false; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -182,10 +187,12 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); success = false; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -237,10 +244,12 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); success = false; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -266,7 +275,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); if (queueOutboxMessages == null) { - logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", confirmedMessages.stream().map(UUID::toString).collect(Collectors.joining(","))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -281,9 +290,11 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -308,7 +319,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); if (queueOutboxMessages == null) { - logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", nackedMessages.stream().map(UUID::toString).collect(Collectors.joining(","))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -323,9 +334,11 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -336,7 +349,6 @@ public class OutboxRepositoryImpl implements OutboxRepository { public QueueOutbox create(IntegrationEvent item) { EntityTransaction transaction = null; EntityManager entityManager = null; - boolean success = false; QueueOutboxEntity queueMessage = null; try (FakeRequestScope ignored = new FakeRequestScope()) { try { @@ -354,10 +366,11 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); - success = false; + if (transaction != null) + transaction.rollback(); } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -375,13 +388,14 @@ public class OutboxRepositoryImpl implements OutboxRepository { } // UUID correlationId = UUID.randomUUID(); -// if (event.getEvent() != null) event.getEvent().setTrackingContextTag(correlationId.toString()); +// if (event.getEvent() != null) +// event.getEvent().setTrackingContextTag(correlationId.toString()); // event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent())); // //this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}"); // // QueueOutboxEntity queueMessage = new QueueOutboxEntity(); // queueMessage.setId(UUID.randomUUID()); -// queueMessage.setTenantId(null); +// queueMessage.setTenantId(event.getTenantId()); // queueMessage.setExchange(this.outboxProperties.getExchange()); // queueMessage.setRoute(routingKey); // queueMessage.setMessageId(event.getMessageId()); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxService.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxService.java new file mode 100644 index 000000000..d63a85bf1 --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxService.java @@ -0,0 +1,5 @@ +package gr.cite.notification.integrationevent.outbox; + +public interface OutboxService { + void publish(OutboxIntegrationEvent event); +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxServiceImpl.java new file mode 100644 index 000000000..677db1af5 --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/integrationevent/outbox/OutboxServiceImpl.java @@ -0,0 +1,34 @@ +package gr.cite.notification.integrationevent.outbox; + + +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +@Component +@RequestScope +public class OutboxServiceImpl implements OutboxService { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxServiceImpl.class)); + + private final ApplicationEventPublisher eventPublisher; + + public OutboxServiceImpl( + ApplicationEventPublisher eventPublisher + ) { + this.eventPublisher = eventPublisher; + } + + @Override + public void publish(OutboxIntegrationEvent event) { + try { + eventPublisher.publishEvent(event); + } catch (Exception ex) { + logger.error(new MapLogEntry(String.format("Could not save message ", event.getMessage())).And("message", event.getMessage()).And("ex", ex)); + //Still want to skip it from processing + } + + } +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/Language.java b/notification-service/notification/src/main/java/gr/cite/notification/model/Language.java deleted file mode 100644 index a677beb19..000000000 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/Language.java +++ /dev/null @@ -1,93 +0,0 @@ -package gr.cite.notification.model; - -import gr.cite.notification.common.enums.IsActive; - -import java.time.Instant; -import java.util.UUID; - -public class Language { - - private UUID id; - - public static final String _id = "id"; - - private String code; - - public static final String _code = "code"; - - private Integer ordinal; - - public static final String _ordinal = "ordinal"; - - private Instant createdAt; - - public static final String _createdAt = "createdAt"; - - private Instant updatedAt; - - public static final String _updatedAt = "updatedAt"; - - private IsActive isActive; - - public static final String _isActive = "isActive"; - - private String hash; - - public static final String _hash = "hash"; - - public UUID getId() { - return id; - } - - public void setId(UUID id) { - this.id = id; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public Integer getOrdinal() { - return ordinal; - } - - public void setOrdinal(Integer ordinal) { - this.ordinal = ordinal; - } - - public Instant getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Instant createdAt) { - this.createdAt = createdAt; - } - - public Instant getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt(Instant updatedAt) { - this.updatedAt = updatedAt; - } - - public IsActive getIsActive() { - return isActive; - } - - public void setIsActive(IsActive isActive) { - this.isActive = isActive; - } - - public String getHash() { - return hash; - } - - public void setHash(String hash) { - this.hash = hash; - } -} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/NotificationTemplate.java b/notification-service/notification/src/main/java/gr/cite/notification/model/NotificationTemplate.java index 81be59bab..5a869d4a1 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/NotificationTemplate.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/NotificationTemplate.java @@ -23,8 +23,8 @@ public class NotificationTemplate { private NotificationTemplateKind kind; public static final String _kind = "kind"; - private Language language; - public static final String _language = "language"; + private String languageCode; + public static final String _languageCode = "languageCode"; private NotificationTemplateValue value; public static final String _value = "value"; @@ -76,12 +76,12 @@ public class NotificationTemplate { this.kind = kind; } - public Language getLanguage() { - return language; + public String getLanguageCode() { + return languageCode; } - public void setLanguage(Language language) { - this.language = language; + public void setLanguageCode(String languageCode) { + this.languageCode = languageCode; } public NotificationTemplateValue getValue() { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/TenantUser.java b/notification-service/notification/src/main/java/gr/cite/notification/model/TenantUser.java new file mode 100644 index 000000000..402356465 --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/TenantUser.java @@ -0,0 +1,98 @@ +package gr.cite.notification.model; + + +import gr.cite.notification.common.enums.IsActive; + +import java.time.Instant; +import java.util.UUID; + +public class TenantUser { + + private UUID id; + public final static String _id = "id"; + + private User user; + public final static String _user = "user"; + + private Tenant tenant; + public final static String _tenant = "tenant"; + + private IsActive isActive; + public final static String _isActive = "isActive"; + + private Instant createdAt; + public final static String _createdAt = "createdAt"; + + private Instant updatedAt; + public final static String _updatedAt = "updatedAt"; + + private String hash; + public final static String _hash = "hash"; + + private Boolean belongsToCurrentTenant; + public static final String _belongsToCurrentTenant = "belongsToCurrentTenant"; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Tenant getTenant() { + return tenant; + } + + public void setTenant(Tenant tenant) { + this.tenant = tenant; + } + + public IsActive getIsActive() { + return isActive; + } + + public void setIsActive(IsActive isActive) { + this.isActive = isActive; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = updatedAt; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public Boolean getBelongsToCurrentTenant() { + return belongsToCurrentTenant; + } + + public void setBelongsToCurrentTenant(Boolean belongsToCurrentTenant) { + this.belongsToCurrentTenant = belongsToCurrentTenant; + } +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/builder/LanguageBuilder.java b/notification-service/notification/src/main/java/gr/cite/notification/model/builder/LanguageBuilder.java deleted file mode 100644 index e435a9c3f..000000000 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/builder/LanguageBuilder.java +++ /dev/null @@ -1,65 +0,0 @@ -package gr.cite.notification.model.builder; - -import gr.cite.notification.authorization.AuthorizationFlags; -import gr.cite.notification.convention.ConventionService; -import gr.cite.notification.data.LanguageEntity; -import gr.cite.notification.model.Language; -import gr.cite.tools.exception.MyApplicationException; -import gr.cite.tools.fieldset.FieldSet; -import gr.cite.tools.logging.DataLogEntry; -import gr.cite.tools.logging.LoggerService; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -import java.util.*; - -@Component -@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) -public class LanguageBuilder extends BaseBuilder { - - private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); - - @Autowired - public LanguageBuilder( - ConventionService conventionService) { - super(conventionService, new LoggerService(LoggerFactory.getLogger(LanguageBuilder.class))); - } - - public LanguageBuilder authorize(EnumSet values) { - this.authorize = values; - return this; - } - - @Override - public List build(FieldSet fields, List data) throws MyApplicationException { - this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0)); - this.logger.trace(new DataLogEntry("requested fields", fields)); - if (fields == null || data == null || fields.isEmpty()) - return new ArrayList<>(); - - List models = new ArrayList<>(); - for (LanguageEntity d : data) { - Language m = new Language(); - if (fields.hasField(this.asIndexer(Language._id))) - m.setId(d.getId()); - if (fields.hasField(this.asIndexer(Language._code))) - m.setCode(d.getCode()); - if (fields.hasField(this.asIndexer(Language._ordinal))) - m.setOrdinal(d.getOrdinal()); - if (fields.hasField(this.asIndexer(Language._createdAt))) - m.setCreatedAt(d.getCreatedAt()); - if (fields.hasField(this.asIndexer(Language._updatedAt))) - m.setUpdatedAt(d.getUpdatedAt()); - if (fields.hasField(this.asIndexer(Language._isActive))) - m.setIsActive(d.getIsActive()); - if (fields.hasField(this.asIndexer(Language._hash))) - m.setHash(this.hashValue(d.getUpdatedAt())); - models.add(m); - } - this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); - return models; - } -} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/builder/NotificationTemplateBuilder.java b/notification-service/notification/src/main/java/gr/cite/notification/model/builder/NotificationTemplateBuilder.java index 7be362af0..ac1848069 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/builder/NotificationTemplateBuilder.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/builder/NotificationTemplateBuilder.java @@ -5,12 +5,9 @@ import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.types.notificationtemplate.NotificationTemplateValueEntity; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.NotificationTemplateEntity; -import gr.cite.notification.model.Language; import gr.cite.notification.model.NotificationTemplate; import gr.cite.notification.model.Tenant; import gr.cite.notification.model.builder.notificationtemplate.NotificationTemplateValueBuilder; -import gr.cite.notification.model.notificationtemplate.NotificationTemplateValue; -import gr.cite.notification.query.LanguageQuery; import gr.cite.notification.query.TenantQuery; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.query.QueryFactory; @@ -59,9 +56,6 @@ public class NotificationTemplateBuilder extends BaseBuilder tenantMap = this.collectTenants(tenantFields, data); - FieldSet languageFields = fields.extractPrefixed(this.asPrefix(NotificationTemplate._language)); - Map languageMap = this.collectLanguages(languageFields, data); - List models = new ArrayList<>(); for(NotificationTemplateEntity d : data){ NotificationTemplate m = new NotificationTemplate(); @@ -69,7 +63,7 @@ public class NotificationTemplateBuilder extends BaseBuilder collectLanguages(FieldSet fields, List datas) throws MyApplicationException { - if (fields.isEmpty() || datas.isEmpty()) return null; - this.logger.debug("checking related - {}", NotificationTemplate.class.getSimpleName()); - - Map itemMap = null; - if (!fields.hasOtherField(this.asIndexer(Language._id))) { - itemMap = this.asEmpty( - datas.stream().map(x -> x.getLanguageId()).distinct().collect(Collectors.toList()), - x -> { - Language item = new Language(); - item.setId(x); - return item; - }, - x -> x.getId()); - } else { - FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(Language._id); - LanguageQuery q = this.queryFactory.query(LanguageQuery.class).authorize(this.authorize).ids(datas.stream().map(x -> x.getLanguageId()).distinct().collect(Collectors.toList())); - itemMap = this.builderFactory.builder(LanguageBuilder.class).authorize(this.authorize).asForeignKey(q, clone, x -> x.getId()); - } - if (!fields.hasField(Tenant._id)) { - itemMap.values().stream().filter(x -> x != null).map(x -> { - x.setId(null); - return x; - }).collect(Collectors.toList()); - } - - return itemMap; - } - private Map collectTenants(FieldSet fields, List datas) throws MyApplicationException { if (fields.isEmpty() || datas.isEmpty()) return null; this.logger.debug("checking related - {}", NotificationTemplate.class.getSimpleName()); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/InAppNotificationDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/InAppNotificationDeleter.java index 68ac201c6..fc4551de2 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/InAppNotificationDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/InAppNotificationDeleter.java @@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.data.InAppNotificationEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.query.InAppNotificationQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -27,19 +27,16 @@ import java.util.stream.Collectors; public class InAppNotificationDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InAppNotificationDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; - private final DeleterFactory deleterFactory; @Autowired public InAppNotificationDeleter( - TenantScopedEntityManager entityManager, - QueryFactory queryFactory, - DeleterFactory deleterFactory + TenantEntityManager entityManager, + QueryFactory queryFactory ) { this.entityManager = entityManager; this.queryFactory = queryFactory; - this.deleterFactory = deleterFactory; } public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationDeleter.java index b4587862e..c5099590a 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationDeleter.java @@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.data.NotificationEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.query.NotificationQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -27,13 +27,13 @@ import java.util.stream.Collectors; public class NotificationDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; private final DeleterFactory deleterFactory; @Autowired public NotificationDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationTemplateDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationTemplateDeleter.java index 7ff4c3ead..a69f060c8 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationTemplateDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/NotificationTemplateDeleter.java @@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.data.NotificationTemplateEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.query.NotificationTemplateQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -27,13 +27,13 @@ import java.util.stream.Collectors; public class NotificationTemplateDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationTemplateDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; private final DeleterFactory deleterFactory; @Autowired public NotificationTemplateDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantConfigurationDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantConfigurationDeleter.java index ffbb084fc..5db6972e4 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantConfigurationDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantConfigurationDeleter.java @@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.data.TenantConfigurationEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.query.TenantConfigurationQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -27,13 +27,13 @@ import java.util.stream.Collectors; public class TenantConfigurationDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantConfigurationDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; private final DeleterFactory deleterFactory; @Autowired public TenantConfigurationDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantDeleter.java index db4900766..f9fa2c7f0 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantDeleter.java @@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.data.TenantEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.query.TenantQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -26,13 +26,13 @@ import java.util.UUID; public class TenantDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; private final DeleterFactory deleterFactory; @Autowired public TenantDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java new file mode 100644 index 000000000..faf322de7 --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/TenantUserDeleter.java @@ -0,0 +1,71 @@ +package gr.cite.notification.model.deleter; + +import gr.cite.notification.common.enums.IsActive; +import gr.cite.notification.data.TenantEntityManager; +import gr.cite.notification.data.TenantUserEntity; +import gr.cite.notification.query.TenantUserQuery; +import gr.cite.tools.data.deleter.Deleter; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.management.InvalidApplicationException; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantUserDeleter implements Deleter { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantUserDeleter.class)); + + private final TenantEntityManager entityManager; + private final QueryFactory queryFactory; + + @Autowired + public TenantUserDeleter( + TenantEntityManager entityManager, + QueryFactory queryFactory + ) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + } + + public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(e -> e.size()).orElse(0)).And("ids", ids)); + List datas = this.queryFactory.query(TenantUserQuery.class).ids(ids).collect(); + logger.trace("retrieved {} items", Optional.ofNullable(datas).map(e -> e.size()).orElse(0)); + this.deleteAndSave(datas); + } + + public void deleteAndSave(List datas) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(datas).map(e -> e.size()).orElse(0)); + this.delete(datas); + logger.trace("saving changes"); + this.entityManager.flush(); + logger.trace("changes saved"); + } + + public void delete(List datas) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(datas).map(x -> x.size()).orElse(0)); + if (datas == null || datas.isEmpty()) return; + + Instant now = Instant.now(); + + for (TenantUserEntity item : datas) { + logger.trace("deleting item {}", item.getId()); + item.setIsActive(IsActive.Inactive); + item.setUpdatedAt(now); + logger.trace("updating item"); + this.entityManager.merge(item); + logger.trace("updated item"); + } + } +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserContactInfoDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserContactInfoDeleter.java index 0ecec6c68..a583d34fc 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserContactInfoDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserContactInfoDeleter.java @@ -1,7 +1,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.UserContactInfoEntity; import gr.cite.notification.query.UserContactInfoQuery; import gr.cite.tools.data.deleter.Deleter; @@ -27,13 +27,13 @@ public class UserContactInfoDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserContactInfoDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final QueryFactory queryFactory; @Autowired public UserContactInfoDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory ) { this.entityManager = entityManager; diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserCredentialDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserCredentialDeleter.java index b29145af9..e400ff201 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserCredentialDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserCredentialDeleter.java @@ -1,7 +1,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.UserCredentialEntity; import gr.cite.notification.query.UserCredentialQuery; import gr.cite.tools.data.deleter.Deleter; @@ -26,13 +26,13 @@ public class UserCredentialDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final QueryFactory queryFactory; @Autowired public UserCredentialDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory ) { this.entityManager = entityManager; diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserDeleter.java b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserDeleter.java index e32cec8a5..16c2d25d7 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserDeleter.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/deleter/UserDeleter.java @@ -1,6 +1,7 @@ package gr.cite.notification.model.deleter; import gr.cite.notification.common.enums.IsActive; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.UserContactInfoEntity; import gr.cite.notification.data.UserCredentialEntity; import gr.cite.notification.data.UserEntity; @@ -12,7 +13,6 @@ import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; -import jakarta.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -32,7 +32,7 @@ public class UserDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserDeleter.class)); - private final EntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; @@ -40,7 +40,7 @@ public class UserDeleter implements Deleter { @Autowired public UserDeleter( - EntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/model/persist/NotificationTemplatePersist.java b/notification-service/notification/src/main/java/gr/cite/notification/model/persist/NotificationTemplatePersist.java index c0aeed0b0..6e7286dbd 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/model/persist/NotificationTemplatePersist.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/model/persist/NotificationTemplatePersist.java @@ -32,7 +32,7 @@ public class NotificationTemplatePersist { private NotificationTemplateKind kind; public static final String _kind = "kind"; - private UUID languageId; + private String languageCode; public static final String _languageId = "languageId"; private NotificationTemplateValuePersist value; @@ -73,12 +73,12 @@ public class NotificationTemplatePersist { this.kind = kind; } - public UUID getLanguageId() { - return languageId; + public String getLanguageCode() { + return languageCode; } - public void setLanguageId(UUID languageId) { - this.languageId = languageId; + public void setLanguageCode(String languageCode) { + this.languageCode = languageCode; } public NotificationTemplateValuePersist getValue() { @@ -130,7 +130,7 @@ public class NotificationTemplatePersist { .must(() -> !this.isValidHash(item.getHash())) .failOn(NotificationTemplatePersist._hash).failWith(messageSource.getMessage("Validation_OverPosting", new Object[]{}, LocaleContextHolder.getLocale())), this.spec() - .must(() -> !this.isNull(item.getLanguageId())) + .must(() -> !this.isNull(item.getLanguageCode())) .failOn(NotificationTemplatePersist._languageId).failWith(messageSource.getMessage("Validation_Required", new Object[]{NotificationTemplatePersist._languageId}, LocaleContextHolder.getLocale())), this.spec() .must(() -> !this.isNull(item.getNotificationType())) diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/LanguageQuery.java b/notification-service/notification/src/main/java/gr/cite/notification/query/LanguageQuery.java deleted file mode 100644 index 314689e67..000000000 --- a/notification-service/notification/src/main/java/gr/cite/notification/query/LanguageQuery.java +++ /dev/null @@ -1,190 +0,0 @@ -package gr.cite.notification.query; - -import gr.cite.notification.authorization.AuthorizationFlags; -import gr.cite.notification.common.enums.IsActive; -import gr.cite.notification.data.LanguageEntity; -import gr.cite.notification.model.Language; -import gr.cite.tools.data.query.FieldResolver; -import gr.cite.tools.data.query.QueryBase; -import gr.cite.tools.data.query.QueryContext; -import jakarta.persistence.Tuple; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.Predicate; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -import java.time.Instant; -import java.util.*; - -@Component -@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) -public class LanguageQuery extends QueryBase { - - private String like; - - private Collection ids; - - private Collection isActives; - - private Collection codes; - - private Collection excludedIds; - - private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); - - public LanguageQuery like(String value) { - this.like = value; - return this; - } - - public LanguageQuery ids(UUID value) { - this.ids = List.of(value); - return this; - } - - public LanguageQuery ids(UUID... value) { - this.ids = Arrays.asList(value); - return this; - } - - public LanguageQuery ids(Collection values) { - this.ids = values; - return this; - } - - public LanguageQuery isActive(IsActive value) { - this.isActives = List.of(value); - return this; - } - - public LanguageQuery isActive(IsActive... value) { - this.isActives = Arrays.asList(value); - return this; - } - - public LanguageQuery isActive(Collection values) { - this.isActives = values; - return this; - } - - public LanguageQuery codes(String value) { - this.codes = List.of(value); - return this; - } - - public LanguageQuery codes(String... value) { - this.codes = Arrays.asList(value); - return this; - } - - public LanguageQuery codes(Collection values) { - this.codes = values; - return this; - } - - public LanguageQuery excludedIds(Collection values) { - this.excludedIds = values; - return this; - } - - public LanguageQuery excludedIds(UUID value) { - this.excludedIds = List.of(value); - return this; - } - - public LanguageQuery excludedIds(UUID... value) { - this.excludedIds = Arrays.asList(value); - return this; - } - - public LanguageQuery authorize(EnumSet values) { - this.authorize = values; - return this; - } - - public LanguageQuery( - ) { - } - - @Override - protected Class entityClass() { - return LanguageEntity.class; - } - - @Override - protected Boolean isFalseQuery() { - return this.isEmpty(this.ids) || this.isEmpty(this.isActives) || this.isEmpty(this.excludedIds) || this.isEmpty(this.codes); - } - - @Override - protected Predicate applyFilters(QueryContext queryContext) { - List predicates = new ArrayList<>(); - if (this.ids != null) { - CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(LanguageEntity._id)); - for (UUID item : this.ids) - inClause.value(item); - predicates.add(inClause); - } - if (this.like != null && !this.like.isEmpty()) { - predicates.add(queryContext.CriteriaBuilder.like(queryContext.Root.get(LanguageEntity._code), this.like)); - } - if (this.isActives != null) { - CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(LanguageEntity._isActive)); - for (IsActive item : this.isActives) - inClause.value(item); - predicates.add(inClause); - } - if (this.codes != null) { - CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(LanguageEntity._code)); - for (String item : this.codes) - inClause.value(item); - predicates.add(inClause); - } - if (this.excludedIds != null) { - CriteriaBuilder.In notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(LanguageEntity._id)); - for (UUID item : this.excludedIds) - notInClause.value(item); - predicates.add(notInClause.not()); - } - if (!predicates.isEmpty()) { - Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); - return queryContext.CriteriaBuilder.and(predicatesArray); - } else { - return null; - } - } - - @Override - protected LanguageEntity convert(Tuple tuple, Set columns) { - LanguageEntity item = new LanguageEntity(); - item.setId(QueryBase.convertSafe(tuple, columns, LanguageEntity._id, UUID.class)); - item.setCode(QueryBase.convertSafe(tuple, columns, LanguageEntity._code, String.class)); - item.setOrdinal(QueryBase.convertSafe(tuple, columns, LanguageEntity._ordinal, Integer.class)); - item.setCreatedAt(QueryBase.convertSafe(tuple, columns, LanguageEntity._createdAt, Instant.class)); - item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, LanguageEntity._updatedAt, Instant.class)); - item.setIsActive(QueryBase.convertSafe(tuple, columns, LanguageEntity._isActive, IsActive.class)); - return item; - } - - @Override - protected String fieldNameOf(FieldResolver item) { - if (item.match(Language._id)) - return LanguageEntity._id; - else if (item.match(Language._code)) - return LanguageEntity._code; - else if (item.match(Language._ordinal)) - return LanguageEntity._ordinal; - else if (item.match(Language._createdAt)) - return LanguageEntity._createdAt; - else if (item.match(Language._updatedAt)) - return LanguageEntity._updatedAt; - else if (item.match(Language._hash)) - return LanguageEntity._updatedAt; - else if (item.match(Language._isActive)) - return LanguageEntity._isActive; - else - return null; - } - -} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationTemplateQuery.java b/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationTemplateQuery.java index e35dae690..ae7a62ee6 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationTemplateQuery.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/query/NotificationTemplateQuery.java @@ -2,7 +2,6 @@ package gr.cite.notification.query; import gr.cite.notification.authorization.AuthorizationFlags; import gr.cite.notification.common.enums.*; -import gr.cite.notification.data.LanguageEntity; import gr.cite.notification.data.NotificationTemplateEntity; import gr.cite.notification.model.NotificationTemplate; import gr.cite.tools.data.query.FieldResolver; @@ -15,7 +14,7 @@ import org.springframework.stereotype.Component; import jakarta.persistence.Tuple; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Predicate; -import jakarta.persistence.criteria.Subquery; + import java.time.Instant; import java.util.*; @@ -28,8 +27,7 @@ public class NotificationTemplateQuery extends QueryBase excludedIds; private Collection isActives; private Collection notificationTypes; - private Collection languages; - private LanguageQuery languageQuery; + private Collection languageCodes; private List channels; private List kinds; @@ -80,25 +78,21 @@ public class NotificationTemplateQuery extends QueryBase values) { - this.languages = values; + public NotificationTemplateQuery languageCodes(Collection values) { + this.languageCodes = values; return this; } - public NotificationTemplateQuery languageSubQuery(LanguageQuery subQuery) { - this.languageQuery = subQuery; - return this; - } public NotificationTemplateQuery channels(NotificationTemplateChannel... channels) { this.channels = List.of(channels); @@ -157,7 +151,7 @@ public class NotificationTemplateQuery extends QueryBase inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(NotificationTemplateEntity._languageId)); - for (UUID item : this.languages) + if (this.languageCodes != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(NotificationTemplateEntity._languageCode)); + for (String item : this.languageCodes) inClause.value(item); predicates.add(inClause); } - if (this.languageQuery != null) { - QueryContext subQuery = this.applySubQuery(this.languageQuery, queryContext, UUID.class, root -> root.get(NotificationTemplateEntity._id)); - predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(NotificationTemplateEntity._languageId)).value(subQuery.Query)); - } - if (this.channels != null) { CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(NotificationTemplateEntity._channel)); for (NotificationTemplateChannel item : this.channels) @@ -237,9 +226,8 @@ public class NotificationTemplateQuery extends QueryBase { + + private Collection ids; + private Collection userIds; + private Collection tenantIds; + private Collection isActives; + private UserQuery userQuery; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + private final UserScope userScope; + private final AuthorizationService authService; + + public TenantUserQuery( + UserScope userScope, + AuthorizationService authService + ) { + this.userScope = userScope; + this.authService = authService; + } + + public TenantUserQuery ids(UUID value) { + this.ids = List.of(value); + return this; + } + + public TenantUserQuery ids(UUID... value) { + this.ids = Arrays.asList(value); + return this; + } + + public TenantUserQuery ids(Collection values) { + this.ids = values; + return this; + } + + public TenantUserQuery userIds(UUID value) { + this.userIds = List.of(value); + return this; + } + + public TenantUserQuery userIds(UUID... value) { + this.userIds = Arrays.asList(value); + return this; + } + + public TenantUserQuery userIds(Collection values) { + this.userIds = values; + return this; + } + + public TenantUserQuery tenantIds(UUID value) { + this.tenantIds = List.of(value); + return this; + } + + public TenantUserQuery tenantIds(UUID... value) { + this.tenantIds = Arrays.asList(value); + return this; + } + + public TenantUserQuery tenantIds(Collection values) { + this.tenantIds = values; + return this; + } + + public TenantUserQuery isActive(IsActive value) { + this.isActives = List.of(value); + return this; + } + + public TenantUserQuery isActive(IsActive... value) { + this.isActives = Arrays.asList(value); + return this; + } + + public TenantUserQuery isActive(Collection values) { + this.isActives = values; + return this; + } + + public TenantUserQuery userSubQuery(UserQuery subQuery) { + this.userQuery = subQuery; + return this; + } + + public TenantUserQuery authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + protected Class entityClass() { + return TenantUserEntity.class; + } + + @Override + protected Boolean isFalseQuery() { + return this.isEmpty(this.ids) || this.isEmpty(this.userIds) || this.isEmpty(this.tenantIds) || this.isEmpty(this.isActives) || this.isFalseQuery(this.userQuery); + } + + + @Override + protected Predicate applyAuthZ(QueryContext queryContext) { + if (this.authorize.contains(AuthorizationFlags.None)) return null; + if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseTenant)) return null; + UUID ownerId = null; + if (this.authorize.contains(AuthorizationFlags.Owner)) ownerId = this.userScope.getUserIdSafe(); + + List predicates = new ArrayList<>(); + if (ownerId != null) { + predicates.add(queryContext.CriteriaBuilder.equal(queryContext.Root.get(TenantUserEntity._userId), ownerId)); + } + if (!predicates.isEmpty()) { + Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); + return queryContext.CriteriaBuilder.and(predicatesArray); + } else { + return queryContext.CriteriaBuilder.or(); //Creates a false query + } + } + + @Override + protected Predicate applyFilters(QueryContext queryContext) { + List predicates = new ArrayList<>(); + if (this.ids != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._id)); + for (UUID item : this.ids) inClause.value(item); + predicates.add(inClause); + } + if (this.userIds != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._userId)); + for (UUID item : this.userIds) inClause.value(item); + predicates.add(inClause); + } + if (this.tenantIds != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._tenantId)); + for (UUID item : this.tenantIds) inClause.value(item); + predicates.add(inClause); + } + if (this.isActives != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._isActive)); + for (IsActive item : this.isActives) inClause.value(item); + predicates.add(inClause); + } + if (this.userQuery != null) { + QueryContext subQuery = this.applySubQuery(this.userQuery, queryContext, UUID.class, root -> root.get(TenantUserEntity._userId)); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._userId)).value(subQuery.Query)); + } + if (!predicates.isEmpty()) { + Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); + return queryContext.CriteriaBuilder.and(predicatesArray); + } else { + return null; + } + } + + @Override + protected TenantUserEntity convert(Tuple tuple, Set columns) { + TenantUserEntity item = new TenantUserEntity(); + item.setId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._id, UUID.class)); + item.setTenantId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._tenantId, UUID.class)); + item.setUserId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._userId, UUID.class)); + item.setTenantId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._tenantId, UUID.class)); + item.setCreatedAt(QueryBase.convertSafe(tuple, columns, TenantUserEntity._createdAt, Instant.class)); + item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, TenantUserEntity._updatedAt, Instant.class)); + item.setIsActive(QueryBase.convertSafe(tuple, columns, TenantUserEntity._isActive, IsActive.class)); + return item; + } + + @Override + protected String fieldNameOf(FieldResolver item) { + if (item.match(TenantUser._id)) return TenantUserEntity._id; + else if (item.match(TenantUser._tenant, Tenant._id)) return TenantUserEntity._tenantId; + else if (item.prefix(TenantUser._tenant)) return TenantUserEntity._tenantId; + else if (item.match(TenantUser._isActive)) return TenantUserEntity._isActive; + else if (item.match(TenantUser._createdAt)) return TenantUserEntity._createdAt; + else if (item.match(TenantUser._updatedAt)) return TenantUserEntity._updatedAt; + else if (item.match(TenantUser._hash)) return TenantUserEntity._updatedAt; + else if (item.match(TenantUser._user, UserEntity._id)) return TenantUserEntity._userId; + else if (item.prefix(TenantUser._user)) return TenantUserEntity._userId; + else if (item.match(TenantUser._belongsToCurrentTenant)) return TenantUserEntity._tenantId; + else return null; + } +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/LanguageLookup.java b/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/LanguageLookup.java deleted file mode 100644 index 6d3f7dc3b..000000000 --- a/notification-service/notification/src/main/java/gr/cite/notification/query/lookup/LanguageLookup.java +++ /dev/null @@ -1,76 +0,0 @@ -package gr.cite.notification.query.lookup; - -import gr.cite.notification.common.enums.IsActive; -import gr.cite.notification.query.LanguageQuery; -import gr.cite.tools.data.query.Lookup; -import gr.cite.tools.data.query.QueryFactory; - -import java.util.List; -import java.util.UUID; - -public class LanguageLookup extends Lookup { - - private String like; - - private List isActive; - - private List codes; - - private List ids; - - private List excludedIds; - - public String getLike() { - return like; - } - - public void setLike(String like) { - this.like = like; - } - - public List getIsActive() { - return isActive; - } - - public void setIsActive(List isActive) { - this.isActive = isActive; - } - - public List getIds() { - return ids; - } - - public void setIds(List ids) { - this.ids = ids; - } - - public List getExcludedIds() { - return excludedIds; - } - - public void setExcludedIds(List excludeIds) { - this.excludedIds = excludeIds; - } - - public List getCodes() { - return codes; - } - - public void setCodes(List codes) { - this.codes = codes; - } - - public LanguageQuery enrich(QueryFactory queryFactory) { - LanguageQuery query = queryFactory.query(LanguageQuery.class); - if (this.like != null) query.like(this.like); - if (this.isActive != null) query.isActive(this.isActive); - if (this.codes != null) query.codes(this.codes); - if (this.ids != null) query.ids(this.ids); - if (this.excludedIds != null) query.excludedIds(this.excludedIds); - - this.enrichCommon(query); - - return query; - } - -} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/utils/BuildSubQueryInput.java b/notification-service/notification/src/main/java/gr/cite/notification/query/utils/BuildSubQueryInput.java new file mode 100644 index 000000000..28c8be2ed --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/query/utils/BuildSubQueryInput.java @@ -0,0 +1,90 @@ +package gr.cite.notification.query.utils; + +import gr.cite.tools.data.query.QueryContext; +import jakarta.persistence.criteria.*; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public class BuildSubQueryInput { + private final AbstractQuery query; + private final CriteriaBuilder criteriaBuilder; + private final Class entityType; + private final Class keyType; + private final Function, Expression> keyPathFunc; + private final BiFunction, CriteriaBuilder, Predicate> filterFunc; + + public BuildSubQueryInput(Builder builder) { + query = builder.query; + criteriaBuilder = builder.criteriaBuilder; + entityType = builder.entityType; + keyType = builder.keyType; + keyPathFunc = builder.keyPathFunc; + filterFunc = builder.filterFunc; + } + + public AbstractQuery getQuery() { + return query; + } + + public CriteriaBuilder getCriteriaBuilder() { + return criteriaBuilder; + } + + public Class getEntityType() { + return entityType; + } + + public Class getKeyType() { + return keyType; + } + + public Function, Expression> getKeyPathFunc() { + return keyPathFunc; + } + + public BiFunction, CriteriaBuilder, Predicate> getFilterFunc() { + return filterFunc; + } + + public static class Builder { + private final Class entityType; + private final Class keyType; + private AbstractQuery query; + private CriteriaBuilder criteriaBuilder; + private Function, Expression> keyPathFunc; + private BiFunction, CriteriaBuilder, Predicate> filterFunc; + + public Builder(Class entityType, Class keyType) { + this.entityType = entityType; + this.keyType = keyType; + } + + public Builder(Class entityType, Class keyType, QueryContext queryContext) { + this.entityType = entityType; + this.keyType = keyType; + this.query = queryContext.Query; + this.criteriaBuilder = queryContext.CriteriaBuilder; + } + + public Builder query(AbstractQuery query) { + this.query = query; + return this; + } + + public Builder criteriaBuilder(CriteriaBuilder criteriaBuilder) { + this.criteriaBuilder = criteriaBuilder; + return this; + } + + public Builder keyPathFunc(Function, Expression> keyPathFunc) { + this.keyPathFunc = keyPathFunc; + return this; + } + + public Builder filterFunc(BiFunction, CriteriaBuilder, Predicate> filterFunc) { + this.filterFunc = filterFunc; + return this; + } + } +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsService.java b/notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsService.java new file mode 100644 index 000000000..eda7c1eff --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsService.java @@ -0,0 +1,7 @@ +package gr.cite.notification.query.utils; + +import jakarta.persistence.criteria.Subquery; + +public interface QueryUtilsService { + Subquery buildSubQuery(BuildSubQueryInput parameters); +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsServiceImpl.java new file mode 100644 index 000000000..fd179178c --- /dev/null +++ b/notification-service/notification/src/main/java/gr/cite/notification/query/utils/QueryUtilsServiceImpl.java @@ -0,0 +1,18 @@ +package gr.cite.notification.query.utils; + +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Subquery; +import org.springframework.stereotype.Component; + +@Component +public class QueryUtilsServiceImpl implements QueryUtilsService { + @Override + public Subquery buildSubQuery(BuildSubQueryInput parameters){ + Subquery subQuery = parameters.getQuery().subquery(parameters.getKeyType()); + Root subQueryRoot = subQuery.from(parameters.getEntityType()); + subQuery.select(parameters.getKeyPathFunc().apply(subQueryRoot)).distinct(true); + subQuery.where(parameters.getFilterFunc().apply(subQueryRoot, parameters.getCriteriaBuilder())); + return subQuery; + } +} + diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/inappnotification/InAppNotificationServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/inappnotification/InAppNotificationServiceImpl.java index 1c928e53a..c42efc474 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/inappnotification/InAppNotificationServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/inappnotification/InAppNotificationServiceImpl.java @@ -5,7 +5,7 @@ import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.enums.NotificationInAppTracking; import gr.cite.notification.common.scope.user.UserScope; import gr.cite.notification.data.InAppNotificationEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.model.deleter.InAppNotificationDeleter; import gr.cite.notification.query.InAppNotificationQuery; import gr.cite.tools.data.deleter.DeleterFactory; @@ -32,7 +32,7 @@ public class InAppNotificationServiceImpl implements InAppNotificationService { private final QueryFactory queryFactory; - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final AuthorizationService authService; @@ -40,7 +40,7 @@ public class InAppNotificationServiceImpl implements InAppNotificationService { private final UserScope userScope; @Autowired - public InAppNotificationServiceImpl(QueryFactory queryFactory, TenantScopedEntityManager entityManager, AuthorizationService authService, DeleterFactory deleterFactory, UserScope userScope) { + public InAppNotificationServiceImpl(QueryFactory queryFactory, TenantEntityManager entityManager, AuthorizationService authService, DeleterFactory deleterFactory, UserScope userScope) { this.queryFactory = queryFactory; this.entityManager = entityManager; this.authService = authService; @@ -80,7 +80,7 @@ public class InAppNotificationServiceImpl implements InAppNotificationService { String sqlQuery = "UPDATE " + entity + " as e SET " + InAppNotificationEntity._trackingState + " = :trackingState, " + InAppNotificationEntity._updatedAt + "= :updatedAt, " + InAppNotificationEntity._readTime + " = :readTime " + "WHERE e." + InAppNotificationEntity._trackingState + " = :trackingStateCondition AND e." + InAppNotificationEntity._userId + " = :userId"; - Query query = this.entityManager.createQuery(sqlQuery) + Query query = this.entityManager.getEntityManager().createQuery(sqlQuery) .setParameter("trackingState", NotificationInAppTracking.DELIVERED) .setParameter("updatedAt", Instant.now()) .setParameter("readTime", Instant.now()) diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/EmailMessageBuilder.java b/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/EmailMessageBuilder.java index 3ae78942d..ab99f90e0 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/EmailMessageBuilder.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/EmailMessageBuilder.java @@ -2,19 +2,14 @@ package gr.cite.notification.service.message.builder; import gr.cite.notification.cache.NotificationTemplateCache; import gr.cite.notification.common.StringUtils; -import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.NotificationContactType; import gr.cite.notification.common.enums.NotificationTemplateChannel; -import gr.cite.notification.common.enums.NotificationTemplateKind; import gr.cite.notification.common.types.notification.FieldInfo; import gr.cite.notification.config.notification.NotificationConfig; import gr.cite.notification.config.notification.NotificationProperties; -import gr.cite.notification.data.LanguageEntity; import gr.cite.notification.data.NotificationEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.model.NotificationTemplate; -import gr.cite.notification.query.LanguageQuery; -import gr.cite.notification.query.NotificationTemplateQuery; import gr.cite.notification.service.formatting.FormattingService; import gr.cite.notification.service.message.common.MessageBuilderBase; import gr.cite.notification.service.message.infobuilder.MessageInfoBuilderService; @@ -45,8 +40,6 @@ public class EmailMessageBuilder extends MessageBuilderBase implements MessageBu private final MessageInfoBuilderService messageInfoBuilderService; private final Map cipherFields; private final NotificationTemplateService notificationTemplateService; - private final QueryFactory queryFactory; - @Autowired public EmailMessageBuilder(NotificationProperties properties, @Qualifier(NotificationConfig.BeanQualifier.FLOW_MAP) @@ -58,14 +51,13 @@ public class EmailMessageBuilder extends MessageBuilderBase implements MessageBu CipherService cipherService, CipherProfileProperties cipherProfileProperties, FormattingService formattingService, - NotificationTemplateCache cache, NotificationTemplateService notificationTemplateService, QueryFactory queryFactory) { + NotificationTemplateCache cache, NotificationTemplateService notificationTemplateService) { super(logger, errors, cipherService, cipherProfileProperties, formattingService, cache); this.properties = properties; this.flowMap = flowMap; this.messageInfoBuilderService = messageInfoBuilderService; this.cipherFields = cipherFields; this.notificationTemplateService = notificationTemplateService; - this.queryFactory = queryFactory; } @Override @@ -76,7 +68,7 @@ public class EmailMessageBuilder extends MessageBuilderBase implements MessageBu return null; } NotificationProperties.Flow options = this.flowMap.get("email").getOrDefault(notification.getType(), null); - NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.Email, this.queryFactory.query(LanguageQuery.class).codes(messageInfo.getLanguage()).isActive(IsActive.Active).first().getId()); + NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.Email,messageInfo.getLanguage()); if (options == null && template == null) { logger.error("Could not retrieve flow options for notification " + notification.getId() + " of type " + notification.getType()); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/InAppMessageBuilder.java b/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/InAppMessageBuilder.java index e2e75c86b..17936dff6 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/InAppMessageBuilder.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/message/builder/InAppMessageBuilder.java @@ -12,7 +12,6 @@ import gr.cite.notification.config.notification.NotificationProperties; import gr.cite.notification.data.NotificationEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.model.NotificationTemplate; -import gr.cite.notification.query.LanguageQuery; import gr.cite.notification.service.formatting.FormattingService; import gr.cite.notification.service.message.common.MessageBuilderBase; import gr.cite.notification.service.message.infobuilder.MessageInfoBuilderService; @@ -71,7 +70,7 @@ public class InAppMessageBuilder extends MessageBuilderBase implements MessageBu } NotificationProperties.Flow options = this.flowMap.get("in-app").getOrDefault(notification.getType(), null); - NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.Email, this.queryFactory.query(LanguageQuery.class).codes(messageInfo.getLanguage()).isActive(IsActive.Active).first().getId()); + NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.Email, messageInfo.getLanguage()); if (options == null && template == null) { logger.error("Could not retrieve flow options for notification " + notification.getId() + " of type " + notification.getType()); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/notification/NotificationServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/notification/NotificationServiceImpl.java index 40637e999..3bf92b0f9 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/notification/NotificationServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/notification/NotificationServiceImpl.java @@ -8,7 +8,7 @@ import gr.cite.notification.common.enums.NotificationContactType; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.TenantEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.NotificationEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.model.SendNotificationResult; @@ -55,7 +55,7 @@ import java.util.UUID; public class NotificationServiceImpl implements NotificationService { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationServiceImpl.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final AuthorizationService authService; private final AuthorizationService authorizationService; private final DeleterFactory deleterFactory; @@ -72,7 +72,7 @@ public class NotificationServiceImpl implements NotificationService { @Autowired public NotificationServiceImpl( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, AuthorizationService authService, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory, diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingService.java b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingService.java index 2f161f7b7..b1fc7e35f 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingService.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingService.java @@ -9,7 +9,6 @@ import java.util.UUID; public interface NotificationSchedulingService { - List getTenants(); CandidateInfo candidateToNotify(Instant lastCandidateNotificationCreationTimestamp); Boolean shouldWait(CandidateInfo candidateInfo); Boolean shouldOmitNotify(UUID notificationId); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingServiceImpl.java index 9ed9590c4..b315945e3 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationscheduling/NotificationSchedulingServiceImpl.java @@ -9,6 +9,7 @@ import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.config.notification.NotificationProperties; import gr.cite.notification.data.NotificationEntity; import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.model.SendNotificationResult; import gr.cite.notification.query.NotificationQuery; import gr.cite.notification.query.TenantQuery; @@ -18,6 +19,7 @@ import gr.cite.notification.service.notification.NotificationService; import gr.cite.notification.service.track.TrackingFactory; import gr.cite.notification.service.track.model.TrackerResponse; import gr.cite.tools.data.query.Ordering; +import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; @@ -44,34 +46,19 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling this.properties = properties; } - - @Override - public List getTenants() { - try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { - List tenantIds = List.of(new MiniTenant(new UUID(0L, 0L), "")); - TenantScope tenantScope = applicationContext.getBean(TenantScope.class); - if (tenantScope.isMultitenant()) { - FieldSet fieldSet = new BaseFieldSet(List.of(TenantEntity._id, TenantEntity._code)); - TenantQuery tenantQuery = this.applicationContext.getBean(TenantQuery.class); - List tenants = tenantQuery.isActive(IsActive.Active).collectAs(fieldSet); - tenantIds = tenants.stream().map(tenantEntity -> new MiniTenant(tenantEntity.getId(), tenantEntity.getCode())).collect(Collectors.toList()); - } - return tenantIds; - } - } - @Override public CandidateInfo candidateToNotify(Instant lastCandidateNotificationCreationTimestamp) { EntityManager entityManager = null; EntityTransaction transaction = null; CandidateInfo candidateInfo = null; try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); - NotificationQuery notificationQuery = applicationContext.getBean(NotificationQuery.class); + NotificationQuery notificationQuery = queryFactory.query(NotificationQuery.class); NotificationEntity candidates; notificationQuery = notificationQuery @@ -85,9 +72,20 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling NotificationNotifyState previousState = candidates.getNotifyState(); candidates.setNotifyState(NotificationNotifyState.PROCESSING); //candidates.setUpdatedAt(Instant.now()); - candidates = entityManager.merge(candidates); - entityManager.persist(candidates); - entityManager.flush(); + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + try { + if (candidates.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(candidates.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + candidates = entityManager.merge(candidates); + entityManager.persist(candidates); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); + } candidateInfo = new CandidateInfo(candidates.getId(), previousState, candidates.getCreatedAt()); } @@ -134,8 +132,21 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling if (!itIsTime) { notification.setNotifyState(candidateInfo.getPreviousState()); //notification.setUpdatedAt(Instant.now()); - notification = entityManager.merge(notification); - entityManager.persist(notification); + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + try { + if (notification.getTenantId() != null) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(notification.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + notification = entityManager.merge(notification); + entityManager.persist(notification); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); + } } shouldWait = !itIsTime; @@ -171,8 +182,21 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling if (age >= omitSeconds) { notification.setNotifyState(NotificationNotifyState.OMITTED); //notification.setUpdatedAt(Instant.now()); - notification = entityManager.merge(notification); - entityManager.persist(notification); + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + try { + if (notification.getTenantId() != null) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(notification.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + notification = entityManager.merge(notification); + entityManager.persist(notification); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); + } shouldOmit = true; } transaction.commit(); @@ -205,26 +229,44 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling NotificationEntity notification = notificationQuery.ids(notificationId).first(); if (notification == null) throw new IllegalArgumentException("notification is null"); + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + SendNotificationResult result = null; NotificationService notificationService = this.applicationContext.getBean(NotificationService.class); - result = notificationService.doNotify(notification); + try { + if (notification.getTenantId() != null) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(notification.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + result = notificationService.doNotify(notification); - if (result != null && result.getSuccess()) { - notification.setNotifyState(NotificationNotifyState.SUCCESSFUL); - notification.setTrackingData(result.getTrackingData()); - notification.setNotifiedWith(result.getContactType()); - notification.setNotifiedAt(Instant.now()); - } else { - notification.setNotifyState(NotificationNotifyState.ERROR); - notification.setRetryCount(notification.getRetryCount() != null ? notification.getRetryCount() + 1 : 0); - notification.setNotifiedWith(null); - notification.setNotifiedAt(null); + if (result != null && result.getSuccess()) { + notification.setNotifyState(NotificationNotifyState.SUCCESSFUL); + notification.setTrackingData(result.getTrackingData()); + notification.setNotifiedWith(result.getContactType()); + notification.setNotifiedAt(Instant.now()); + } else { + notification.setNotifyState(NotificationNotifyState.ERROR); + notification.setRetryCount(notification.getRetryCount() != null ? notification.getRetryCount() + 1 : 0); + notification.setNotifiedWith(null); + notification.setNotifiedAt(null); + } + //notification.setUpdatedAt(Instant.now()); + + NotificationEntity notification1 = entityManager.merge(notification); + entityManager.persist(notification1); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); } - //notification.setUpdatedAt(Instant.now()); - - NotificationEntity notification1 = entityManager.merge(notification); - entityManager.persist(notification1); + + //we want to return false for notification we want to add in the skip bag success = result != null && result.getSuccess(); @@ -247,12 +289,13 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling EntityTransaction transaction = null; CandidateInfo candidateInfo = null; try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); - NotificationQuery notificationQuery = applicationContext.getBean(NotificationQuery.class); + NotificationQuery notificationQuery = queryFactory.query(NotificationQuery.class); NotificationEntity candidates; notificationQuery = notificationQuery @@ -268,8 +311,20 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling NotificationNotifyState previousState = candidates.getNotifyState(); candidates.setTrackingProcess(NotificationTrackingProcess.PROCESSING); //candidates.setUpdatedAt(Instant.now()); - candidates = entityManager.merge(candidates); - entityManager.persist(candidates); + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + try { + if (candidates.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(candidates.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + candidates = entityManager.merge(candidates); + entityManager.persist(candidates); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); + } candidateInfo = new CandidateInfo(candidates.getId(), previousState, candidates.getCreatedAt()); } transaction.commit(); @@ -303,8 +358,22 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling if (age >= omitSeconds) { notification.setTrackingProcess(NotificationTrackingProcess.OMITTED); //notification.setUpdatedAt(Instant.now()); - notification = entityManager.merge(notification); - entityManager.persist(notification); + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + try { + if (notification.getTenantId() != null) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(notification.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + + notification = entityManager.merge(notification); + entityManager.persist(notification); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); + } shouldOmit = true; } transaction.commit(); @@ -327,41 +396,59 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling EntityTransaction transaction = null; Boolean success = null; try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) { + QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); entityManager = entityManagerFactory.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); - NotificationQuery notificationQuery = applicationContext.getBean(NotificationQuery.class); + NotificationQuery notificationQuery = queryFactory.query(NotificationQuery.class); NotificationEntity notification = notificationQuery.ids(notificationId).first(); if (notification == null) throw new IllegalArgumentException("notification is null"); if (notification.getNotifiedWith() == null) throw new IllegalArgumentException("Notification's Notified With is null"); if (notification.getNotifiedAt() == null) throw new IllegalArgumentException("Notification's Notified At is null"); - + + TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class); + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + TrackerResponse result = null; try { - TrackingFactory trackingFactory = applicationContext.getBean(TrackingFactory.class); - result = trackingFactory.fromContactType(notification.getNotifiedWith()).track(notification); - } catch (Exception e) { - logger.error("Could not complete track for notification " + notification.getId(), e); - } + if (notification.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(notification.getTenantId()).first(); + tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode()); + } else { + tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode()); + } + try { + TrackingFactory trackingFactory = applicationContext.getBean(TrackingFactory.class); + result = trackingFactory.fromContactType(notification.getNotifiedWith()).track(notification); + } catch (Exception e) { + logger.error("Could not complete track for notification " + notification.getId(), e); + } - if (result != null && notification.getTrackingProcess().equals(result.getTrackingProgress()) && notification.getTrackingState().equals(result.getTrackingState())) { - return false; - } + if (result != null && notification.getTrackingProcess().equals(result.getTrackingProgress()) && notification.getTrackingState().equals(result.getTrackingState())) { + return false; + } - if (result != null) { - notification.setTrackingState(result.getTrackingState()); - notification.setTrackingProcess(result.getTrackingProgress()); - notification.setTrackingData(result.getTrackingData()); - } else { - notification.setTrackingProcess(NotificationTrackingProcess.ERROR); - } - //notification.setUpdatedAt(Instant.now()); + if (result != null) { + notification.setTrackingState(result.getTrackingState()); + notification.setTrackingProcess(result.getTrackingProgress()); + notification.setTrackingData(result.getTrackingData()); + } else { + notification.setTrackingProcess(NotificationTrackingProcess.ERROR); + } + //notification.setUpdatedAt(Instant.now()); - NotificationEntity notification1 = entityManager.merge(notification); - entityManager.persist(notification1); + NotificationEntity notification1 = entityManager.merge(notification); + entityManager.persist(notification1); + entityManager.flush(); + } finally { + tenantScope.removeTempTenant(entityManager); + } + + //we want to return false for notification we want to add in the skip bag diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationServiceTemplateImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationServiceTemplateImpl.java index 0b2b74349..a65ec8cd7 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationServiceTemplateImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationServiceTemplateImpl.java @@ -13,9 +13,8 @@ import gr.cite.notification.common.types.notificationtemplate.FieldOptionsEntity import gr.cite.notification.common.types.notificationtemplate.NotificationTemplateValueEntity; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.NotificationTemplateEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.errorcode.ErrorThesaurusProperties; -import gr.cite.notification.model.Language; import gr.cite.notification.model.NotificationTemplate; import gr.cite.notification.model.builder.NotificationTemplateBuilder; import gr.cite.notification.model.deleter.NotificationTemplateDeleter; @@ -56,7 +55,7 @@ import java.util.UUID; public class NotificationServiceTemplateImpl implements NotificationTemplateService { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationServiceTemplateImpl.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final AuthorizationService authService; private final AuthorizationService authorizationService; private final DeleterFactory deleterFactory; @@ -69,7 +68,7 @@ public class NotificationServiceTemplateImpl implements NotificationTemplateServ @Autowired public NotificationServiceTemplateImpl( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, AuthorizationService authService, AuthorizationService authorizationService, DeleterFactory deleterFactory, BuilderFactory builderFactory, @@ -112,7 +111,7 @@ public class NotificationServiceTemplateImpl implements NotificationTemplateServ data.setNotificationType(model.getNotificationType()); data.setChannel(model.getChannel()); data.setKind(model.getKind()); - data.setLanguageId(model.getLanguageId()); + data.setLanguageCode(model.getLanguageCode()); data.setValue(this.jsonHandlingService.toJsonSafe(this.buildNotificationTemplateValueEntity(model.getValue()))); data.setUpdatedAt(Instant.now()); @@ -184,11 +183,11 @@ public class NotificationServiceTemplateImpl implements NotificationTemplateServ } @Override - public NotificationTemplate lookupOverriddenTemplates(UUID notificationType, NotificationTemplateChannel channel, UUID language) { + public NotificationTemplate lookupOverriddenTemplates(UUID notificationType, NotificationTemplateChannel channel, String language) { NotificationTemplateQuery query = this.queryFactory.query(NotificationTemplateQuery.class) .notificationTypes(notificationType) .channels(channel) - .languages(language) + .languageCodes(language) .kinds(NotificationTemplateKind.Primary) .isActive(IsActive.Active); @@ -200,9 +199,7 @@ public class NotificationServiceTemplateImpl implements NotificationTemplateServ NotificationTemplate._channel, NotificationTemplate._notificationType, NotificationTemplate._kind, - - NotificationTemplate._language + "." + Language._id, - NotificationTemplate._language + "." + Language._code, + NotificationTemplate._languageCode, NotificationTemplate._value + "." + NotificationTemplateValue._subjectKey, NotificationTemplate._value + "." + NotificationTemplateValue._subjectText, diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationTemplateService.java b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationTemplateService.java index bbeb106b2..a64200aa7 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationTemplateService.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/notificationtemplate/NotificationTemplateService.java @@ -16,6 +16,6 @@ public interface NotificationTemplateService { NotificationTemplate persist(NotificationTemplatePersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; - NotificationTemplate lookupOverriddenTemplates(UUID notificationType, NotificationTemplateChannel channel, UUID language); + NotificationTemplate lookupOverriddenTemplates(UUID notificationType, NotificationTemplateChannel channel, String language); } diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java index 8437565d8..5e7953c40 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/tenant/TenantServiceImpl.java @@ -7,6 +7,7 @@ import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.TenantEntity; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.model.Tenant; import gr.cite.notification.model.builder.TenantBuilder; import gr.cite.notification.model.deleter.TenantDeleter; @@ -21,7 +22,6 @@ import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; -import jakarta.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -42,14 +42,14 @@ public class TenantServiceImpl implements TenantService { private final ConventionService conventionService; - private final EntityManager entityManager; + private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; public TenantServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, - EntityManager entityManager, + TenantEntityManager entityManager, BuilderFactory builderFactory) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -75,6 +75,7 @@ public class TenantServiceImpl implements TenantService { data.setCode(model.getCode()); data.setIsActive(IsActive.Active); data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); this.entityManager.persist(data); } else { diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/tenantconfiguration/TenantConfigurationServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/tenantconfiguration/TenantConfigurationServiceImpl.java index 47e49c500..5b2bd8775 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/tenantconfiguration/TenantConfigurationServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/tenantconfiguration/TenantConfigurationServiceImpl.java @@ -11,7 +11,7 @@ import gr.cite.notification.common.types.tenantconfiguration.EmailClientConfigur import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer; import gr.cite.notification.convention.ConventionService; import gr.cite.notification.data.TenantConfigurationEntity; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.model.TenantConfiguration; import gr.cite.notification.model.builder.TenantConfigurationBuilder; @@ -54,11 +54,11 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic private final MessageSource messageSource; private final BuilderFactory builderFactory; - private final TenantScopedEntityManager dbContext; + private final TenantEntityManager dbContext; private final DeleterFactory deleterFactory; @Autowired - public TenantConfigurationServiceImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, AuthorizationService authorizationService, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, BuilderFactory builderFactory, TenantScopedEntityManager dbContext, DeleterFactory deleterFactory) { + public TenantConfigurationServiceImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, AuthorizationService authorizationService, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, BuilderFactory builderFactory, TenantEntityManager dbContext, DeleterFactory deleterFactory) { this.applicationContext = applicationContext; this.jsonHandlingService = jsonHandlingService; this.authorizationService = authorizationService; diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java index 13fd51a13..ff5006f3f 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/user/UserServiceImpl.java @@ -3,19 +3,24 @@ package gr.cite.notification.service.user; import com.fasterxml.jackson.core.JsonProcessingException; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.notification.authorization.AuthorizationFlags; -import gr.cite.notification.authorization.OwnedResource; import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.enums.IsActive; +import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.convention.ConventionService; -import gr.cite.notification.data.UserContactInfoEntity; -import gr.cite.notification.data.UserCredentialEntity; -import gr.cite.notification.data.UserEntity; +import gr.cite.notification.data.*; import gr.cite.notification.integrationevent.inbox.usertouched.UserTouchedIntegrationEvent; +import gr.cite.notification.model.Tenant; import gr.cite.notification.model.User; import gr.cite.notification.model.builder.UserBuilder; +import gr.cite.notification.model.deleter.TenantUserDeleter; +import gr.cite.notification.model.deleter.UserContactInfoDeleter; +import gr.cite.notification.model.deleter.UserCredentialDeleter; import gr.cite.notification.model.deleter.UserDeleter; +import gr.cite.notification.query.TenantQuery; +import gr.cite.notification.query.TenantUserQuery; import gr.cite.notification.query.UserContactInfoQuery; +import gr.cite.notification.query.UserCredentialQuery; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; @@ -30,11 +35,14 @@ import gr.cite.tools.logging.MapLogEntry; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; import java.time.Instant; import java.util.*; +import java.util.stream.Collectors; @Service public class UserServiceImpl implements UserService { @@ -47,20 +55,23 @@ public class UserServiceImpl implements UserService { private final ConventionService conventionService; - private final EntityManager entityManager; + private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; private final QueryFactory queryFactory; + private final TenantScope tenantScope; + + private final MessageSource messageSource; + private final JsonHandlingService jsonHandlingService; public UserServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, - EntityManager entityManager, - BuilderFactory builderFactory, - QueryFactory queryFactory, + TenantEntityManager entityManager, + BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource, JsonHandlingService jsonHandlingService) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -68,6 +79,8 @@ public class UserServiceImpl implements UserService { this.entityManager = entityManager; this.builderFactory = builderFactory; this.queryFactory = queryFactory; + this.tenantScope = tenantScope; + this.messageSource = messageSource; this.jsonHandlingService = jsonHandlingService; } @@ -94,11 +107,6 @@ public class UserServiceImpl implements UserService { this.entityManager.persist(data); - for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { - this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId())); - } - - this.entityManager.persist(this.buildUserCredentialEntityFromEventData(model)); } else { data.setName(model.getName()); data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile())); @@ -107,52 +115,6 @@ public class UserServiceImpl implements UserService { this.entityManager.merge(data); - List contactInfoList = this.queryFactory.query(UserContactInfoQuery.class).userIds(model.getId()).collect(); - - List updatedContacts = new ArrayList<>(); - for (UserContactInfoEntity c : contactInfoList) { - boolean updated = false; - - //Contact found on the existing, update it - for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { - if (c.getType() == eventC.getType() && Objects.equals(c.getValue(), eventC.getValue())) { - c.setUserId(model.getId()); - c.setType(c.getType()); - c.setValue(c.getValue()); - c.setOrdinal(c.getOrdinal()); - c.setUpdatedAt(Instant.now()); - c.setIsActive(IsActive.Active); - - this.entityManager.merge(c); - - updatedContacts.add(c); - updated = true; - break; - } - } - - //Contact was not found on the event data, remove it - if (!updated) { - c.setIsActive(IsActive.Inactive); - this.entityManager.merge(c); - } - } - - for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { - boolean updated = false; - for (UserContactInfoEntity c : updatedContacts) { - if (c.getType() == eventC.getType() && Objects.equals(c.getValue(), eventC.getValue())) { - updated = true; - break; - } - } - - //New contact detected since it came with the event data and was not existing in the database, add it - if (!updated) { - this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId())); - } - } - } } else { throw new MyApplicationException("Not valid user id"); @@ -160,6 +122,12 @@ public class UserServiceImpl implements UserService { this.entityManager.flush(); + this.persistContactInfo(model.getUserContactInfo(), data.getId()); + this.persistUserCredential(model.getCredentials(), data.getId()); + this.persistTenantUser(model.getTenantUsers(), data.getId()); + + this.entityManager.flush(); + return this.builderFactory.builder(UserBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, User._id), data); } @@ -172,30 +140,104 @@ public class UserServiceImpl implements UserService { this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); } - private UserContactInfoEntity buildContactInfoEntityFromEventData(UserTouchedIntegrationEvent.UserContactInfo eventC, UUID userId) { - UserContactInfoEntity contactInfo = new UserContactInfoEntity(); - contactInfo.setId(UUID.randomUUID()); - contactInfo.setUserId(userId); - contactInfo.setType(eventC.getType()); - contactInfo.setValue(eventC.getValue()); - contactInfo.setOrdinal(eventC.getOrdinal()); - contactInfo.setCreatedAt(Instant.now()); - contactInfo.setUpdatedAt(Instant.now()); - contactInfo.setIsActive(IsActive.Active); + private void persistContactInfo(List models, UUID userId) throws InvalidApplicationException { + List items = this.queryFactory.query(UserContactInfoQuery.class) + .userIds(userId) + .collect(); + List updatedCreatedIds = new ArrayList<>(); + if (models != null) { + for (UserTouchedIntegrationEvent.UserContactInfo model : models) { + UserContactInfoEntity data = items.stream().filter(x -> x.getType().equals(model.getType()) && x.getValue().equals(model.getValue())).findFirst().orElse(null); + if (data == null) { + data = new UserContactInfoEntity(); + data.setId(UUID.randomUUID()); + data.setUserId(userId); + data.setType(model.getType()); + data.setValue(model.getValue()); + data.setOrdinal(model.getOrdinal()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + entityManager.persist(data); + } else { + data.setOrdinal(model.getOrdinal()); + entityManager.merge(data); + } + updatedCreatedIds.add(data.getId()); + } + } + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(UserContactInfoDeleter.class).delete(toDelete); - return contactInfo; + entityManager.flush(); } - private UserCredentialEntity buildUserCredentialEntityFromEventData(UserTouchedIntegrationEvent event) { - UserCredentialEntity credentialEntity = new UserCredentialEntity(); - credentialEntity.setId(UUID.randomUUID()); - credentialEntity.setUserId(event.getId()); - credentialEntity.setExternalId(event.getSubjectId()); - credentialEntity.setCreatedAt(Instant.now()); - credentialEntity.setUpdatedAt(Instant.now()); - credentialEntity.setIsActive(IsActive.Active); + private void persistUserCredential(List models, UUID userId) throws InvalidApplicationException { + List items = this.queryFactory.query(UserCredentialQuery.class) + .userIds(userId) + .isActive(IsActive.Active) + .collect(); + List updatedCreatedIds = new ArrayList<>(); + if (models != null) { + for (UserTouchedIntegrationEvent.UserCredential model : models) { + UserCredentialEntity data = items.stream().filter(x -> x.getExternalId().equals(model.getSubjectId())).findFirst().orElse(null); + if (data == null) { + data = new UserCredentialEntity(); + data.setId(UUID.randomUUID()); + data.setUserId(userId); + data.setExternalId(model.getSubjectId()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + entityManager.persist(data); + } + updatedCreatedIds.add(data.getId()); + } + } + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(UserCredentialDeleter.class).delete(toDelete); - return credentialEntity; + entityManager.flush(); } -} + private void persistTenantUser(List models, UUID userId) throws InvalidApplicationException { + List items = this.queryFactory.query(TenantUserQuery.class) + .userIds(userId) + .isActive(IsActive.Active) + .collect(); + + List updatedCreatedIds = new ArrayList<>(); + if (models != null) { + List tenantEntities = this.queryFactory.query(TenantQuery.class) + .ids(models.stream().map(UserTouchedIntegrationEvent.TenantUser::getTenant).toList()) + .isActive(IsActive.Active) + .collectAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + for (UserTouchedIntegrationEvent.TenantUser model : models) { + TenantUserEntity data = items.stream().filter(x -> x.getTenantId().equals(model.getTenant())).findFirst().orElse(null); + if (data == null) { + try { + TenantEntity tenant = tenantEntities.stream().filter(x -> x.getId().equals(model.getTenant())).findFirst().orElse(null); + if (tenant == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getTenant(), Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale())); + this.tenantScope.setTempTenant(this.entityManager.getEntityManager(), tenant.getId(), tenant.getCode()); + data = new TenantUserEntity(); + data.setId(UUID.randomUUID()); + data.setUserId(userId); + data.setTenantId(model.getTenant()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + entityManager.persist(data); + } finally { + this.tenantScope.removeTempTenant(this.entityManager.getEntityManager()); + } + } + updatedCreatedIds.add(data.getId()); + } + } + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(TenantUserDeleter.class).delete(toDelete); + + entityManager.flush(); + } + +} \ No newline at end of file diff --git a/notification-service/notification/src/main/java/gr/cite/notification/service/userNotificationPreference/UserNotificationPreferenceServiceImpl.java b/notification-service/notification/src/main/java/gr/cite/notification/service/userNotificationPreference/UserNotificationPreferenceServiceImpl.java index 860c39395..64ddd6d47 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/service/userNotificationPreference/UserNotificationPreferenceServiceImpl.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/service/userNotificationPreference/UserNotificationPreferenceServiceImpl.java @@ -5,7 +5,7 @@ import gr.cite.notification.authorization.Permission; import gr.cite.notification.common.enums.NotificationContactType; import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer; import gr.cite.notification.config.notification.NotificationConfig; -import gr.cite.notification.data.TenantScopedEntityManager; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.data.UserNotificationPreferenceEntity; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.model.UserNotificationPreference; @@ -43,7 +43,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr private final TenantConfigurationService tenantConfigurationService; private final Map> globalPoliciesMap; private final ErrorThesaurusProperties errors; - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; @Autowired public UserNotificationPreferenceServiceImpl(QueryFactory queryFactory, @@ -52,7 +52,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr TenantConfigurationService tenantConfigurationService, @Qualifier(NotificationConfig.BeanQualifier.GLOBAL_POLICIES_MAP) Map> globalPoliciesMap, - ErrorThesaurusProperties errors, TenantScopedEntityManager entityManager) { + ErrorThesaurusProperties errors, TenantEntityManager entityManager) { this.queryFactory = queryFactory; this.builderFactory = builderFactory; this.authService = authService;