Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Sofia Papacharalampous 2024-04-04 17:03:03 +03:00
commit d94759ff5d
124 changed files with 2880 additions and 2984 deletions

View File

@ -4,6 +4,7 @@ import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope; import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.data.QueueInboxEntity; import gr.cite.annotation.data.QueueInboxEntity;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.integrationevent.TrackedEvent; import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval.AnnotationEntitiesRemovalIntegrationEventHandler; import gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval.AnnotationEntitiesRemovalIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.annotationentitiestouch.AnnotationEntitiesTouchedIntegrationEventHandler; import gr.cite.annotation.integrationevent.inbox.annotationentitiestouch.AnnotationEntitiesTouchedIntegrationEventHandler;
@ -278,6 +279,10 @@ public class InboxRepositoryImpl implements InboxRepository {
entityManager = entityManagerFactory.createEntityManager(); entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction(); transaction = entityManager.getTransaction();
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
transaction.begin(); transaction.begin();
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);

View File

@ -32,6 +32,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.*; import java.util.*;
@Component @Component
@ -44,15 +45,26 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
private final ValidatorFactory validatorFactory; private final ValidatorFactory validatorFactory;
private final ApplicationContext applicationContext;
private final QueryFactory queryFactory; 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.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractorProperties = claimExtractorProperties;
this.auditService = auditService;
this.tenantEntityManager = tenantEntityManager;
this.deleterFactory = deleterFactory;
this.tenantScope = tenantScope;
} }
@Override @Override
@ -65,41 +77,25 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
this.validatorFactory.validator(AnnotationEntitiesRemovalIntegrationEvent.AnnotationEntitiesRemovalIntegrationEventValidator.class).validateForce(event); this.validatorFactory.validator(AnnotationEntitiesRemovalIntegrationEvent.AnnotationEntitiesRemovalIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
TenantScope scope = this.applicationContext.getBean(TenantScope.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
if (scope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(properties.getTenantId(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
// logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
// return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
scope.setTenant(null, scope.getDefaultTenantCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); 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(); tenantEntityManager.disableTenantFilters();
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> items = entityUserQuery List<EntityUserEntity> items = entityUserQuery
@ -110,38 +106,24 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(items); 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( auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event) new AbstractMap.SimpleEntry<String, Object>("model", event)
)); ));
transaction.commit();
} catch (Exception e) { } catch (Exception ex) {
transaction.rollback(); status = EventProcessingStatus.Error;
throw e; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
try {
this.tenantEntityManager.enableTenantFilters();
} catch (InvalidApplicationException e) {
} }
} 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) { return status;
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -14,6 +14,7 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.Tenant; import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.query.EntityUserQuery; import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.annotation.query.TenantQuery; 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.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
@ -32,6 +33,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -46,15 +48,24 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
private final ValidatorFactory validatorFactory; private final ValidatorFactory validatorFactory;
private final ApplicationContext applicationContext;
private final QueryFactory queryFactory; 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.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext; this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractorProperties = claimExtractorProperties;
this.auditService = auditService;
this.tenantEntityManager = tenantEntityManager;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.deleterFactory = deleterFactory;
this.tenantScope = tenantScope;
} }
@Override @Override
@ -67,40 +78,23 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
this.validatorFactory.validator(AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntitiesTouchedIntegrationEventValidator.class).validateForce(event); this.validatorFactory.validator(AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntitiesTouchedIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
TenantScope scope = this.applicationContext.getBean(TenantScope.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
if (scope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(properties.getTenantId(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
// logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
// return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
scope.setTenant(null, scope.getDefaultTenantCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); 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(); tenantEntityManager.disableTenantFilters();
for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) { for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) {
@ -122,7 +116,6 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
data.setUpdatedAt(Instant.now()); data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active); data.setIsActive(IsActive.Active);
entityManager.persist(data);
} }
updatedCreatedIds.add(data.getId()); updatedCreatedIds.add(data.getId());
} }
@ -130,39 +123,27 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
List<EntityUserEntity> toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); List<EntityUserEntity> 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); deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(toDelete);
entityManager.flush(); tenantEntityManager.flush();
}
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Persist, Map.ofEntries( auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event) new AbstractMap.SimpleEntry<String, Object>("model", event)
)); ));
}
transaction.commit(); } catch (Exception ex) {
} catch (Exception e) { status = EventProcessingStatus.Error;
transaction.rollback(); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
throw e;
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
try {
this.tenantEntityManager.enableTenantFilters();
} catch (InvalidApplicationException e) {
} }
} 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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); return status;
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -1,26 +1,18 @@
package gr.cite.annotation.integrationevent.inbox.tenantremoval; 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.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.scope.fake.FakeRequestScope; 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.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.service.tenant.TenantService; 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.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.logging.LoggerService; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; 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.annotation.Scope;
import org.springframework.stereotype.Component; 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 static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantRemovalIntegrationEventHandlerImpl.class));
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final ApplicationContext applicationContext; private final ClaimExtractorProperties claimExtractorProperties;
private final TenantService tenantService;
private final AuditService auditService;
public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext) { 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.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext; this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractorProperties = claimExtractorProperties;
this.tenantService = tenantService;
this.auditService = auditService;
this.tenantEntityManager = tenantEntityManager;
this.tenantRemovalConsistencyHandler = tenantRemovalConsistencyHandler;
} }
@Override @Override
@ -49,65 +48,34 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
if (event == null) if (event == null)
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
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())))) {
if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) status = EventProcessingStatus.Postponed;
return EventProcessingStatus.Postponed; currentPrincipalResolver.pop();
return status;
}
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(); tenantEntityManager.disableTenantFilters();
TenantService tenantService = this.applicationContext.getBean(TenantService.class);
tenantService.deleteAndSave(event.getId()); tenantService.deleteAndSave(event.getId());
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", event.getId()) new AbstractMap.SimpleEntry<String, Object>("id", event.getId())
)); ));
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
transaction.commit();
} catch (Exception e) { } catch (Exception ex) {
transaction.rollback(); status = EventProcessingStatus.Error;
throw e; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
} }
return status;
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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return null;
} }
} }

View File

@ -1,27 +1,20 @@
package gr.cite.annotation.integrationevent.inbox.tenanttouch; 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.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; 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.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.persist.TenantTouchedIntegrationEventPersist; import gr.cite.annotation.model.persist.TenantTouchedIntegrationEventPersist;
import gr.cite.annotation.service.tenant.TenantService; 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.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; 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)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantTouchedIntegrationEventHandlerImpl.class));
protected final ApplicationContext applicationContext;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory; 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) { public TenantTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager) {
this.applicationContext = applicationContext;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractorProperties = claimExtractorProperties;
this.tenantService = tenantService;
this.auditService = auditService;
this.tenantEntityManager = tenantEntityManager;
} }
@Override @Override
@ -56,59 +57,25 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn
model.setCode(event.getCode()); model.setCode(event.getCode());
this.validatorFactory.validator(TenantTouchedIntegrationEventPersist.TenantTouchedIntegrationEventPersistValidator.class).validateForce(model); this.validatorFactory.validator(TenantTouchedIntegrationEventPersist.TenantTouchedIntegrationEventPersistValidator.class).validateForce(model);
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null; tenantEntityManager.disableTenantFilters();
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); 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);
tenantEntityManager.disableTenantFilters();
TenantService tenantService = this.applicationContext.getBean(TenantService.class);
tenantService.persist(model, null); tenantService.persist(model, null);
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model) new AbstractMap.SimpleEntry<String, Object>("model", model)
)); ));
transaction.commit();
} catch (Exception e) { } catch (Exception ex) {
transaction.rollback(); status = EventProcessingStatus.Error;
throw e; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
} }
return status;
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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return null;
} }
} }

View File

@ -1,12 +1,10 @@
package gr.cite.annotation.integrationevent.inbox.userremoval; 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.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; 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.common.scope.tenant.TenantScope;
import gr.cite.annotation.data.TenantEntity; import gr.cite.annotation.data.TenantEntity;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; 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.model.Tenant;
import gr.cite.annotation.query.TenantQuery; import gr.cite.annotation.query.TenantQuery;
import gr.cite.annotation.service.user.UserService; 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.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
@ -43,22 +37,35 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ApplicationContext applicationContext;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final MessageSource messageSource; 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( public UserRemovalIntegrationEventHandlerImpl(
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
ApplicationContext applicationContext,
ErrorThesaurusProperties errors, 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.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext;
this.errors = errors; this.errors = errors;
this.messageSource = messageSource; 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 @Override
@ -71,77 +78,44 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName()); logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName());
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
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)); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(properties.getTenantId(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
// logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
scope.setTenant(null, scope.getDefaultTenantCode()); // return EventProcessingStatus.Error;
this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) {
if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) status = EventProcessingStatus.Postponed;
return EventProcessingStatus.Postponed; currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
return status;
}
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()); userService.deleteAndSave(event.getUserId());
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Delete, Map.ofEntries( auditService.track(AuditableAction.User_Delete, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", event.getUserId()) new AbstractMap.SimpleEntry<String, Object>("id", event.getUserId())
)); ));
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
} catch (Exception ex) {
transaction.commit(); status = EventProcessingStatus.Error;
} catch (Exception e) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
transaction.rollback();
throw e;
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
} }
transaction.commit(); return status;
} 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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -4,7 +4,6 @@ import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; 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.common.scope.tenant.TenantScope;
import gr.cite.annotation.data.TenantEntity; import gr.cite.annotation.data.TenantEntity;
import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; 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.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; 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)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class));
protected final ApplicationContext applicationContext;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory; 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( public UserTouchedIntegrationEventHandlerImpl(
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
ApplicationContext applicationContext, ValidatorFactory validatorFactory, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager) {
ValidatorFactory validatorFactory) {
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext;
this.validatorFactory = validatorFactory; 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 @Override
@ -63,71 +68,37 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event); this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
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)); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(properties.getTenantId(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
// logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
// return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
scope.setTenant(null, scope.getDefaultTenantCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); 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); userService.persist(event, null);
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Persist, Map.ofEntries( auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event) new AbstractMap.SimpleEntry<String, Object>("model", event)
)); ));
transaction.commit(); } catch (Exception ex) {
} catch (Exception e) { status = EventProcessingStatus.Error;
transaction.rollback(); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
throw e;
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
} }
} catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working return status;
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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -36,6 +36,12 @@ public final class Permission {
public static String EditLanguage = "EditLanguage"; public static String EditLanguage = "EditLanguage";
public static String DeleteLanguage = "DeleteLanguage"; public static String DeleteLanguage = "DeleteLanguage";
//NotificationTemplate
public static String BrowseNotificationTemplate = "BrowseNotificationTemplate";
public static String EditNotificationTemplate = "EditNotificationTemplate";
public static String DeleteNotificationTemplate = "DeleteNotificationTemplate";
//Language //Language
public static String BrowseStatistics = "BrowseStatistics"; public static String BrowseStatistics = "BrowseStatistics";
public static String BrowsePublicStatistics = "BrowsePublicStatistics"; public static String BrowsePublicStatistics = "BrowsePublicStatistics";

View File

@ -4,6 +4,7 @@ import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.fake.FakeRequestScope; import eu.eudat.commons.fake.FakeRequestScope;
import eu.eudat.data.QueueInboxEntity; import eu.eudat.data.QueueInboxEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.integrationevent.TrackedEvent; import eu.eudat.integrationevent.TrackedEvent;
import eu.eudat.query.QueueInboxQuery; import eu.eudat.query.QueueInboxQuery;
import gr.cite.queueinbox.entity.QueueInbox; import gr.cite.queueinbox.entity.QueueInbox;
@ -28,12 +29,10 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; 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; protected final ApplicationContext applicationContext;
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InboxRepositoryImpl.class));
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final InboxProperties inboxProperties; private final InboxProperties inboxProperties;
@ -234,10 +233,17 @@ public class InboxRepositoryImpl implements InboxRepository {
} }
private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) { private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) {
QueueInboxEntity queueMessage = new QueueInboxEntity(); QueueInboxEntity queueMessage = new QueueInboxEntity();
queueMessage.setId(UUID.randomUUID()); queueMessage.setId(UUID.randomUUID());
Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null; Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null;
if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId); 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.setExchange(this.inboxProperties.getExchange());
queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey());
queueMessage.setQueue(inboxCreatorParams.getQueueName()); queueMessage.setQueue(inboxCreatorParams.getQueueName());
@ -265,6 +271,10 @@ public class InboxRepositoryImpl implements InboxRepository {
entityManager = entityManagerFactory.createEntityManager(); entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction(); transaction = entityManager.getTransaction();
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
transaction.begin(); transaction.begin();
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); 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()); logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId());
} else { } else {
EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); EventProcessingStatus status = this.processMessage(queueInboxMessage);
switch (status) { switch (status) {
case Success: { case Success: {
queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL);
@ -317,29 +327,39 @@ public class InboxRepositoryImpl implements InboxRepository {
return success; return success;
} }
private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) {
private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) {
IntegrationEventHandler handler = null; 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) if (handler == null)
return EventProcessingStatus.Discard; return EventProcessingStatus.Discard;
IntegrationEventProperties properties = new IntegrationEventProperties(); IntegrationEventProperties properties = new IntegrationEventProperties();
properties.setAppId(appId); properties.setAppId(queueInboxMessage.getApplicationId());
properties.setMessageId(messageId); 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)) // using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag))
// { // {
try { try {
return handler.handle(properties, message); return handler.handle(properties, queueInboxMessage.getMessage());
} catch (Exception ex) { } 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; return EventProcessingStatus.Error;
} }
// } // }
} }
private Boolean RoutingKeyMatched(String routingKey, List<String> topics) { private Boolean routingKeyMatched(String routingKey, List<String> topics) {
if (topics == null || topics.isEmpty()) if (topics == null || topics.isEmpty())
return false; return false;
return topics.stream().anyMatch(x -> x.equals(routingKey)); return topics.stream().anyMatch(x -> x.equals(routingKey));

View File

@ -1,10 +1,13 @@
package eu.eudat.integrationevent.inbox; package eu.eudat.integrationevent.inbox;
import java.util.UUID;
public class IntegrationEventProperties { public class IntegrationEventProperties {
private String messageId; private String messageId;
private String appId; private String appId;
private UUID tenantId;
public String getMessageId() { public String getMessageId() {
return messageId; return messageId;
@ -22,4 +25,11 @@ public class IntegrationEventProperties {
this.appId = appId; this.appId = appId;
} }
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
} }

View File

@ -23,7 +23,7 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.TENANT_TOUCH); message.setType(OutboxIntegrationEvent.TENANT_TOUCH);
message.setTenantId(event.getId()); // message.setTenantId(event.getId()); //Hack Can not Queue inbox before tenant created
message.setEvent(event); message.setEvent(event);
this.outboxService.publish(message); this.outboxService.publish(message);

View File

@ -167,11 +167,6 @@ public class StorageFileCleanupTask implements Closeable, ApplicationListener<A
return success; return success;
} }
@Transactional
private void purgeSafe(StorageFileService storageFileService, UUID fileId){
storageFileService.purgeSafe(fileId);
}
private CandidateInfo candidate(Instant lastCandidateCreationTimestamp) { private CandidateInfo candidate(Instant lastCandidateCreationTimestamp) {
EntityTransaction transaction = null; EntityTransaction transaction = null;
EntityManager entityManager = null; EntityManager entityManager = null;

View File

@ -6,8 +6,10 @@ import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.TenantEntity; import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager; import eu.eudat.data.TenantEntityManager;
import eu.eudat.data.UserEntity; import eu.eudat.data.UserEntity;
import eu.eudat.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent; import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent;
import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEventHandler; import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.userremoval.UserRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler; import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler;
import eu.eudat.query.TenantQuery; import eu.eudat.query.TenantQuery;
import eu.eudat.query.UserQuery; import eu.eudat.query.UserQuery;
@ -44,8 +46,12 @@ public class MaintenanceController {
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler; private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler;
private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler; private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler;
private final TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler;
@Autowired @Autowired
public MaintenanceController( public MaintenanceController(
AuthorizationService authorizationService, AuthorizationService authorizationService,
@ -54,14 +60,18 @@ public class MaintenanceController {
QueryFactory queryFactory, QueryFactory queryFactory,
TenantEntityManager entityManager, TenantEntityManager entityManager,
UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler,
TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler) { UserRemovalIntegrationEventHandler userRemovalIntegrationEventHandler,
TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler,
TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.elasticService = elasticService; this.elasticService = elasticService;
this.auditService = auditService; this.auditService = auditService;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.entityManager = entityManager; this.entityManager = entityManager;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler; this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.userRemovalIntegrationEventHandler = userRemovalIntegrationEventHandler;
this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler; this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler;
this.tenantRemovalIntegrationEventHandler = tenantRemovalIntegrationEventHandler;
} }
@RequestMapping(method = RequestMethod.POST, value = {"/index/elastic"}) @RequestMapping(method = RequestMethod.POST, value = {"/index/elastic"})
@ -92,13 +102,19 @@ public class MaintenanceController {
this.authorizationService.authorizeForce(Permission.ManageQueueEvents); this.authorizationService.authorizeForce(Permission.ManageQueueEvents);
this.entityManager.disableTenantFilters(); this.entityManager.disableTenantFilters();
UserQuery userQuery = queryFactory.query(UserQuery.class).isActive(IsActive.Active); UserQuery userQuery = queryFactory.query(UserQuery.class);
List<UserEntity> users = userQuery.collect(); userQuery.isActive(IsActive.Active);
List<UserEntity> activeUsers = userQuery.collect();
userQuery.isActive(IsActive.Inactive);
List<UserEntity> inactiveUsers = userQuery.collect();
this.entityManager.enableTenantFilters(); this.entityManager.enableTenantFilters();
for(UserEntity user : users) for(UserEntity user : activeUsers)
this.userTouchedIntegrationEventHandler.handle(user.getId()); this.userTouchedIntegrationEventHandler.handle(user.getId());
for(UserEntity user : inactiveUsers)
this.userRemovalIntegrationEventHandler.handle(user.getId());
this.auditService.track(AuditableAction.Maintenance_SendUserTouchEvents); this.auditService.track(AuditableAction.Maintenance_SendUserTouchEvents);
} }
@ -108,17 +124,24 @@ public class MaintenanceController {
this.authorizationService.authorizeForce(Permission.ManageQueueEvents); this.authorizationService.authorizeForce(Permission.ManageQueueEvents);
this.entityManager.disableTenantFilters(); this.entityManager.disableTenantFilters();
TenantQuery tenantQuery = queryFactory.query(TenantQuery.class).isActive(IsActive.Active); TenantQuery tenantQuery = queryFactory.query(TenantQuery.class);
List<TenantEntity> tenants = tenantQuery.collect(); tenantQuery.isActive(IsActive.Active);
List<TenantEntity> activeTenants = tenantQuery.collect();
tenantQuery.isActive(IsActive.Inactive);
List<TenantEntity> inactiveTenants = tenantQuery.collect();
this.entityManager.enableTenantFilters(); this.entityManager.enableTenantFilters();
for (TenantEntity tenant : tenants) { for (TenantEntity tenant : activeTenants) {
TenantTouchedIntegrationEvent event = new TenantTouchedIntegrationEvent(); TenantTouchedIntegrationEvent event = new TenantTouchedIntegrationEvent();
event.setId(tenant.getId()); event.setId(tenant.getId());
event.setCode(tenant.getCode()); event.setCode(tenant.getCode());
this.tenantTouchedIntegrationEventHandler.handle(event); this.tenantTouchedIntegrationEventHandler.handle(event);
} }
for (TenantEntity tenant : inactiveTenants) {
this.tenantRemovalIntegrationEventHandler.handle(tenant.getId());
}
this.auditService.track(AuditableAction.Maintenance_SendTenantTouchEvents); this.auditService.track(AuditableAction.Maintenance_SendTenantTouchEvents);
} }
} }

View File

@ -110,7 +110,25 @@ permissions:
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
# NotificationTemplate
BrowseNotificationTemplate:
roles: [ ]
clients: [ ]
allowAnonymous: true
allowAuthenticated: true
EditNotificationTemplate:
roles:
- TenantAdmin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteNotificationTemplate:
roles:
- TenantAdmin
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Language # Language
BrowseLanguage: BrowseLanguage:
roles: [ ] roles: [ ]

View File

@ -41,7 +41,7 @@ queue:
generate-file-topic: generate.file generate-file-topic: generate.file
rabbitmq: rabbitmq:
enable: true enable: true
interval-seconds: 30 interval-seconds: 3
options: options:
retry-threashold: 100 retry-threashold: 100
retry-delay-step-seconds: 300 retry-delay-step-seconds: 300
@ -54,7 +54,7 @@ queue:
exchange: null exchange: null
rabbitmq: rabbitmq:
enable: true enable: true
interval-seconds: 30 interval-seconds: 3
options: options:
retry-threashold: 100 retry-threashold: 100
retry-delay-step-seconds: 300 retry-delay-step-seconds: 300

View File

@ -26,11 +26,11 @@ BEGIN
notified_with smallint, notified_with smallint,
CONSTRAINT "ntf_Notification_pkey" PRIMARY KEY (id), CONSTRAINT "ntf_Notification_pkey" PRIMARY KEY (id),
CONSTRAINT "ntf_Notification_tenant_fkey" FOREIGN KEY (tenant) CONSTRAINT "ntf_Notification_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id) MATCH SIMPLE REFERENCES public."ntf_Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION, ON DELETE NO ACTION,
CONSTRAINT "ntf_Notification_user_fkey" FOREIGN KEY ("user") CONSTRAINT "ntf_Notification_user_fkey" FOREIGN KEY ("user")
REFERENCES public."User" (id) MATCH SIMPLE REFERENCES public."ntf_User" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION ON DELETE NO ACTION
); );

View File

@ -10,20 +10,15 @@ BEGIN
channel smallint NOT NULL, channel smallint NOT NULL,
notification_type uuid NOT NULL, notification_type uuid NOT NULL,
kind smallint NOT NULL, kind smallint NOT NULL,
language uuid NOT NULL, language_code character varying(200) COLLATE pg_catalog."default" NOT NULL,
value text NOT NULL, value text NOT NULL,
is_active smallint NOT NULL, is_active smallint NOT NULL,
created_at timestamp without time zone NOT NULL, created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL, updated_at timestamp without time zone NOT NULL,
tenant uuid, tenant uuid,
CONSTRAINT "NotificationTemplate_pkey" PRIMARY KEY (id), CONSTRAINT "NotificationTemplate_pkey" PRIMARY KEY (id),
CONSTRAINT "NotificationTemplate_language_fkey" FOREIGN KEY (language)
REFERENCES public."Language" (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
NOT VALID,
CONSTRAINT "NotificationTemplate_tenant_fkey" FOREIGN KEY (tenant) CONSTRAINT "NotificationTemplate_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id) MATCH SIMPLE REFERENCES public."ntf_Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION ON DELETE NO ACTION
NOT VALID NOT VALID

View File

@ -21,7 +21,7 @@ BEGIN
is_active smallint NOT NULL, is_active smallint NOT NULL,
CONSTRAINT "ntf_QueryInbox_pkey" PRIMARY KEY (id), CONSTRAINT "ntf_QueryInbox_pkey" PRIMARY KEY (id),
CONSTRAINT "ntf_QueryInbox_tenant_fkey" FOREIGN KEY (tenant) CONSTRAINT "ntf_QueryInbox_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id) MATCH SIMPLE REFERENCES public."ntf_Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION ON DELETE NO ACTION
NOT VALID NOT VALID

View File

@ -21,7 +21,7 @@ BEGIN
is_active smallint NOT NULL, is_active smallint NOT NULL,
CONSTRAINT "ntf_QueueOutbox_pkey" PRIMARY KEY (id), CONSTRAINT "ntf_QueueOutbox_pkey" PRIMARY KEY (id),
CONSTRAINT "ntf_QueueOutbox_tenant_fkey" FOREIGN KEY (tenant) CONSTRAINT "ntf_QueueOutbox_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id) MATCH SIMPLE REFERENCES public."ntf_Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION ON DELETE NO ACTION
); );

View File

@ -21,11 +21,11 @@ BEGIN
is_active smallint NOT NULL, is_active smallint NOT NULL,
CONSTRAINT "ntf_InAppNotification_pkey" PRIMARY KEY (id), CONSTRAINT "ntf_InAppNotification_pkey" PRIMARY KEY (id),
CONSTRAINT "ntf_InAppNotification_tenant_fkey" FOREIGN KEY (tenant) CONSTRAINT "ntf_InAppNotification_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id) MATCH SIMPLE REFERENCES public."ntf_tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION, ON DELETE NO ACTION,
CONSTRAINT "ntf_InAppNotification_user_fkey" FOREIGN KEY ("user") CONSTRAINT "ntf_InAppNotification_user_fkey" FOREIGN KEY ("user")
REFERENCES public."User" (id) MATCH SIMPLE REFERENCES public."ntf_User" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION
ON DELETE NO ACTION ON DELETE NO ACTION
); );

View File

@ -329,17 +329,6 @@ const appRoutes: Routes = [
}) })
}, },
}, },
{
path: 'index-managment',
loadChildren: () => import('./ui/admin/index-managment/index-managment.module').then(m => m.IndexManagmentModule),
data: {
authContext: {
permissions: [AppPermission.ViewMaintenancePage]
},
breadcrumb: true,
title: 'GENERAL.TITLES.INDEX-MANAGMENT'
},
},
{ {
path: 'maintenance-tasks', path: 'maintenance-tasks',
loadChildren: () => import('./ui/admin/maintenance-tasks/maintenance-tasks.module').then(m => m.MaintenanceTasksModule), loadChildren: () => import('./ui/admin/maintenance-tasks/maintenance-tasks.module').then(m => m.MaintenanceTasksModule),
@ -347,7 +336,8 @@ const appRoutes: Routes = [
authContext: { authContext: {
permissions: [AppPermission.ViewMaintenancePage] permissions: [AppPermission.ViewMaintenancePage]
}, },
breadcrumb: true breadcrumb: true,
title: 'GENERAL.TITLES.MAINTENANCE-TASKS'
}, },
}, },
{ {

View File

@ -2,7 +2,6 @@ import { NotificationTemplateChannel } from "@app/core/common/enum/notification-
import { NotificationTemplateKind } from "@app/core/common/enum/notification-template-kind"; import { NotificationTemplateKind } from "@app/core/common/enum/notification-template-kind";
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model"; import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
import { Guid } from "@common/types/guid"; import { Guid } from "@common/types/guid";
import { Language } from "../language/language";
import { NotificationDataType } from "@app/core/common/enum/notification-data-type"; import { NotificationDataType } from "@app/core/common/enum/notification-data-type";
import { EmailOverrideMode } from "@app/core/common/enum/email-override-mode"; import { EmailOverrideMode } from "@app/core/common/enum/email-override-mode";
import { NotificationType } from "@app/core/common/enum/notification-type"; import { NotificationType } from "@app/core/common/enum/notification-type";
@ -11,7 +10,7 @@ export interface NotificationTemplate extends BaseEntity{
channel: NotificationTemplateChannel; channel: NotificationTemplateChannel;
notificationType: NotificationType; notificationType: NotificationType;
kind: NotificationTemplateKind; kind: NotificationTemplateKind;
language: Language; languageCode: string;
value: NotificationTemplateValue; value: NotificationTemplateValue;
} }
@ -49,7 +48,7 @@ export interface NotificationTemplatePersist extends BaseEntityPersist{
channel: NotificationTemplateChannel; channel: NotificationTemplateChannel;
notificationType: NotificationType; notificationType: NotificationType;
kind: NotificationTemplateKind; kind: NotificationTemplateKind;
languageId: Guid; languageCode: string;
value: NotificationTemplateValuePersist; value: NotificationTemplateValuePersist;
} }

View File

@ -28,4 +28,18 @@ export class MaintenanceService extends BaseService {
.delete<any>(url).pipe( .delete<any>(url).pipe(
catchError((error: any) => throwError(error))); catchError((error: any) => throwError(error)));
} }
sendUserTouchEvents(): Observable<any> {
const url = `${this.apiBase}/events/users/touch`;
return this.http
.delete<any>(url).pipe(
catchError((error: any) => throwError(error)));
}
sendTenantTouchEvents(): Observable<any> {
const url = `${this.apiBase}/events/tenants/touch`;
return this.http
.delete<any>(url).pipe(
catchError((error: any) => throwError(error)));
}
} }

View File

@ -1,15 +0,0 @@
<div class="container-fluid">
<div class="row root">
<div class="col-md-10 offset-md-1">
<div class="mt-4 mb-4"></div>
<mat-card class="p-2">
<div class="mt-2">
<div style="color: red;">Warning: Danger zone. It might delete Dataset tags if not careful</div>
<button mat-raised-button color="primary" (click)="generateIndex($event)" class="lightblue-btn button">Generate Index</button>
<button mat-raised-button color="primary" (click)="clearIndex($event)" class="lightblue-btn button">Clear Index</button>
</div>
</mat-card>
</div>
</div>
</div>

View File

@ -1,7 +0,0 @@
.root {
// padding-bottom: 2em;
.button {
margin: 5px;
}
}

View File

@ -1,87 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router';
import { MaintenanceService } from '@app/core/services/maintenance/maintenance.service';
@Component({
selector: 'app-index-managment',
templateUrl: './index-managment.component.html',
styleUrls: ['./index-managment.component.scss']
})
export class IndexManagmentComponent extends BaseComponent implements OnInit {
constructor(
private uiNotificationService: UiNotificationService,
private translate: TranslateService,
private router: Router,
private maintenanceService: MaintenanceService
)
{
super();
}
ngOnInit() {
}
generateIndex(ev: Event) {
(ev.target as HTMLButtonElement).disabled = true;
// this.datasetService.generateIndex().pipe(takeUntil(this._destroyed)).subscribe(
// response => {
// (ev.target as HTMLButtonElement).disabled = false;
// this.onCallbackSuccess();
// },
// error => {
// (ev.target as HTMLButtonElement).disabled = false;
// this.onCallbackError(error);
// }
// );
this.maintenanceService.generateElasticIndex().pipe(takeUntil(this._destroyed)).subscribe(
response => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackSuccess();
},
error => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackError(error);
}
);
}
clearIndex(ev: Event) {
(ev.target as HTMLButtonElement).disabled = true;
// this.datasetService.clearIndex().pipe(takeUntil(this._destroyed)).subscribe(
// response => {
// (ev.target as HTMLButtonElement).disabled = false;
// this.onCallbackSuccess();
// },
// error => {
// (ev.target as HTMLButtonElement).disabled = false;
// this.onCallbackError(error);
// }
// );
this.maintenanceService.clearElasticIndex().pipe(takeUntil(this._destroyed)).subscribe(
response => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackSuccess();
},
error => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackError(error);
}
);
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification( this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/reload']).then(() => this.router.navigate(['/index-managment']));
}
onCallbackError(error: any) {
this.uiNotificationService.snackBarNotification( error, SnackBarNotificationLevel.Error);
//this.validateAllFormFields(this.formGroup);
}
}

View File

@ -1,20 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IndexManagmentRoutingModule } from './index-managment.routing';
import { IndexManagmentComponent } from './index-managment.component';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module';
@NgModule({
declarations: [IndexManagmentComponent],
imports: [
CommonUiModule,
CommonFormsModule,
ConfirmationDialogModule,
IndexManagmentRoutingModule
]
})
export class IndexManagmentModule { }

View File

@ -1,15 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { IndexManagmentComponent } from './index-managment.component';
import { AdminAuthGuard } from '@app/core/admin-auth-guard.service';
const routes: Routes = [
{ path: '', component: IndexManagmentComponent, canActivate: [AdminAuthGuard] },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class IndexManagmentRoutingModule { }

View File

@ -1,10 +1,47 @@
<!-- <div class="row root"> --> <!-- <div class="row root">
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
<mat-card class="p-4 mt-4"> <mat-card class="p-4 mt-4">
<div style="color: red;">Warning: Danger zone. Irreversible actions!</div> <div style="color: red;">Warning: Danger zone. Irreversible actions!</div>
<div> <div>
<!-- <button mat-raised-button color="primary" (click)="migrateSemantics($event)" class="lightblue-btn button">Migrate semantics</button> --> <button mat-raised-button color="primary" (click)="migrateSemantics($event)" class="lightblue-btn button">Migrate semantics</button>
<!-- <button mat-raised-button color="primary" (click)="addRdaInSemantics($event)" class="lightblue-btn button">Add rda in semantics</button> --> <button mat-raised-button color="primary" (click)="addRdaInSemantics($event)" class="lightblue-btn button">Add rda in semantics</button>
</div> </div>
</mat-card> </mat-card>
</div> -->
<div class="container-fluid">
<div class="row root">
<div class="col-md-10 offset-md-1">
<div class="row mt-4 mb-4"></div>
<mat-accordion multi>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{language.instant('MAINTENANCE-TASKS.SECTIONS.INDEXES.TITLE')}}
</mat-panel-title>
<mat-panel-description>
{{language.instant('MAINTENANCE-TASKS.SECTIONS.INDEXES.DESCRIPTION')}}
</mat-panel-description>
</mat-expansion-panel-header>
<div class="mt-2">
<button mat-raised-button color="primary" (click)="generateIndex($event)" class="lightblue-btn button">{{language.instant('MAINTENANCE-TASKS.SECTIONS.INDEXES.ACTIONS.GENERATE-INDEX')}}</button>
<button mat-raised-button color="primary" (click)="clearIndex($event)" class="lightblue-btn button">{{language.instant('MAINTENANCE-TASKS.SECTIONS.INDEXES.ACTIONS.CLEAR-INDEX')}}</button>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{language.instant('MAINTENANCE-TASKS.SECTIONS.EVENTS.TITLE')}}
</mat-panel-title>
<mat-panel-description>
{{language.instant('MAINTENANCE-TASKS.SECTIONS.EVENTS.DESCRIPTION')}}
</mat-panel-description>
</mat-expansion-panel-header>
<div class="mt-2">
<button mat-raised-button color="primary" (click)="sendUserTouchEvents($event)" class="lightblue-btn button">{{language.instant('MAINTENANCE-TASKS.SECTIONS.EVENTS.ACTIONS.SEND-USER-TOUCH')}}</button>
<button mat-raised-button color="primary" (click)="sendTenantTouchEvents($event)" class="lightblue-btn button">{{language.instant('MAINTENANCE-TASKS.SECTIONS.EVENTS.ACTIONS.SEND-TENANT-TOUCH')}}</button>
</div>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
</div> </div>

View File

@ -1,9 +1,12 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { MaintenanceService } from '@app/core/services/maintenance/maintenance.service'; import { MaintenanceService } from '@app/core/services/maintenance/maintenance.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseComponent } from '@common/base/base.component'; import { BaseComponent } from '@common/base/base.component';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'app-maintenance-tasks', selector: 'app-maintenance-tasks',
@ -13,6 +16,8 @@ import { TranslateService } from '@ngx-translate/core';
export class MaintenanceTasksComponent extends BaseComponent implements OnInit { export class MaintenanceTasksComponent extends BaseComponent implements OnInit {
constructor( constructor(
protected dialog: MatDialog,
protected language: TranslateService,
private maintenanceService: MaintenanceService, private maintenanceService: MaintenanceService,
private uiNotificationService: UiNotificationService, private uiNotificationService: UiNotificationService,
private translate: TranslateService, private translate: TranslateService,
@ -53,6 +58,130 @@ export class MaintenanceTasksComponent extends BaseComponent implements OnInit {
// ); // );
// } // }
generateIndex(ev: Event) {
this.dialog.open(ConfirmationDialogComponent, {
data: {
message: this.language.instant('MAINTENANCE-TASKS.CONFIRMATION.MESSAGE'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
},
maxWidth: '30em'
})
.afterClosed()
.subscribe(confirm => {
if (confirm) {
this.doGenerateIndex(ev);
}
});
}
private doGenerateIndex(ev: Event) {
(ev.target as HTMLButtonElement).disabled = true;
this.maintenanceService.generateElasticIndex().pipe(takeUntil(this._destroyed)).subscribe(
_ => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackSuccess();
},
error => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackError(error);
}
);
}
clearIndex(ev: Event) {
this.dialog.open(ConfirmationDialogComponent, {
data: {
message: this.language.instant('MAINTENANCE-TASKS.CONFIRMATION.MESSAGE'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
},
maxWidth: '30em'
})
.afterClosed()
.subscribe(confirm => {
if (confirm) {
this.doClearIndex(ev);
}
});
}
private doClearIndex(ev: Event) {
(ev.target as HTMLButtonElement).disabled = true;
this.maintenanceService.clearElasticIndex().pipe(takeUntil(this._destroyed)).subscribe(
_ => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackSuccess();
},
error => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackError(error);
}
);
}
sendUserTouchEvents(ev: Event) {
this.dialog.open(ConfirmationDialogComponent, {
data: {
message: this.language.instant('MAINTENANCE-TASKS.CONFIRMATION.MESSAGE'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
},
maxWidth: '30em'
})
.afterClosed()
.subscribe(confirm => {
if (confirm) {
this.doSendUserTouchEvents(ev);
}
});
}
private doSendUserTouchEvents(ev: Event) {
(ev.target as HTMLButtonElement).disabled = true;
this.maintenanceService.sendUserTouchEvents().pipe(takeUntil(this._destroyed)).subscribe(
_ => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackSuccess();
},
error => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackError(error);
}
);
}
sendTenantTouchEvents(ev: Event) {
this.dialog.open(ConfirmationDialogComponent, {
data: {
message: this.language.instant('MAINTENANCE-TASKS.CONFIRMATION.MESSAGE'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
},
maxWidth: '30em'
})
.afterClosed()
.subscribe(confirm => {
if (confirm) {
this.doSendTenantTouchEvents(ev);
}
});
}
private doSendTenantTouchEvents(ev: Event) {
(ev.target as HTMLButtonElement).disabled = true;
this.maintenanceService.sendTenantTouchEvents().pipe(takeUntil(this._destroyed)).subscribe(
_ => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackSuccess();
},
error => {
(ev.target as HTMLButtonElement).disabled = false;
this.onCallbackError(error);
}
);
}
onCallbackSuccess(): void { onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); this.uiNotificationService.snackBarNotification(this.translate.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/reload']).then(() => this.router.navigate(['/maintenance-tasks'])); this.router.navigate(['/reload']).then(() => this.router.navigate(['/maintenance-tasks']));

View File

@ -45,13 +45,13 @@
<div class="col-12 col-lg-4"> <div class="col-12 col-lg-4">
<mat-form-field class="w-100"> <mat-form-field class="w-100">
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.LANGUAGE' | translate}}</mat-label> <mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.FIELDS.LANGUAGE' | translate}}</mat-label>
<mat-select [formControl]="formGroup.get('languageId')" name="language" required> <mat-select [formControl]="formGroup.get('languageCode')" name="language" required>
<mat-option *ngFor="let language of languages" [value]="language.id"> <mat-option *ngFor="let language of languages" [value]="language.code">
{{language.code}} {{language.code}}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="formGroup.get('languageId').hasError('backendError')">{{formGroup.get('languageId').getError('backendError').message}}</mat-error> <mat-error *ngIf="formGroup.get('languageCode').hasError('backendError')">{{formGroup.get('languageCode').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('languageId').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="formGroup.get('languageCode').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12 col-lg-4"> <div class="col-12 col-lg-4">

View File

@ -15,7 +15,7 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements
channel: NotificationTemplateChannel; channel: NotificationTemplateChannel;
notificationType: NotificationType; notificationType: NotificationType;
kind: NotificationTemplateKind; kind: NotificationTemplateKind;
languageId: Guid; languageCode: string;
value: NotificationTemplateValueEditorModel = new NotificationTemplateValueEditorModel(); value: NotificationTemplateValueEditorModel = new NotificationTemplateValueEditorModel();
permissions: string[]; permissions: string[];
@ -30,7 +30,7 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements
this.channel = item.channel; this.channel = item.channel;
this.notificationType = item.notificationType; this.notificationType = item.notificationType;
this.kind = item.kind; 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); } if (item.value) { this.value = new NotificationTemplateValueEditorModel(this.validationErrorModel).fromModel(item.value); }
} }
@ -45,7 +45,7 @@ export class NotificationTemplateEditorModel extends BaseEditorModel implements
channel: [{ value: this.channel, disabled: disabled }, context.getValidation('channel').validators], channel: [{ value: this.channel, disabled: disabled }, context.getValidation('channel').validators],
notificationType: [{ value: this.notificationType, disabled: disabled }, context.getValidation('notificationType').validators], notificationType: [{ value: this.notificationType, disabled: disabled }, context.getValidation('notificationType').validators],
kind: [{ value: this.kind, disabled: disabled }, context.getValidation('kind').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({ value: this.value.buildForm({
rootPath: `value.` 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: 'channel', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'channel')] });
baseValidationArray.push({ key: 'notificationType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'notificationType')] }); 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: '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: 'value', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'value')] });
baseValidationArray.push({ key: 'hash', validators: [] }); baseValidationArray.push({ key: 'hash', validators: [] });

View File

@ -24,8 +24,7 @@ export class NotificationTemplateEditorResolver extends BaseEditorResolver {
nameof<NotificationTemplate>(x => x.notificationType), nameof<NotificationTemplate>(x => x.notificationType),
nameof<NotificationTemplate>(x => x.kind), nameof<NotificationTemplate>(x => x.kind),
[nameof<NotificationTemplate>(x => x.language),nameof<Language>(x => x.id)].join('.'), nameof<NotificationTemplate>(x => x.languageCode),
[nameof<NotificationTemplate>(x => x.language),nameof<Language>(x => x.code)].join('.'),
[nameof<NotificationTemplate>(x => x.value),nameof<NotificationTemplateValue>(x => x.subjectText)].join('.'), [nameof<NotificationTemplate>(x => x.value),nameof<NotificationTemplateValue>(x => x.subjectText)].join('.'),
[nameof<NotificationTemplate>(x => x.value),nameof<NotificationTemplateValue>(x => x.subjectKey)].join('.'), [nameof<NotificationTemplate>(x => x.value),nameof<NotificationTemplateValue>(x => x.subjectKey)].join('.'),

View File

@ -119,7 +119,7 @@ export class SidebarComponent implements OnInit {
if (this.authentication.hasPermission(AppPermission.ViewSupportiveMaterialPage)) this.adminItems.routes.push({ path: '/supportive-material', title: 'SIDE-BAR.SUPPORTIVE-MATERIAL', icon: 'help_center' }); if (this.authentication.hasPermission(AppPermission.ViewSupportiveMaterialPage)) this.adminItems.routes.push({ path: '/supportive-material', title: 'SIDE-BAR.SUPPORTIVE-MATERIAL', icon: 'help_center' });
if (this.authentication.hasPermission(AppPermission.ViewNotificationTemplatePage)) this.adminItems.routes.push({ path: '/notification-templates', title: 'SIDE-BAR.NOTIFICATION-TEMPLATES', icon: 'grid_guides' }); if (this.authentication.hasPermission(AppPermission.ViewNotificationTemplatePage)) this.adminItems.routes.push({ path: '/notification-templates', title: 'SIDE-BAR.NOTIFICATION-TEMPLATES', icon: 'grid_guides' });
if (this.authentication.hasPermission(AppPermission.ViewNotificationPage)) this.adminItems.routes.push({ path: '/notifications', title: 'SIDE-BAR.NOTIFICATIONS', icon: 'notifications' }); if (this.authentication.hasPermission(AppPermission.ViewNotificationPage)) this.adminItems.routes.push({ path: '/notifications', title: 'SIDE-BAR.NOTIFICATIONS', icon: 'notifications' });
if (this.authentication.hasPermission(AppPermission.ViewMaintenancePage)) this.adminItems.routes.push({ path: '/index-managment', title: 'SIDE-BAR.MAINTENANCE', icon: 'build' }); if (this.authentication.hasPermission(AppPermission.ViewMaintenancePage)) this.adminItems.routes.push({ path: '/maintenance-tasks', title: 'SIDE-BAR.MAINTENANCE', icon: 'build' });
this.groupMenuItems.push(this.adminItems); this.groupMenuItems.push(this.adminItems);
this.infoItems = { this.infoItems = {

View File

@ -90,7 +90,8 @@
"USERS": "Users", "USERS": "Users",
"PROFILE": "My Profile", "PROFILE": "My Profile",
"LOGIN": "Login", "LOGIN": "Login",
"DATASET-OVERVIEW": "Description Overview" "DATASET-OVERVIEW": "Description Overview",
"MAINTENANCE-TASKS": "Maintenance"
}, },
"FILE-TRANSFORMER": { "FILE-TRANSFORMER": {
"PDF": "PDF", "PDF": "PDF",
@ -271,6 +272,29 @@
"PRIVATE": "Hidden" "PRIVATE": "Hidden"
} }
}, },
"MAINTENANCE-TASKS": {
"SECTIONS": {
"INDEXES": {
"TITLE": "Manage indexes",
"DESCRIPTION": "From here you can manage the Elastic indexes",
"ACTIONS": {
"GENERATE-INDEX": "Generate Index",
"CLEAR-INDEX": "Clear Index"
}
},
"EVENTS": {
"TITLE": "Manage events",
"DESCRIPTION": "From here you can manage the Message Queue events",
"ACTIONS": {
"SEND-USER-TOUCH": "Send user touch events",
"SEND-TENANT-TOUCH": "Send tenant touch events"
}
}
},
"CONFIRMATION": {
"MESSAGE": "Are you sure you want to perform this action?"
}
},
"DESCRIPTION-TEMPLATE-EDITOR": { "DESCRIPTION-TEMPLATE-EDITOR": {
"TITLE": { "TITLE": {
"NEW": "New API Client", "NEW": "New API Client",

View File

@ -3,7 +3,7 @@ package gr.cite.notification.web;
import gr.cite.notification.web.scope.tenant.TenantInterceptor; import gr.cite.notification.web.scope.tenant.TenantInterceptor;
import gr.cite.notification.web.scope.tenant.TenantScopeClaimInterceptor; import gr.cite.notification.web.scope.tenant.TenantScopeClaimInterceptor;
import gr.cite.notification.web.scope.tenant.TenantScopeHeaderInterceptor; 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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

View File

@ -26,6 +26,8 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; 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 //In the example below, the default client handler will be ignored by the resolver
@Override @Override
public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() { public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() {
return List.of(PermissionClientAuthorizationHandler.class); return new ArrayList<>();
} }
}; };
} }

View File

@ -75,17 +75,4 @@ public class PrincipalController {
} }
@GetMapping("my-tenants")
public List<String> myTenants() {
logger.debug("my-tenants");
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();
List<String> 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());
}
} }

View File

@ -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) {
}
}

View File

@ -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 {
}

View File

@ -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<UserInterceptorCacheService.UserInterceptorCacheValue> {
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<UserInterceptorCacheValue> valueClass() {
return UserInterceptorCacheValue.class;
}
@Override
public String keyOf(UserInterceptorCacheValue value) {
return this.buildKey(value.getSubjectId());
}
public String buildKey(String subject) {
HashMap<String, String> keyParts = new HashMap<>();
keyParts.put("$subject$", subject);
return this.generateKey(keyParts);
}
}

View File

@ -54,8 +54,10 @@ public class TenantByCodeCacheService extends CacheService<TenantByCodeCacheServ
@EventListener @EventListener
public void handleTenantTouchedEvent(TenantTouchedEvent event) { public void handleTenantTouchedEvent(TenantTouchedEvent event) {
if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantCode())); if (!this.conventionService.isNullOrEmpty(event.getTenantCode()))
if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode())) this.evict(this.buildKey(event.getPreviousTenantCode())); this.evict(this.buildKey(event.getTenantCode()));
if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode()))
this.evict(this.buildKey(event.getPreviousTenantCode()));
} }
@Override @Override
@ -69,8 +71,8 @@ public class TenantByCodeCacheService extends CacheService<TenantByCodeCacheServ
} }
public String buildKey(String code) { public String buildKey(String code) {
return this.generateKey(new HashMap<>() {{ HashMap<String, String> keyParts = new HashMap<>();
put("$code$", code); keyParts.put("$code$", code);
}}); return this.generateKey(keyParts);
} }
} }

View File

@ -55,7 +55,8 @@ public class TenantByIdCacheService extends CacheService<TenantByIdCacheService.
@EventListener @EventListener
public void handleTenantTouchedEvent(TenantTouchedEvent event) { public void handleTenantTouchedEvent(TenantTouchedEvent event) {
if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantId())); if (!this.conventionService.isNullOrEmpty(event.getTenantCode()))
this.evict(this.buildKey(event.getTenantId()));
} }
@Override @Override
@ -69,8 +70,8 @@ public class TenantByIdCacheService extends CacheService<TenantByIdCacheService.
} }
public String buildKey(UUID id) { public String buildKey(UUID id) {
return this.generateKey(new HashMap<>() {{ HashMap<String, String> keyParts = new HashMap<>();
put("$tenantId$", id.toString().toLowerCase(Locale.ROOT)); keyParts.put("$tenantId$", id.toString().toLowerCase(Locale.ROOT));
}}); return this.generateKey(keyParts);
} }
} }

View File

@ -4,6 +4,7 @@ package gr.cite.notification.web.scope.tenant;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; 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.authorization.Permission;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.common.scope.tenant.TenantScope; 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.UserEntity;
import gr.cite.notification.data.tenant.TenantScopedBaseEntity; import gr.cite.notification.data.tenant.TenantScopedBaseEntity;
import gr.cite.notification.errorcode.ErrorThesaurusProperties; 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.exception.MyForbiddenException;
import gr.cite.tools.logging.LoggerService; 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.hibernate.Session;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -26,15 +35,9 @@ import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.context.request.WebRequestInterceptor;
import javax.management.InvalidApplicationException; 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.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID;
@Component @Component
public class TenantInterceptor implements WebRequestInterceptor { public class TenantInterceptor implements WebRequestInterceptor {
@ -45,11 +48,10 @@ public class TenantInterceptor implements WebRequestInterceptor {
private final CurrentPrincipalResolver currentPrincipalResolver; private final CurrentPrincipalResolver currentPrincipalResolver;
private final ClaimExtractor claimExtractor; private final ClaimExtractor claimExtractor;
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final ErrorThesaurusProperties errorThesaurusProperties;
private final TenantScopeProperties tenantScopeProperties; private final TenantScopeProperties tenantScopeProperties;
private final UserAllowedTenantCacheService userAllowedTenantCacheService; private final UserAllowedTenantCacheService userAllowedTenantCacheService;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final QueryUtilsService queryUtilsService;
@PersistenceContext @PersistenceContext
public EntityManager entityManager; public EntityManager entityManager;
@ -60,35 +62,37 @@ public class TenantInterceptor implements WebRequestInterceptor {
CurrentPrincipalResolver currentPrincipalResolver, CurrentPrincipalResolver currentPrincipalResolver,
ClaimExtractor claimExtractor, ClaimExtractor claimExtractor,
ApplicationContext applicationContext, ApplicationContext applicationContext,
ErrorThesaurusProperties errorThesaurusProperties,
TenantScopeProperties tenantScopeProperties, TenantScopeProperties tenantScopeProperties,
UserAllowedTenantCacheService userAllowedTenantCacheService, UserAllowedTenantCacheService userAllowedTenantCacheService,
ErrorThesaurusProperties errors) { ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.userScope = userScope; this.userScope = userScope;
this.currentPrincipalResolver = currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor; this.claimExtractor = claimExtractor;
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.errorThesaurusProperties = errorThesaurusProperties;
this.tenantScopeProperties = tenantScopeProperties; this.tenantScopeProperties = tenantScopeProperties;
this.userAllowedTenantCacheService = userAllowedTenantCacheService; this.userAllowedTenantCacheService = userAllowedTenantCacheService;
this.errors = errors; this.errors = errors;
this.queryUtilsService = queryUtilsService;
} }
@Override @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.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return;
if (!this.tenantScope.isMultitenant()) return; if (!this.tenantScope.isMultitenant()) return;
boolean isAllowedNoTenant = this.applicationContext.getBean(AuthorizationService.class).authorize(Permission.AllowNoTenant); boolean isAllowedNoTenant = this.applicationContext.getBean(AuthorizationService.class).authorize(Permission.AllowNoTenant);
if (tenantScope.isSet() && this.entityManager != null) { if (tenantScope.isSet() && this.entityManager != null) {
List<String> currentPrincipalTenantCodes = this.claimExtractor.asStrings(this.currentPrincipalResolver.currentPrincipal(), TenantScope.TenantCodesClaimName); List<String> currentPrincipalTenantCodes = this.claimExtractor.asStrings(this.currentPrincipalResolver.currentPrincipal(), ClaimNames.TenantCodesClaimName);
if ((currentPrincipalTenantCodes == null || !currentPrincipalTenantCodes.contains(tenantScope.getTenantCode())) && !isAllowedNoTenant) { if ((currentPrincipalTenantCodes == null || !currentPrincipalTenantCodes.contains(tenantScope.getTenantCode())) && !isAllowedNoTenant) {
logger.warn("tenant not allowed {}", this.tenantScope.getTenant()); 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; boolean isUserAllowedTenant = false;
if (this.tenantScope.isDefaultTenant()){
isUserAllowedTenant = true;
} else {
UserAllowedTenantCacheService.UserAllowedTenantCacheValue cacheValue = this.userAllowedTenantCacheService.lookup(this.userAllowedTenantCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.getTenant())); UserAllowedTenantCacheService.UserAllowedTenantCacheValue cacheValue = this.userAllowedTenantCacheService.lookup(this.userAllowedTenantCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.getTenant()));
if (cacheValue != null) { if (cacheValue != null) {
isUserAllowedTenant = cacheValue.isAllowed(); isUserAllowedTenant = cacheValue.isAllowed();
@ -96,24 +100,32 @@ public class TenantInterceptor implements WebRequestInterceptor {
isUserAllowedTenant = this.isUserAllowedTenant(); isUserAllowedTenant = this.isUserAllowedTenant();
this.userAllowedTenantCacheService.put(new UserAllowedTenantCacheService.UserAllowedTenantCacheValue(this.userScope.getUserId(), this.tenantScope.getTenant(), isUserAllowedTenant)); this.userAllowedTenantCacheService.put(new UserAllowedTenantCacheService.UserAllowedTenantCacheValue(this.userScope.getUserId(), this.tenantScope.getTenant(), isUserAllowedTenant));
} }
}
if (isUserAllowedTenant) { if (isUserAllowedTenant) {
if(!tenantScope.isDefaultTenant()) {
this.entityManager this.entityManager
.unwrap(Session.class) .unwrap(Session.class)
.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); .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 { } else {
if (isAllowedNoTenant || this.isWhiteListedEndpoint(request)) { if (isAllowedNoTenant || this.isWhiteListedEndpoint(request)) {
tenantScope.setTenant(null, null); tenantScope.setTenant(null, null);
} else { } else {
logger.warn("tenant not allowed {}", this.tenantScope.getTenant()); 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 { } else {
if (!isAllowedNoTenant) { if (!isAllowedNoTenant) {
if (!this.isWhiteListedEndpoint(request)) { if (!this.isWhiteListedEndpoint(request)) {
logger.warn("tenant scope not provided"); 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; return false;
} }
private boolean isUserAllowedTenant() throws InvalidApplicationException { private boolean isUserAllowedTenant() throws InvalidApplicationException, InterruptedException {
if (userScope.isSet()) { if (userScope.isSet()) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<UserEntity> query = criteriaBuilder.createQuery(UserEntity.class);
Root<UserEntity> root = query.from(UserEntity.class); Root<UserEntity> root = query.from(UserEntity.class);
Subquery<TenantUserEntity> subQuery = query.subquery(TenantUserEntity.class);
Root<TenantUserEntity> subQueryRoot = subQuery.from(TenantUserEntity.class);
query.where(criteriaBuilder.and( query.where(criteriaBuilder.and(
criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active), criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active),
criteriaBuilder.in(root.get(UserEntity._id)).value(subQuery.where( criteriaBuilder.in(root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class)
criteriaBuilder.and( .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._tenantId), this.tenantScope.getTenant()),
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()), criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()),
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active) criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active)
)).select(subQueryRoot.get(TenantUserEntity._userId)).distinct(true) );
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
}
)
))
) )
)); ));
query.multiselect(root.get(UserEntity._id).alias(UserEntity._id)); query.multiselect(root.get(UserEntity._id).alias(UserEntity._id));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<UserEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() > 0) return true; return !results.isEmpty();
} }
return false; return false;

View File

@ -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.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; 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.enums.IsActive;
import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.common.scope.tenant.TenantScope;
import gr.cite.notification.convention.ConventionService; 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.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.logging.LoggerService; 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.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -22,12 +28,6 @@ import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.context.request.WebRequestInterceptor;
import javax.management.InvalidApplicationException; 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.List;
import java.util.UUID; import java.util.UUID;
@ -68,7 +68,7 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
this.claimExtractorContext = claimExtractorContext; this.claimExtractorContext = claimExtractorContext;
this.tenantByCodeCacheService = tenantByCodeCacheService; this.tenantByCodeCacheService = tenantByCodeCacheService;
this.tenantByIdCacheService = tenantByIdCacheService; this.tenantByIdCacheService = tenantByIdCacheService;
this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + TenantScope.TenantClaimName; this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + ClaimNames.TenantClaimName;
} }
@Override @Override
@ -78,18 +78,26 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();
if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) { if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) {
Boolean scoped = this.scopeByPrincipal(this.tenantScope, principal); boolean scoped = this.scopeByPrincipal(principal);
if (!scoped) scoped = this.scopeByClient(this.tenantScope, 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()); 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) { private boolean scopeByPrincipal(MyPrincipal principal) {
String tenantCode = this.claimExtractor.tenantString(principal); String tenantCode = this.claimExtractor.tenantString(principal);
if (tenantCode == null || tenantCode.isBlank()) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); if (this.conventionService.isNullOrEmpty(tenantCode)) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName);
if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return false;
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); UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode);
if (tenantId == null && tenantCode == null) return false;
if (tenantId == null) { if (tenantId == null) {
TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode));
if (cacheValue != null) { if (cacheValue != null) {
@ -112,26 +120,27 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
} }
} }
if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { if (tenantId != null) {
logger.debug("parsed tenant header and set tenant id to {}", tenantId); logger.debug("parsed tenant header and set tenant id to {}", tenantId);
this.tenantScope.setTenant(tenantId, tenantCode); this.tenantScope.setTenant(tenantId, tenantCode);
this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
return true;
} }
return tenantId != null; return false;
} }
private Boolean scopeByClient(TenantScope scope, MyPrincipal principal) throws InvalidApplicationException { private boolean scopeByClient(MyPrincipal principal) throws InvalidApplicationException {
String client = this.claimExtractor.client(principal); String client = this.claimExtractor.client(principal);
Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client); 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)); 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) { private UUID getTenantIdFromDatabase(String tenantCode) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -139,27 +148,16 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getId();
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; return null;
} }
private String getTenantCodeFromDatabase(UUID tenantId) { private String getTenantCodeFromDatabase(UUID tenantId) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -167,20 +165,9 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getCode();
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; return null;
} }

View File

@ -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.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; 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.enums.IsActive;
import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.common.scope.tenant.TenantScope;
import gr.cite.notification.convention.ConventionService; import gr.cite.notification.convention.ConventionService;
import gr.cite.notification.data.TenantEntity; import gr.cite.notification.data.TenantEntity;
import gr.cite.tools.logging.LoggerService; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull; 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.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor; 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.List;
import java.util.UUID; import java.util.UUID;
@ -56,16 +56,22 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
} }
@Override @Override
public void preHandle(WebRequest request) throws InvalidApplicationException { public void preHandle(@NotNull WebRequest request) {
if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return;
if (!this.tenantScope.isMultitenant()) return; if (!this.tenantScope.isMultitenant()) return;
String tenantCode = request.getHeader(TenantScope.TenantClaimName); String tenantCode = request.getHeader(ClaimNames.TenantClaimName);
logger.debug("retrieved request tenant header is: {header}", tenantCode); logger.debug("retrieved request tenant header is: {}", tenantCode);
if (this.conventionService.isNullOrEmpty(tenantCode)) return; 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); UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode);
if (tenantId == null && tenantCode == null) return;
if (tenantId == null) { if (tenantId == null) {
TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode));
if (cacheValue != null) { if (cacheValue != null) {
@ -86,8 +92,8 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
} }
} }
if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { if (tenantId != null) {
logger.debug("parsed tenant header and set tenant id to {tenant}", tenantId); logger.debug("parsed tenant header and set tenant id to {}", tenantId);
this.tenantScope.setTenant(tenantId, tenantCode); this.tenantScope.setTenant(tenantId, tenantCode);
this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
} }
@ -95,7 +101,7 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
private UUID getTenantIdFromDatabase(String tenantCode) { private UUID getTenantIdFromDatabase(String tenantCode) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -103,27 +109,16 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getId();
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 null; return null;
} }
private String getTenantCodeFromDatabase(UUID tenantId) { private String getTenantCodeFromDatabase(UUID tenantId) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -131,20 +126,9 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getCode();
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 null; return null;
} }

View File

@ -40,4 +40,5 @@ public class TenantScopeProperties {
public void setEnforceTrustedTenant(Boolean enforceTrustedTenant) { public void setEnforceTrustedTenant(Boolean enforceTrustedTenant) {
this.enforceTrustedTenant = enforceTrustedTenant; this.enforceTrustedTenant = enforceTrustedTenant;
} }
} }

View File

@ -8,3 +8,5 @@ import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "cache.user-allowed-tenant") @ConfigurationProperties(prefix = "cache.user-allowed-tenant")
public class UserAllowedTenantCacheOptions extends CacheOptions { public class UserAllowedTenantCacheOptions extends CacheOptions {
} }

View File

@ -82,9 +82,10 @@ public class UserAllowedTenantCacheService extends CacheService<UserAllowedTenan
} }
public String buildKey(UUID userId, UUID tenantId) { public String buildKey(UUID userId, UUID tenantId) {
return this.generateKey(new HashMap<>() {{ HashMap<String, String> keyParts = new HashMap<>();
put("$user_id$", userId.toString().toLowerCase(Locale.ROOT)); keyParts.put("$user_id$", userId.toString().toLowerCase(Locale.ROOT));
put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT)); keyParts.put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT));
}}); return this.generateKey(keyParts);
} }
} }

View File

@ -1,154 +1,81 @@
//package gr.cite.notification.web.scope.user; package gr.cite.notification.web.scope.user;
//
//
//import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
//import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; 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.common.scope.user.UserScope; import gr.cite.notification.data.UserCredentialEntity;
//import gr.cite.notification.data.UserEntity; import gr.cite.notification.model.UserCredential;
//import gr.cite.notification.locale.LocaleService; import gr.cite.notification.query.UserCredentialQuery;
//import gr.cite.tools.logging.LoggerService; import gr.cite.tools.data.query.QueryFactory;
//import org.slf4j.LoggerFactory; import gr.cite.tools.exception.MyForbiddenException;
//import org.springframework.beans.factory.annotation.Autowired; import gr.cite.tools.fieldset.BaseFieldSet;
//import org.springframework.lang.NonNull; import org.jetbrains.annotations.NotNull;
//import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.transaction.PlatformTransactionManager; import org.springframework.lang.NonNull;
//import org.springframework.transaction.TransactionDefinition; import org.springframework.stereotype.Component;
//import org.springframework.transaction.TransactionStatus; import org.springframework.ui.ModelMap;
//import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.web.context.request.WebRequest;
//import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequestInterceptor;
//import org.springframework.web.context.request.WebRequest;
//import org.springframework.web.context.request.WebRequestInterceptor; import java.util.UUID;
//
//import javax.management.InvalidApplicationException; @Component
//import jakarta.persistence.EntityManager; public class UserInterceptor implements WebRequestInterceptor {
//import jakarta.persistence.PersistenceContext; private final UserScope userScope;
//import jakarta.persistence.Tuple; private final ClaimExtractor claimExtractor;
//import jakarta.persistence.criteria.CriteriaBuilder; private final CurrentPrincipalResolver currentPrincipalResolver;
//import jakarta.persistence.criteria.CriteriaQuery; private final UserInterceptorCacheService userInterceptorCacheService;
//import jakarta.persistence.criteria.Root; private final QueryFactory queryFactory;
//import java.time.Instant;
//import java.util.List; @Autowired
//import java.util.UUID; public UserInterceptor(
// UserScope userScope,
//@Component ClaimExtractor claimExtractor,
//public class UserInterceptor implements WebRequestInterceptor { CurrentPrincipalResolver currentPrincipalResolver,
// private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class)); UserInterceptorCacheService userInterceptorCacheService,
// private final UserScope userScope; QueryFactory queryFactory) {
// private final ClaimExtractor claimExtractor; this.userScope = userScope;
// private final CurrentPrincipalResolver currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
// private final LocaleService localeService; this.claimExtractor = claimExtractor;
// private final PlatformTransactionManager transactionManager; this.userInterceptorCacheService = userInterceptorCacheService;
// private final UserInterceptorCacheService userInterceptorCacheService; this.queryFactory = queryFactory;
// @PersistenceContext }
// public EntityManager entityManager;
// @Override
// @Autowired public void preHandle(@NotNull WebRequest request) {
// public UserInterceptor( UUID userId = null;
// UserScope userScope, if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) {
// LocaleService localeService, String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());
// ClaimExtractor claimExtractor, if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed");
// CurrentPrincipalResolver currentPrincipalResolver,
// PlatformTransactionManager transactionManager, UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId));
// UserInterceptorCacheService userInterceptorCacheService if (cacheValue != null) {
// ) { userId = cacheValue.getUserId();
// this.userScope = userScope; } else {
// this.localeService = localeService; userId = this.findExistingUserFromDb(subjectId);
// this.currentPrincipalResolver = currentPrincipalResolver; if (userId != null) {
// this.claimExtractor = claimExtractor; cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId);
// this.transactionManager = transactionManager; this.userInterceptorCacheService.put(cacheValue);
// this.userInterceptorCacheService = userInterceptorCacheService; }
// } }
// }
// @Override this.userScope.setUserId(userId);
// public void preHandle(WebRequest request) throws InvalidApplicationException { }
// UUID userId = null; private UUID findExistingUserFromDb(String subjectId) {
// if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user));
// String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); if (userCredential != null) {
// return userCredential.getUserId();
// UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); }
// if (cacheValue != null) { return null;
// userId = cacheValue.getUserId(); }
// } else { @Override
// userId = this.getUserIdFromDatabase(subjectId); public void postHandle(@NonNull WebRequest request, ModelMap model) {
// if (userId == null) userId = this.createUser(subjectId); this.userScope.setUserId(null);
// }
// this.userInterceptorCacheService.put(new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId));
// } @Override
// } public void afterCompletion(@NonNull WebRequest request, Exception ex) {
// this.userScope.setUserId(userId); }
// } }
//
// private UUID getUserIdFromDatabase(String subjectId) throws InvalidApplicationException {
// CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
// CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class);
// Root<UserEntity> 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<Tuple> 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) {
// }
//}

View File

@ -1,10 +1,10 @@
//package gr.cite.notification.web.scope.user; package gr.cite.notification.web.scope.user;
//
//import gr.cite.tools.cache.CacheOptions; import gr.cite.tools.cache.CacheOptions;
//import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
//
//@Configuration @Configuration
//@ConfigurationProperties(prefix = "cache.user-by-subject-id") @ConfigurationProperties(prefix = "cache.user-by-subject-id")
//public class UserInterceptorCacheOptions extends CacheOptions { public class UserInterceptorCacheOptions extends CacheOptions {
//} }

View File

@ -1,77 +1,67 @@
//package gr.cite.notification.web.scope.user; package gr.cite.notification.web.scope.user;
//
//import gr.cite.notification.convention.ConventionService; import gr.cite.notification.convention.ConventionService;
//import gr.cite.notification.event.UserTouchedEvent; import gr.cite.tools.cache.CacheService;
//import gr.cite.tools.cache.CacheService; import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;
//import org.springframework.context.event.EventListener;
//import org.springframework.stereotype.Service; import java.util.HashMap;
// import java.util.UUID;
//import java.util.HashMap;
//import java.util.UUID; @Service
// public class UserInterceptorCacheService extends CacheService<UserInterceptorCacheService.UserInterceptorCacheValue> {
//@Service
//public class UserInterceptorCacheService extends CacheService<UserInterceptorCacheService.UserInterceptorCacheValue> { public static class UserInterceptorCacheValue {
//
// public static class UserInterceptorCacheValue { public UserInterceptorCacheValue() {
// }
// public UserInterceptorCacheValue() {
// } public UserInterceptorCacheValue(String subjectId, UUID userId) {
// this.subjectId = subjectId;
// public UserInterceptorCacheValue(String subjectId, UUID userId) { this.userId = userId;
// this.subjectId = subjectId; }
// this.userId = userId;
// }
// public String getSubjectId() {
// private String subjectId; return subjectId;
// }
// public String getSubjectId() {
// return subjectId; public void setSubjectId(String subjectId) {
// } this.subjectId = subjectId;
// }
// public void setSubjectId(String subjectId) {
// this.subjectId = subjectId; private String subjectId;
// } 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; }
// }
// }
// @Autowired
// private final ConventionService conventionService; public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) {
// super(options);
// @Autowired }
// public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) {
// super(options); @Override
// this.conventionService = conventionService; protected Class<UserInterceptorCacheValue> valueClass() {
// } return UserInterceptorCacheValue.class;
// }
// @EventListener
// public void handleUserTouchedEvent(UserTouchedEvent event) { @Override
// if (!this.conventionService.isNullOrEmpty(event.getSubjectId())) this.evict(this.buildKey(event.getSubjectId())); public String keyOf(UserInterceptorCacheValue value) {
// if (!this.conventionService.isNullOrEmpty(event.getPreviousSubjectId())) this.evict(this.buildKey(event.getPreviousSubjectId())); return this.buildKey(value.getSubjectId());
// } }
//
// @Override
// protected Class<UserInterceptorCacheValue> valueClass() { public String buildKey(String subject) {
// return UserInterceptorCacheValue.class; HashMap<String, String> keyParts = new HashMap<>();
// } keyParts.put("$subject$", subject);
// return this.generateKey(keyParts);
// @Override }
// public String keyOf(UserInterceptorCacheValue value) { }
// return this.buildKey(value.getSubjectId());
// }
//
//
// public String buildKey(String subject) {
// return this.generateKey(new HashMap<>() {{
// put("$subject$", subject);
// }});
// }
//}

View File

@ -26,8 +26,11 @@ error-thesaurus:
non-person-principal: non-person-principal:
code: 108 code: 108
message: the operation is available only to person users message: the operation is available only to person users
blocking-consent: tenant-not-allowed:
code: 113 code: 113
message: tenant not allowed
blocking-consent:
code: 114
message: user consents are not sufficient to complete the operation message: user consents are not sufficient to complete the operation
single-tenant-configuration-per-type-supported: single-tenant-configuration-per-type-supported:
code: 116 code: 116
@ -41,3 +44,6 @@ error-thesaurus:
overlapping-tenant-configuration-notifier-list: overlapping-tenant-configuration-notifier-list:
code: 119 code: 119
message: Overlapping Tenant Configuration Notifier List message: Overlapping Tenant Configuration Notifier List
tenant-tampering:
code: 123
message: Tenant tampering

View File

@ -20,11 +20,25 @@ idpclient:
Roles: Roles:
- type: resource_access - type: resource_access
path: dmp_web.roles 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: Scope:
- type: scope - type: scope
AccessToken: AccessToken:
- type: x-access-token - type: x-access-token
visibility: SENSITIVE visibility: SENSITIVE
Tenant:
- type: x-tenant
IssuedAt: IssuedAt:
- type: iat - type: iat
Issuer: Issuer:
@ -37,5 +51,8 @@ idpclient:
- type: azp - type: azp
Authorities: Authorities:
- type: authorities - type: authorities
ExternalProviderName: TenantCodes:
- type: identity_provider - type: tenant_roles
filterBy: "(.*):(.*)"
extractByExpression: "(.*):(.*)"
extractExpressionValue: "[[g2]]"

View File

@ -12,14 +12,14 @@ permissions:
EditTenant: EditTenant:
roles: roles:
- Admin - Admin
clients: [ ] clients: [ "opendmp-api-dev" ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteTenant: DeleteTenant:
roles: roles:
- Admin - Admin
claims: [ ] claims: [ ]
clients: [ ] clients: [ "opendmp-api-dev" ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
AllowNoTenant: AllowNoTenant:
@ -32,85 +32,85 @@ permissions:
# Users # Users
BrowseUser: BrowseUser:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: true allowAnonymous: true
allowAuthenticated: false allowAuthenticated: false
EditUser: EditUser:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ "opendmp-api-dev" ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteUser: DeleteUser:
roles: roles:
- Admin - TenantAdmin
claims: [ ] claims: [ ]
clients: [ ] clients: [ "opendmp-api-dev" ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
# UserContactInfo # UserContactInfo
BrowseUserContactInfo: BrowseUserContactInfo:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ "opendmp-api-dev" ]
allowAnonymous: true allowAnonymous: true
allowAuthenticated: false allowAuthenticated: false
EditUserContactInfo: EditUserContactInfo:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteUserContactInfo: DeleteUserContactInfo:
roles: roles:
- Admin - TenantAdmin
claims: [ ] claims: [ ]
clients: [ ] clients: [ "opendmp-api-dev" ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
#Notification #Notification
BrowseNotification: BrowseNotification:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: true allowAnonymous: true
allowAuthenticated: false allowAuthenticated: false
EditNotification: EditNotification:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: true allowAnonymous: true
allowAuthenticated: false allowAuthenticated: false
DeleteNotification: DeleteNotification:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
#Tenant Configuration #Tenant Configuration
BrowseTenantConfiguration: BrowseTenantConfiguration:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
EditTenantConfiguration: EditTenantConfiguration:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
#User Notification Preference #User Notification Preference
BrowseUserNotificationPreference: BrowseUserNotificationPreference:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: true allowAnonymous: true
allowAuthenticated: false allowAuthenticated: false
EditUserNotificationPreference: EditUserNotificationPreference:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
@ -118,25 +118,25 @@ permissions:
# ViewPage Permissions # ViewPage Permissions
ViewNotificationPage: ViewNotificationPage:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
ViewNotificationEventRulePage: ViewNotificationEventRulePage:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
ViewInAppNotificationPage: ViewInAppNotificationPage:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
ViewNotificationTemplatePage: ViewNotificationTemplatePage:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
@ -144,19 +144,19 @@ permissions:
# Notification Template Permissions # Notification Template Permissions
BrowseNotificationTemplate: BrowseNotificationTemplate:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
EditNotificationTemplate: EditNotificationTemplate:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteNotificationTemplate: DeleteNotificationTemplate:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
@ -164,13 +164,13 @@ permissions:
# In App Notification Permissions # In App Notification Permissions
BrowseInAppNotification: BrowseInAppNotification:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteInAppNotification: DeleteInAppNotification:
roles: roles:
- Admin - TenantAdmin
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false

View File

@ -25,9 +25,6 @@ queue:
enable: false enable: false
options: options:
exchange: null exchange: null
forget-me-completed-topic: forgetme.completed
what-you-know-about-me-completed-topic: whatyouknowaboutme.completed
generate-file-topic: generate.file
rabbitmq: rabbitmq:
enable: false enable: false
interval-seconds: 30 interval-seconds: 30

View File

@ -0,0 +1,7 @@
tenant:
multitenancy:
is-multitenant: true
default-tenant-code: default
interceptor:
client-claims-prefix: client_
enforce-trusted-tenant: false

View File

@ -2,7 +2,6 @@ tenant:
multitenancy: multitenancy:
is-multitenant: false is-multitenant: false
interceptor: interceptor:
client-claims-prefix: client_
white-listed-clients: [ ] white-listed-clients: [ ]
enforce-trusted-tenant: false 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' ]

View File

@ -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";
}

View File

@ -5,6 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "tenant.multitenancy") @ConfigurationProperties(prefix = "tenant.multitenancy")
public class MultitenancyProperties { public class MultitenancyProperties {
private boolean isMultitenant; private boolean isMultitenant;
private String defaultTenantCode;
public boolean isMultitenant() { public boolean isMultitenant() {
return isMultitenant; return isMultitenant;
@ -13,4 +14,16 @@ public class MultitenancyProperties {
public void setIsMultitenant(boolean multitenant) { public void setIsMultitenant(boolean multitenant) {
isMultitenant = multitenant; isMultitenant = multitenant;
} }
public void setMultitenant(boolean multitenant) {
isMultitenant = multitenant;
}
public String getDefaultTenantCode() {
return defaultTenantCode;
}
public void setDefaultTenantCode(String defaultTenantCode) {
this.defaultTenantCode = defaultTenantCode;
}
} }

View File

@ -1,34 +1,25 @@
package gr.cite.notification.common.scope.tenant; package gr.cite.notification.common.scope.tenant;
import gr.cite.notification.data.tenant.TenantScopedBaseEntity; import gr.cite.notification.data.tenant.TenantScopedBaseEntity;
import gr.cite.tools.logging.LoggerService; import jakarta.persistence.EntityManager;
import org.hibernate.Session; import org.hibernate.Session;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; 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.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope; import org.springframework.web.context.annotation.RequestScope;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import jakarta.persistence.EntityManager;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
@Component @Component
@RequestScope @RequestScope
public class TenantScope { public class TenantScope {
public static final String TenantReplaceParameter = "::TenantCode::"; public static final String TenantReplaceParameter = "::TenantCode::";
public static final String TenantCodesClaimName = "TenantCodes"; private final MultitenancyProperties multitenancy;
public static final String TenantClaimName = "x-tenant"; private final AtomicReference<UUID> tenant = new AtomicReference<>();
private final AtomicReference<String> tenantCode = new AtomicReference<>();
private MultitenancyProperties multitenancy; private final AtomicReference<UUID> initialTenant = new AtomicReference<>();
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScope.class)); private final AtomicReference<String> initialTenantCode = new AtomicReference<>();
private UUID tenant = null;
private String tenantCode = null;
private UUID initialTenant = null;
private String initialTenantCode = null;
@Autowired @Autowired
public TenantScope(MultitenancyProperties multitenancy) { public TenantScope(MultitenancyProperties multitenancy) {
@ -39,49 +30,79 @@ public class TenantScope {
return multitenancy.isMultitenant(); return multitenancy.isMultitenant();
} }
public String getDefaultTenantCode() {
return multitenancy.getDefaultTenantCode();
}
public Boolean isSet() { public Boolean isSet() {
if (!this.isMultitenant()) return true; if (!this.isMultitenant())
return this.tenant != null; 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 { public UUID getTenant() throws InvalidApplicationException {
if (!this.isMultitenant()) return null; if (!this.isMultitenant())
if (this.tenant == null) throw new InvalidApplicationException("tenant not set"); return null;
return this.tenant; 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 { public String getTenantCode() throws InvalidApplicationException {
if (!this.isMultitenant()) return null; if (!this.isMultitenant())
if (this.tenant == null) throw new InvalidApplicationException("tenant not set"); return null;
return this.tenantCode; if (this.tenantCode.get() == null)
throw new InvalidApplicationException("tenant not set");
return this.tenantCode.get();
} }
public void setTempTenant(EntityManager entityManager, UUID tenant, String tenantCode) { public void setTempTenant(EntityManager entityManager, UUID tenant, String tenantCode) {
this.tenant = tenant; this.tenant.set(tenant);
this.tenantCode.set(tenantCode);
if(this.tenant != null) { if (this.tenant.get() != null && !this.isDefaultTenant()) {
if(!this.isDefaultTenant()) {
entityManager entityManager
.unwrap(Session.class) .unwrap(Session.class)
.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.tenant.toString()); .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) { public void removeTempTenant(EntityManager entityManager) {
this.tenant = this.initialTenant; this.tenant.set(this.initialTenant.get());
this.tenantCode = this.initialTenantCode; this.tenantCode.set(this.initialTenantCode.get());
if(this.initialTenant != null) { if (this.initialTenant.get() != null && !this.isDefaultTenant()) {
if(!this.isDefaultTenant()) {
entityManager entityManager
.unwrap(Session.class) .unwrap(Session.class)
.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.initialTenant.toString()); .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) { public void setTenant(UUID tenant, String tenantCode) {
if (this.isMultitenant()) { if (this.isMultitenant()) {
this.tenant = tenant; this.tenant.set(tenant);
this.initialTenant = tenant; this.initialTenant.set(tenant);
this.tenantCode = tenantCode; this.tenantCode.set(tenantCode);
this.initialTenantCode = tenantCode; this.initialTenantCode.set(tenantCode);
} }
} }
} }

View File

@ -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;
}
}

View File

@ -39,10 +39,10 @@ public class NotificationTemplateEntity extends TenantScopedBaseEntity {
public static final String _kind = "kind"; public static final String _kind = "kind";
@Column(name = "\"language\"", columnDefinition = "uuid", nullable = false) @Column(name = "\"language_code\"", nullable = false, length = 200)
private UUID languageId; private String languageCode;
public static final String _languageId = "languageId"; public static final String _languageCode = "languageCode";
@Column(name = "\"value\"", nullable = false) @Column(name = "\"value\"", nullable = false)
private String value; private String value;
@ -97,12 +97,12 @@ public class NotificationTemplateEntity extends TenantScopedBaseEntity {
this.kind = kind; this.kind = kind;
} }
public UUID getLanguageId() { public String getLanguageCode() {
return languageId; return languageCode;
} }
public void setLanguageId(UUID languageId) { public void setLanguageCode(String languageCode) {
this.languageId = languageId; this.languageCode = languageCode;
} }
public String getValue() { public String getValue() {

View File

@ -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> 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> T find(Class<T> 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;
}
}

View File

@ -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> 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> T find(Class<T> 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();
}
}

View File

@ -1,48 +1,43 @@
package gr.cite.notification.data.tenant; 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 gr.cite.notification.common.scope.tenant.TenantScope;
import jakarta.persistence.EntityManager;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.hibernate.Session; import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import jakarta.persistence.EntityManager;
@Aspect @Aspect
@Component @Component
public class TenantFilterAspect { public class TenantFilterAspect {
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final ClaimExtractor claimExtractor;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final ApplicationContext applicationContext;
@Autowired @Autowired
public TenantFilterAspect( public TenantFilterAspect(
TenantScope tenantScope, TenantScope tenantScope
ClaimExtractor claimExtractor,
CurrentPrincipalResolver currentPrincipalResolver,
ApplicationContext applicationContext
) { ) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor;
this.applicationContext = applicationContext;
} }
@AfterReturning( @AfterReturning(
pointcut = "bean(entityManagerFactory) && execution(* createEntityManager(..))", pointcut = "bean(entityManagerFactory) && execution(* createEntityManager(..))",
returning = "retVal") returning = "retVal")
public void getSessionAfter(JoinPoint joinPoint, Object retVal) throws InvalidApplicationException { 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 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);
}
} }
} }

View File

@ -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.TenantScope;
import gr.cite.notification.common.scope.tenant.TenantScoped; import gr.cite.notification.common.scope.tenant.TenantScoped;
import gr.cite.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreRemove;
import jakarta.persistence.PreUpdate;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreRemove;
import jakarta.persistence.PreUpdate;
import java.util.UUID; import java.util.UUID;
public class TenantListener { public class TenantListener {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantListener.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantListener.class));
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final ErrorThesaurusProperties errors;
@Autowired @Autowired
public TenantListener( public TenantListener(
TenantScope tenantScope TenantScope tenantScope, ErrorThesaurusProperties errors
) { ) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.errors = errors;
} }
@PrePersist @PrePersist
public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant()) { if (tenantScope.isMultitenant()) {
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(); final UUID tenantId = tenantScope.getTenant();
entity.setTenantId(tenantId); entity.setTenantId(tenantId);
}
} else { } else {
entity.setTenantId(null); entity.setTenantId(null);
} }
@ -38,22 +49,30 @@ public class TenantListener {
@PreRemove @PreRemove
public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant()) { if (tenantScope.isMultitenant()) {
if (!tenantScope.isDefaultTenant()) {
if (entity.getTenantId() == null) { if (entity.getTenantId() == null) {
logger.error("somebody tried to set null tenant"); logger.error("somebody tried to set null tenant");
throw new MyForbiddenException("tenant tampering"); throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
} }
if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) {
logger.error("somebody tried to change an entries tenant"); logger.error("somebody tried to change an entries tenant");
throw new MyForbiddenException("tenant tampering"); throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
} }
final UUID tenantId = tenantScope.getTenant(); final UUID tenantId = tenantScope.getTenant();
entity.setTenantId(tenantId); entity.setTenantId(tenantId);
} else { } else {
if (entity.getTenantId() != null) { if (entity.getTenantId() != null) {
logger.error("somebody tried to set non null tenant"); logger.error("somebody tried to set null tenant");
throw new MyForbiddenException("tenant tampering"); throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
} }
} }
} else {
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());
}
}
} }
} }

View File

@ -1,15 +1,13 @@
package gr.cite.notification.data.tenant; package gr.cite.notification.data.tenant;
import gr.cite.notification.common.scope.tenant.TenantScoped; 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.Filter;
import org.hibernate.annotations.FilterDef; import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.ParamDef; 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.io.Serializable;
import java.util.UUID; import java.util.UUID;
@ -17,23 +15,20 @@ import java.util.UUID;
//@Getter //@Getter
//@Setter //@Setter
//@NoArgsConstructor //@NoArgsConstructor
@FilterDef(name = TenantScopedBaseEntity.tenantFilter, parameters = {@ParamDef(name = TenantScopedBaseEntity.tenantFilterTenantParam, type = String.class)}) @FilterDef(name = TenantScopedBaseEntity.TENANT_FILTER, parameters = {@ParamDef(name = TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, type = String.class)})
@Filter(name = "tenantFilter", condition = "tenant = (cast(:tenantId as uuid))") @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) @EntityListeners(TenantListener.class)
public abstract class TenantScopedBaseEntity implements TenantScoped, Serializable { public abstract class TenantScopedBaseEntity implements TenantScoped, Serializable {
@Serial
private static final long serialVersionUID = 1L; 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"; @Column(name = "tenant", columnDefinition = "uuid", nullable = true)
public static final String tenantFilterTenantParam = "tenantId";
@Column(name = "tenant", columnDefinition = "uuid")
private UUID tenantId; private UUID tenantId;
public static final String _tenantId = "tenantId"; public static final String _tenantId = "tenantId";
public UUID getTenantId() { public UUID getTenantId() {
return tenantId; return tenantId;
} }
@ -42,5 +37,5 @@ public abstract class TenantScopedBaseEntity implements TenantScoped, Serializab
public void setTenantId(UUID tenantId) { public void setTenantId(UUID tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
} }

View File

@ -15,6 +15,8 @@ public class ErrorThesaurusProperties {
private ErrorDescription singleTenantConfigurationPerTypeSupported; private ErrorDescription singleTenantConfigurationPerTypeSupported;
private ErrorDescription incompatibleTenantConfigurationTypes; private ErrorDescription incompatibleTenantConfigurationTypes;
private ErrorDescription overlappingTenantConfigurationNotifierList; private ErrorDescription overlappingTenantConfigurationNotifierList;
private ErrorDescription tenantNotAllowed;
private ErrorDescription tenantTampering;
public ErrorDescription getHashConflict() { public ErrorDescription getHashConflict() {
return hashConflict; return hashConflict;
@ -89,4 +91,19 @@ public class ErrorThesaurusProperties {
this.overlappingTenantConfigurationNotifierList = overlappingTenantConfigurationNotifierList; 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;
}
} }

View File

@ -1,5 +1,6 @@
package gr.cite.notification.integrationevent; package gr.cite.notification.integrationevent;
import gr.cite.notification.data.QueueOutboxEntity;
import gr.cite.notification.integrationevent.outbox.OutboxProperties; import gr.cite.notification.integrationevent.outbox.OutboxProperties;
import gr.cite.notification.integrationevent.outbox.OutboxRepositoryImpl; import gr.cite.notification.integrationevent.outbox.OutboxRepositoryImpl;
import gr.cite.queueoutbox.IntegrationEventContextCreator; import gr.cite.queueoutbox.IntegrationEventContextCreator;
@ -56,7 +57,11 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
@Bean @Bean
public IntegrationEventContextCreator integrationEventContextCreator() { 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 @Bean

View File

@ -1,6 +1,7 @@
package gr.cite.notification.integrationevent.inbox; package gr.cite.notification.integrationevent.inbox;
import gr.cite.commons.web.oidc.principal.MyPrincipal; 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.core.ClaimAccessor;
import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.security.oauth2.jwt.JwtClaimNames;
@ -22,12 +23,15 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
this.isAuthenticated = isAuthenticated; this.isAuthenticated = isAuthenticated;
} }
public static InboxPrincipal build(IntegrationEventProperties properties) { public static InboxPrincipal build(IntegrationEventProperties properties, ClaimExtractorProperties claimExtractorProperties) {
InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId"); InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId");
inboxPrincipal.put("client_id", properties.getAppId()); List<ClaimExtractorProperties.KeyPath> 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("active", "true");
inboxPrincipal.put("nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString()); List<ClaimExtractorProperties.KeyPath> notBeforeKey = claimExtractorProperties.getMapping().getOrDefault("NotBefore", null);
inboxPrincipal.put("exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString()); inboxPrincipal.put(notBeforeKey != null && notBeforeKey.getFirst() != null ? notBeforeKey.getFirst().getType() :"nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString());
List<ClaimExtractorProperties.KeyPath> 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; return inboxPrincipal;
} }
@ -45,7 +49,10 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
public List<String> getClaimAsStringList(String claim) { public List<String> getClaimAsStringList(String claim) {
if (claims == null) if (claims == null)
return null; return null;
return this.getClaimAsStringList(claim); if (this.claims.containsKey(claim)){
return List.of(this.claims.get(claim).toString());
}
return null;
} }
@Override @Override

View File

@ -4,6 +4,7 @@ import gr.cite.notification.common.JsonHandlingService;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.common.scope.fake.FakeRequestScope; import gr.cite.notification.common.scope.fake.FakeRequestScope;
import gr.cite.notification.data.QueueInboxEntity; import gr.cite.notification.data.QueueInboxEntity;
import gr.cite.notification.data.TenantEntityManager;
import gr.cite.notification.integrationevent.TrackedEvent; import gr.cite.notification.integrationevent.TrackedEvent;
import gr.cite.notification.integrationevent.inbox.notify.NotifyIntegrationEventHandler; import gr.cite.notification.integrationevent.inbox.notify.NotifyIntegrationEventHandler;
import gr.cite.notification.integrationevent.inbox.tenantremoval.TenantRemovalIntegrationEventHandler; 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.CandidateInfo;
import gr.cite.queueinbox.repository.InboxRepository; import gr.cite.queueinbox.repository.InboxRepository;
import gr.cite.queueinbox.task.MessageOptions; import gr.cite.queueinbox.task.MessageOptions;
import gr.cite.rabbitmq.IntegrationEventMessageConstants;
import gr.cite.rabbitmq.consumer.InboxCreatorParams; import gr.cite.rabbitmq.consumer.InboxCreatorParams;
import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -241,7 +243,14 @@ public class InboxRepositoryImpl implements InboxRepository {
QueueInboxEntity queueMessage = new QueueInboxEntity(); QueueInboxEntity queueMessage = new QueueInboxEntity();
queueMessage.setId(UUID.randomUUID()); 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.setExchange(this.inboxProperties.getExchange());
queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey());
queueMessage.setQueue(inboxCreatorParams.getQueueName()); queueMessage.setQueue(inboxCreatorParams.getQueueName());
@ -269,6 +278,10 @@ public class InboxRepositoryImpl implements InboxRepository {
entityManager = entityManagerFactory.createEntityManager(); entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction(); transaction = entityManager.getTransaction();
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
transaction.begin(); transaction.begin();
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); 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()); logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId());
} else { } else {
EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); EventProcessingStatus status = this.processMessage(queueInboxMessage);
switch (status) { switch (status) {
case Success: { case Success: {
queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL);
@ -321,21 +334,21 @@ public class InboxRepositoryImpl implements InboxRepository {
return success; return success;
} }
private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) {
IntegrationEventHandler handler; IntegrationEventHandler handler;
logger.debug("Processing message with routing key '{}'", routingKey); logger.debug("Processing message with routing key '{}'", queueInboxMessage.getRoute());
if (this.routingKeyMatched(routingKey, this.inboxProperties.getNotifyTopic())) if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantRemovalTopic()))
handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic()))
handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); 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); 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); 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); handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getNotifyTopic()))
handler = this.applicationContext.getBean(NotifyIntegrationEventHandler.class);
else { 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; handler = null;
} }
@ -343,16 +356,17 @@ public class InboxRepositoryImpl implements InboxRepository {
return EventProcessingStatus.Discard; return EventProcessingStatus.Discard;
IntegrationEventProperties properties = new IntegrationEventProperties(); IntegrationEventProperties properties = new IntegrationEventProperties();
properties.setAppId(appId); properties.setAppId(queueInboxMessage.getApplicationId());
properties.setMessageId(messageId); 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)) // using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag))
// { // {
try { try {
return handler.handle(properties, message); return handler.handle(properties, queueInboxMessage.getMessage());
} catch (Exception ex) { } 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; return EventProcessingStatus.Error;
} }
// } // }

View File

@ -1,10 +1,13 @@
package gr.cite.notification.integrationevent.inbox; package gr.cite.notification.integrationevent.inbox;
import java.util.UUID;
public class IntegrationEventProperties { public class IntegrationEventProperties {
private String messageId; private String messageId;
private String appId; private String appId;
private UUID tenantId;
public String getMessageId() { public String getMessageId() {
return messageId; return messageId;
@ -22,4 +25,11 @@ public class IntegrationEventProperties {
this.appId = appId; this.appId = appId;
} }
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
} }

View File

@ -9,8 +9,6 @@ public class NotifyIntegrationEvent extends TrackedEvent {
private UUID userId; private UUID userId;
private UUID tenantId;
private UUID notificationType; private UUID notificationType;
private NotificationContactType contactTypeHint; private NotificationContactType contactTypeHint;
@ -32,14 +30,6 @@ public class NotifyIntegrationEvent extends TrackedEvent {
this.userId = userId; this.userId = userId;
} }
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
public UUID getNotificationType() { public UUID getNotificationType() {
return notificationType; return notificationType;
} }

View File

@ -1,6 +1,7 @@
package gr.cite.notification.integrationevent.inbox.notify; package gr.cite.notification.integrationevent.inbox.notify;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; 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.audit.AuditableAction;
import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.JsonHandlingService;
import gr.cite.notification.common.enums.NotificationNotifyState; 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.fake.FakeRequestScope;
import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.common.scope.tenant.TenantScope;
import gr.cite.notification.data.TenantEntity; import gr.cite.notification.data.TenantEntity;
import gr.cite.notification.data.TenantEntityManager;
import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus;
import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.InboxPrincipal;
@ -46,17 +48,30 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ApplicationContext applicationContext;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final MessageSource messageSource; private final MessageSource messageSource;
private final QueryFactory queryFactory;
public NotifyIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) { 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.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext;
this.errors = errors; this.errors = errors;
this.messageSource = messageSource; 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 @Override
@ -84,72 +99,42 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent
model.setProvenanceRef(event.getProvenanceRef()); model.setProvenanceRef(event.getProvenanceRef());
model.setNotifiedAt(Instant.now()); model.setNotifiedAt(Instant.now());
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
TenantScope scope = this.applicationContext.getBean(TenantScope.class); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
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) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(event.getTenantId(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
currentPrincipalResolver.push(InboxPrincipal.build(properties));
NotifyConsistencyHandler notifyConsistencyHandler = this.applicationContext.getBean(NotifyConsistencyHandler.class); if (!(notifyConsistencyHandler.isConsistent(new NotifyConsistencyPredicates(event.getUserId(), event.getContactTypeHint(), event.getContactHint())))) {
if (!(notifyConsistencyHandler.isConsistent(new NotifyConsistencyPredicates(event.getUserId(), event.getContactTypeHint(), event.getContactHint())))) status = EventProcessingStatus.Postponed;
return EventProcessingStatus.Postponed; currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); return status;
entityManager = entityManagerFactory.createEntityManager(); }
transaction = entityManager.getTransaction();
transaction.begin();
try {
NotificationService notificationService = this.applicationContext.getBean(NotificationService.class);
notificationService.persist(model, new BaseFieldSet()); notificationService.persist(model, new BaseFieldSet());
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.Notification_Persist, Map.ofEntries( auditService.track(AuditableAction.Notification_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", event.getUserId()) new AbstractMap.SimpleEntry<String, Object>("id", event.getUserId())
)); ));
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); } catch (Exception ex) {
status = EventProcessingStatus.Error;
transaction.commit(); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} catch (Exception e) {
transaction.rollback();
throw e;
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
} }
} catch (OptimisticLockException ex) { return status;
// 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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -1,5 +1,6 @@
package gr.cite.notification.integrationevent.inbox.tenantremoval; package gr.cite.notification.integrationevent.inbox.tenantremoval;
import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler;
public interface TenantRemovalIntegrationEventHandler extends IntegrationEventHandler { public interface TenantRemovalIntegrationEventHandler extends IntegrationEventHandler {

View File

@ -1,24 +1,18 @@
package gr.cite.notification.integrationevent.inbox.tenantremoval; package gr.cite.notification.integrationevent.inbox.tenantremoval;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; 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.audit.AuditableAction;
import gr.cite.notification.common.JsonHandlingService; import gr.cite.notification.common.JsonHandlingService;
import gr.cite.notification.common.scope.fake.FakeRequestScope; 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.EventProcessingStatus;
import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.InboxPrincipal;
import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.notification.service.tenant.TenantService; import gr.cite.notification.service.tenant.TenantService;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.logging.LoggerService; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; 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.annotation.Scope;
import org.springframework.stereotype.Component; 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 static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantRemovalIntegrationEventHandlerImpl.class));
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final ApplicationContext applicationContext; private final ClaimExtractorProperties claimExtractorProperties;
private final TenantService tenantService;
private final ErrorThesaurusProperties errors; private final AuditService auditService;
private final TenantEntityManager tenantEntityManager;
private final MessageSource messageSource; private final TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler;
public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager, TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler) {
public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) {
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext; this.currentPrincipalResolver = currentPrincipalResolver;
this.errors = errors; this.claimExtractorProperties = claimExtractorProperties;
this.messageSource = messageSource; this.tenantService = tenantService;
this.auditService = auditService;
this.tenantEntityManager = tenantEntityManager;
this.tenantRemovalConsistencyHandler = tenantRemovalConsistencyHandler;
} }
@Override @Override
@ -52,60 +48,34 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
if (event == null) if (event == null)
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler = this.applicationContext.getBean(TenantRemovalConsistencyHandler.class); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId()))))
return EventProcessingStatus.Postponed;
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) {
entityManager = entityManagerFactory.createEntityManager(); status = EventProcessingStatus.Postponed;
currentPrincipalResolver.pop();
return status;
}
transaction = entityManager.getTransaction(); tenantEntityManager.disableTenantFilters();
transaction.begin();
try {
TenantService tenantService = this.applicationContext.getBean(TenantService.class);
tenantService.deleteAndSave(event.getId()); tenantService.deleteAndSave(event.getId());
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries( auditService.track(AuditableAction.Tenant_Delete, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", event.getId()) new AbstractMap.SimpleEntry<String, Object>("id", event.getId())
)); ));
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
transaction.commit();
} catch (Exception e) { } catch (Exception ex) {
transaction.rollback(); status = EventProcessingStatus.Error;
throw e; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
} }
return status;
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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return null;
} }
} }

View File

@ -1,5 +1,6 @@
package gr.cite.notification.integrationevent.inbox.tenanttouched; package gr.cite.notification.integrationevent.inbox.tenanttouched;
import gr.cite.notification.integrationevent.TrackedEvent; import gr.cite.notification.integrationevent.TrackedEvent;
import java.util.UUID; import java.util.UUID;

View File

@ -1,9 +1,10 @@
package gr.cite.notification.integrationevent.inbox.tenanttouched; package gr.cite.notification.integrationevent.inbox.tenanttouched;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; 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.audit.AuditableAction;
import gr.cite.notification.common.JsonHandlingService; 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.EventProcessingStatus;
import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.InboxPrincipal;
import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; 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.auditing.AuditService;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; 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)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantTouchedIntegrationEventHandlerImpl.class));
protected final ApplicationContext applicationContext;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory; 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) { public TenantTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, TenantService tenantService, AuditService auditService, TenantEntityManager tenantEntityManager) {
this.applicationContext = applicationContext;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractorProperties = claimExtractorProperties;
this.tenantService = tenantService;
this.auditService = auditService;
this.tenantEntityManager = tenantEntityManager;
} }
@Override @Override
@ -53,55 +57,25 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn
model.setCode(event.getCode()); model.setCode(event.getCode());
this.validatorFactory.validator(TenantTouchedIntegrationEventPersist.TenantTouchedIntegrationEventPersistValidator.class).validateForce(model); this.validatorFactory.validator(TenantTouchedIntegrationEventPersist.TenantTouchedIntegrationEventPersistValidator.class).validateForce(model);
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null; tenantEntityManager.disableTenantFilters();
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
currentPrincipalResolver.push(InboxPrincipal.build(properties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
try {
TenantService tenantService = this.applicationContext.getBean(TenantService.class);
tenantService.persist(model, null); tenantService.persist(model, null);
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries( auditService.track(AuditableAction.Tenant_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model) new AbstractMap.SimpleEntry<String, Object>("model", model)
)); ));
transaction.commit();
} catch (Exception e) { } catch (Exception ex) {
transaction.rollback(); status = EventProcessingStatus.Error;
throw e; logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
} }
return status;
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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return null;
} }
} }

View File

@ -1,5 +1,6 @@
package gr.cite.notification.integrationevent.inbox.userremoval; package gr.cite.notification.integrationevent.inbox.userremoval;
import gr.cite.notification.integrationevent.inbox.ConsistencyPredicates; import gr.cite.notification.integrationevent.inbox.ConsistencyPredicates;
import java.util.UUID; import java.util.UUID;

View File

@ -8,7 +8,6 @@ public class UserRemovalIntegrationEvent extends TrackedEvent {
private UUID userId; private UUID userId;
private UUID tenant;
public UUID getUserId() { public UUID getUserId() {
return userId; return userId;
@ -17,12 +16,4 @@ public class UserRemovalIntegrationEvent extends TrackedEvent {
public void setUserId(UUID userId) { public void setUserId(UUID userId) {
this.userId = userId; this.userId = userId;
} }
public UUID getTenant() {
return tenant;
}
public void setTenant(UUID tenant) {
this.tenant = tenant;
}
} }

View File

@ -1,5 +1,6 @@
package gr.cite.notification.integrationevent.inbox.userremoval; package gr.cite.notification.integrationevent.inbox.userremoval;
import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler;
public interface UserRemovalIntegrationEventHandler extends IntegrationEventHandler { public interface UserRemovalIntegrationEventHandler extends IntegrationEventHandler {

View File

@ -1,11 +1,12 @@
package gr.cite.notification.integrationevent.inbox.userremoval; package gr.cite.notification.integrationevent.inbox.userremoval;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; 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.audit.AuditableAction;
import gr.cite.notification.common.JsonHandlingService; 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.common.scope.tenant.TenantScope;
import gr.cite.notification.data.TenantEntity; import gr.cite.notification.data.TenantEntity;
import gr.cite.notification.data.TenantEntityManager;
import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.notification.integrationevent.inbox.EventProcessingStatus; import gr.cite.notification.integrationevent.inbox.EventProcessingStatus;
import gr.cite.notification.integrationevent.inbox.InboxPrincipal; 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.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
@ -41,22 +37,35 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ApplicationContext applicationContext;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final MessageSource messageSource; 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( public UserRemovalIntegrationEventHandlerImpl(
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
ApplicationContext applicationContext,
ErrorThesaurusProperties errors, 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.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext;
this.errors = errors; this.errors = errors;
this.messageSource = messageSource; 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 @Override
@ -69,73 +78,44 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName()); logger.debug("Handling {}", UserRemovalIntegrationEvent.class.getSimpleName());
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
TenantScope scope = this.applicationContext.getBean(TenantScope.class); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
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) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(event.getTenant(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
currentPrincipalResolver.push(InboxPrincipal.build(properties));
UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) {
if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) status = EventProcessingStatus.Postponed;
return EventProcessingStatus.Postponed; currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
return status;
}
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()); userService.deleteAndSave(event.getUserId());
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Delete, Map.ofEntries( auditService.track(AuditableAction.User_Delete, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("id", event.getUserId()) new AbstractMap.SimpleEntry<String, Object>("id", event.getUserId())
)); ));
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
} catch (Exception ex) {
transaction.commit(); status = EventProcessingStatus.Error;
} catch (Exception e) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
transaction.rollback();
throw e;
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
} }
transaction.commit(); return status;
} 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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -5,7 +5,7 @@ import gr.cite.notification.common.validation.BaseValidator;
import gr.cite.notification.convention.ConventionService; import gr.cite.notification.convention.ConventionService;
import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.notification.errorcode.ErrorThesaurusProperties;
import gr.cite.notification.integrationevent.TrackedEvent; 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 gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -23,22 +23,24 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
public static final String _id = "id"; public static final String _id = "id";
private UUID tenant;
private String name; private String name;
public static final String _name = "name"; public static final String _name = "name";
public static final int _nameLength = 200; public static final int _nameLength = 200;
private String subjectId;
public static final String _subjectId = "subjectId";
private UserProfile profile; private UserProfile profile;
private List<UserContactInfo> userContactInfo; private List<UserContactInfo> userContactInfo;
public static final String _userContactInfo = "userContactInfo";
private List<TenantUser> tenantUsers;
public static final String _tenantUsers = "tenantUsers";
private List<UserCredential> credentials;
public static final String _credentials = "credentials";
public UUID getId() { public UUID getId() {
return id; return id;
} }
@ -47,14 +49,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.id = id; this.id = id;
} }
public UUID getTenant() {
return tenant;
}
public void setTenant(UUID tenant) {
this.tenant = tenant;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -63,12 +57,20 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.name = name; this.name = name;
} }
public String getSubjectId() { public List<TenantUser> getTenantUsers() {
return subjectId; return tenantUsers;
} }
public void setSubjectId(String subjectId) { public void setTenantUsers(List<TenantUser> tenantUsers) {
this.subjectId = subjectId; this.tenantUsers = tenantUsers;
}
public List<UserCredential> getCredentials() {
return credentials;
}
public void setCredentials(List<UserCredential> credentials) {
this.credentials = credentials;
} }
public UserProfile getProfile() { public UserProfile getProfile() {
@ -124,9 +126,15 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private ContactInfoType type; private ContactInfoType type;
public static final String _type = "type";
private String value; private String value;
private int ordinal; public static final String _value = "value";
private Integer ordinal;
public static final String _ordinal = "ordinal";
public ContactInfoType getType() { public ContactInfoType getType() {
return type; return type;
@ -144,16 +152,135 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.value = value; this.value = value;
} }
public int getOrdinal() { public Integer getOrdinal() {
return ordinal; return ordinal;
} }
public void setOrdinal(int ordinal) { public void setOrdinal(Integer ordinal) {
this.ordinal = ordinal; this.ordinal = ordinal;
} }
@Component(UserTouchedIntegrationUserContactInfoEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UserTouchedIntegrationUserContactInfoEventValidator extends BaseValidator<UserContactInfo> {
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<UserContactInfo> modelClass() {
return UserContactInfo.class;
}
@Override
protected List<Specification> 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<UserCredential> {
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<UserCredential> modelClass() {
return UserCredential.class;
}
@Override
protected List<Specification> 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<TenantUser> {
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<TenantUser> modelClass() {
return TenantUser.class;
}
@Override
protected List<Specification> 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) @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UserTouchedIntegrationEventValidator extends BaseValidator<UserTouchedIntegrationEvent> { public static class UserTouchedIntegrationEventValidator extends BaseValidator<UserTouchedIntegrationEvent> {
@ -161,9 +288,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private final MessageSource messageSource; 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); super(conventionService, errors);
this.messageSource = messageSource; this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
} }
@Override @Override
@ -183,11 +313,13 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
.failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())),
this.spec() this.spec()
.iff(() -> !this.isEmpty(item.getName())) .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())), .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())),
this.spec() this.navSpec()
.must(() -> !this.isEmpty(item.getSubjectId())) .iff(() -> !this.isListNullOrEmpty(item.getUserContactInfo()))
.failOn(UserTouchedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale())) .on(UserTouchedIntegrationEvent._userContactInfo)
.over(item.getUserContactInfo())
.using((i) -> this.validatorFactory.validator(UserContactInfo.UserTouchedIntegrationUserContactInfoEventValidator.class))
); );
} }
} }

View File

@ -1,5 +1,6 @@
package gr.cite.notification.integrationevent.inbox.usertouched; package gr.cite.notification.integrationevent.inbox.usertouched;
import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler; import gr.cite.notification.integrationevent.inbox.IntegrationEventHandler;
public interface UserTouchedIntegrationEventHandler extends IntegrationEventHandler { public interface UserTouchedIntegrationEventHandler extends IntegrationEventHandler {

View File

@ -1,11 +1,12 @@
package gr.cite.notification.integrationevent.inbox.usertouched; package gr.cite.notification.integrationevent.inbox.usertouched;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; 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.audit.AuditableAction;
import gr.cite.notification.common.JsonHandlingService; 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.common.scope.tenant.TenantScope;
import gr.cite.notification.data.TenantEntity; 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.EventProcessingStatus;
import gr.cite.notification.integrationevent.inbox.InboxPrincipal; import gr.cite.notification.integrationevent.inbox.InboxPrincipal;
import gr.cite.notification.integrationevent.inbox.IntegrationEventProperties; 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.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; 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.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; 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)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserTouchedIntegrationEventHandlerImpl.class));
protected final ApplicationContext applicationContext;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory; 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( public UserTouchedIntegrationEventHandlerImpl(
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
ApplicationContext applicationContext, ValidatorFactory validatorFactory, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager) {
ValidatorFactory validatorFactory) {
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext;
this.validatorFactory = validatorFactory; 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 @Override
@ -61,69 +68,37 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event); this.validatorFactory.validator(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null; EventProcessingStatus status = EventProcessingStatus.Success;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
TenantScope scope = this.applicationContext.getBean(TenantScope.class); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
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) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(event.getTenant(), tenant.getCode()); this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (this.tenantScope.isMultitenant()) {
logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
this.tenantScope.setTempTenant(tenantEntityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
} }
//
// ValidationService validator = this.applicationContext.getBean(ValidationService.class);
// validator.validateForce(model);
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
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); userService.persist(event, null);
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Persist, Map.ofEntries( auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event) new AbstractMap.SimpleEntry<String, Object>("model", event)
)); ));
transaction.commit(); } catch (Exception ex) {
} catch (Exception e) { status = EventProcessingStatus.Error;
transaction.rollback(); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
throw e;
} finally { } finally {
currentPrincipalResolver.pop(); currentPrincipalResolver.pop();
tenantScope.removeTempTenant(this.tenantEntityManager.getEntityManager());
} }
} catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working return status;
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) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
} }
} }

View File

@ -7,52 +7,13 @@ public class OutboxProperties {
private final String exchange; private final String exchange;
private final String tenantTouchTopic;
private final String tenantRemovalTopic; public OutboxProperties(String exchange
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
) { ) {
this.exchange = exchange; this.exchange = exchange;
this.tenantTouchTopic = tenantTouchTopic;
this.tenantRemovalTopic = tenantRemovalTopic;
this.userTouchTopic = userTouchTopic;
this.userRemovalTopic = userRemovalTopic;
this.notifyTopic = notifyTopic;
} }
public String getExchange() { public String getExchange() {
return exchange; 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;
}
} }

View File

@ -20,11 +20,9 @@ import jakarta.persistence.EntityTransaction;
import jakarta.persistence.OptimisticLockException; import jakarta.persistence.OptimisticLockException;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -32,9 +30,11 @@ import java.util.stream.Collectors;
public class OutboxRepositoryImpl implements OutboxRepository { public class OutboxRepositoryImpl implements OutboxRepository {
protected final ApplicationContext applicationContext; protected final ApplicationContext applicationContext;
private final Random random = new Random();
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class));
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final OutboxProperties outboxProperties; private final OutboxProperties outboxProperties;
public OutboxRepositoryImpl( public OutboxRepositoryImpl(
@ -88,14 +88,17 @@ public class OutboxRepositoryImpl implements OutboxRepository {
} catch (OptimisticLockException ex) { } catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working // 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()); logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage());
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
candidate = null; candidate = null;
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), 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; candidate = null;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
@ -137,10 +140,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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; success = false;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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; success = false;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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; success = false;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect();
if (queueOutboxMessages == null) { 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 { } else {
for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) {
@ -281,9 +290,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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 { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect();
if (queueOutboxMessages == null) { 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 { } else {
for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) {
@ -323,9 +334,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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 { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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) { public QueueOutbox create(IntegrationEvent item) {
EntityTransaction transaction = null; EntityTransaction transaction = null;
EntityManager entityManager = null; EntityManager entityManager = null;
boolean success = false;
QueueOutboxEntity queueMessage = null; QueueOutboxEntity queueMessage = null;
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
@ -354,10 +366,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
success = false; transaction.rollback();
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", 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(); // 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())); // event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent()));
// //this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}"); // //this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}");
// //
// QueueOutboxEntity queueMessage = new QueueOutboxEntity(); // QueueOutboxEntity queueMessage = new QueueOutboxEntity();
// queueMessage.setId(UUID.randomUUID()); // queueMessage.setId(UUID.randomUUID());
// queueMessage.setTenantId(null); // queueMessage.setTenantId(event.getTenantId());
// queueMessage.setExchange(this.outboxProperties.getExchange()); // queueMessage.setExchange(this.outboxProperties.getExchange());
// queueMessage.setRoute(routingKey); // queueMessage.setRoute(routingKey);
// queueMessage.setMessageId(event.getMessageId()); // queueMessage.setMessageId(event.getMessageId());

View File

@ -0,0 +1,5 @@
package gr.cite.notification.integrationevent.outbox;
public interface OutboxService {
void publish(OutboxIntegrationEvent event);
}

View File

@ -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
}
}
}

View File

@ -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;
}
}

View File

@ -23,8 +23,8 @@ public class NotificationTemplate {
private NotificationTemplateKind kind; private NotificationTemplateKind kind;
public static final String _kind = "kind"; public static final String _kind = "kind";
private Language language; private String languageCode;
public static final String _language = "language"; public static final String _languageCode = "languageCode";
private NotificationTemplateValue value; private NotificationTemplateValue value;
public static final String _value = "value"; public static final String _value = "value";
@ -76,12 +76,12 @@ public class NotificationTemplate {
this.kind = kind; this.kind = kind;
} }
public Language getLanguage() { public String getLanguageCode() {
return language; return languageCode;
} }
public void setLanguage(Language language) { public void setLanguageCode(String languageCode) {
this.language = language; this.languageCode = languageCode;
} }
public NotificationTemplateValue getValue() { public NotificationTemplateValue getValue() {

View File

@ -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;
}
}

View File

@ -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<Language, LanguageEntity> {
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired
public LanguageBuilder(
ConventionService conventionService) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(LanguageBuilder.class)));
}
public LanguageBuilder authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
public List<Language> build(FieldSet fields, List<LanguageEntity> 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<Language> 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;
}
}

View File

@ -5,12 +5,9 @@ import gr.cite.notification.common.JsonHandlingService;
import gr.cite.notification.common.types.notificationtemplate.NotificationTemplateValueEntity; import gr.cite.notification.common.types.notificationtemplate.NotificationTemplateValueEntity;
import gr.cite.notification.convention.ConventionService; import gr.cite.notification.convention.ConventionService;
import gr.cite.notification.data.NotificationTemplateEntity; import gr.cite.notification.data.NotificationTemplateEntity;
import gr.cite.notification.model.Language;
import gr.cite.notification.model.NotificationTemplate; import gr.cite.notification.model.NotificationTemplate;
import gr.cite.notification.model.Tenant; import gr.cite.notification.model.Tenant;
import gr.cite.notification.model.builder.notificationtemplate.NotificationTemplateValueBuilder; 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.notification.query.TenantQuery;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -59,9 +56,6 @@ public class NotificationTemplateBuilder extends BaseBuilder<NotificationTemplat
FieldSet tenantFields = fields.extractPrefixed(this.asPrefix(NotificationTemplate._tenant)); FieldSet tenantFields = fields.extractPrefixed(this.asPrefix(NotificationTemplate._tenant));
Map<UUID, Tenant> tenantMap = this.collectTenants(tenantFields, data); Map<UUID, Tenant> tenantMap = this.collectTenants(tenantFields, data);
FieldSet languageFields = fields.extractPrefixed(this.asPrefix(NotificationTemplate._language));
Map<UUID, Language> languageMap = this.collectLanguages(languageFields, data);
List<NotificationTemplate> models = new ArrayList<>(); List<NotificationTemplate> models = new ArrayList<>();
for(NotificationTemplateEntity d : data){ for(NotificationTemplateEntity d : data){
NotificationTemplate m = new NotificationTemplate(); NotificationTemplate m = new NotificationTemplate();
@ -69,7 +63,7 @@ public class NotificationTemplateBuilder extends BaseBuilder<NotificationTemplat
if(fields.hasField(this.asIndexer(NotificationTemplate._channel))) m.setChannel(d.getChannel()); if(fields.hasField(this.asIndexer(NotificationTemplate._channel))) m.setChannel(d.getChannel());
if(fields.hasField(this.asIndexer(NotificationTemplate._notificationType))) m.setNotificationType(d.getNotificationType()); if(fields.hasField(this.asIndexer(NotificationTemplate._notificationType))) m.setNotificationType(d.getNotificationType());
if(fields.hasField(this.asIndexer(NotificationTemplate._kind))) m.setKind(d.getKind()); if(fields.hasField(this.asIndexer(NotificationTemplate._kind))) m.setKind(d.getKind());
if (!languageFields.isEmpty() && languageMap != null && languageMap.containsKey(d.getLanguageId())) m.setLanguage(languageMap.get(d.getLanguageId())); if(fields.hasField(this.asIndexer(NotificationTemplate._languageCode))) m.setLanguageCode(d.getLanguageCode());
if (!valueFields.isEmpty() && d.getValue() != null){ if (!valueFields.isEmpty() && d.getValue() != null){
NotificationTemplateValueEntity value = this.jsonHandlingService.fromJsonSafe(NotificationTemplateValueEntity.class, d.getValue()); NotificationTemplateValueEntity value = this.jsonHandlingService.fromJsonSafe(NotificationTemplateValueEntity.class, d.getValue());
m.setValue(this.builderFactory.builder(NotificationTemplateValueBuilder.class).authorize(this.authorize).build(valueFields, value)); m.setValue(this.builderFactory.builder(NotificationTemplateValueBuilder.class).authorize(this.authorize).build(valueFields, value));
@ -85,35 +79,6 @@ public class NotificationTemplateBuilder extends BaseBuilder<NotificationTemplat
return models; return models;
} }
private Map<UUID, Language> collectLanguages(FieldSet fields, List<NotificationTemplateEntity> datas) throws MyApplicationException {
if (fields.isEmpty() || datas.isEmpty()) return null;
this.logger.debug("checking related - {}", NotificationTemplate.class.getSimpleName());
Map<UUID, Language> 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<UUID, Tenant> collectTenants(FieldSet fields, List<NotificationTemplateEntity> datas) throws MyApplicationException { private Map<UUID, Tenant> collectTenants(FieldSet fields, List<NotificationTemplateEntity> datas) throws MyApplicationException {
if (fields.isEmpty() || datas.isEmpty()) return null; if (fields.isEmpty() || datas.isEmpty()) return null;
this.logger.debug("checking related - {}", NotificationTemplate.class.getSimpleName()); this.logger.debug("checking related - {}", NotificationTemplate.class.getSimpleName());

View File

@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.data.InAppNotificationEntity; 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.notification.query.InAppNotificationQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
@ -27,19 +27,16 @@ import java.util.stream.Collectors;
public class InAppNotificationDeleter implements Deleter { public class InAppNotificationDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InAppNotificationDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(InAppNotificationDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory; protected final QueryFactory queryFactory;
private final DeleterFactory deleterFactory;
@Autowired @Autowired
public InAppNotificationDeleter( public InAppNotificationDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory, QueryFactory queryFactory
DeleterFactory deleterFactory
) { ) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.deleterFactory = deleterFactory;
} }
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException { public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {

View File

@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.data.NotificationEntity; 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.notification.query.NotificationQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
@ -27,13 +27,13 @@ import java.util.stream.Collectors;
public class NotificationDeleter implements Deleter { public class NotificationDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory; protected final QueryFactory queryFactory;
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
@Autowired @Autowired
public NotificationDeleter( public NotificationDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory, QueryFactory queryFactory,
DeleterFactory deleterFactory DeleterFactory deleterFactory
) { ) {

View File

@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.data.NotificationTemplateEntity; 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.notification.query.NotificationTemplateQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
@ -27,13 +27,13 @@ import java.util.stream.Collectors;
public class NotificationTemplateDeleter implements Deleter { public class NotificationTemplateDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationTemplateDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotificationTemplateDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory; protected final QueryFactory queryFactory;
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
@Autowired @Autowired
public NotificationTemplateDeleter( public NotificationTemplateDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory, QueryFactory queryFactory,
DeleterFactory deleterFactory DeleterFactory deleterFactory
) { ) {

View File

@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.data.TenantConfigurationEntity; 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.notification.query.TenantConfigurationQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
@ -27,13 +27,13 @@ import java.util.stream.Collectors;
public class TenantConfigurationDeleter implements Deleter { public class TenantConfigurationDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantConfigurationDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantConfigurationDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory; protected final QueryFactory queryFactory;
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
@Autowired @Autowired
public TenantConfigurationDeleter( public TenantConfigurationDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory, QueryFactory queryFactory,
DeleterFactory deleterFactory DeleterFactory deleterFactory
) { ) {

View File

@ -2,7 +2,7 @@ package gr.cite.notification.model.deleter;
import gr.cite.notification.common.enums.IsActive; import gr.cite.notification.common.enums.IsActive;
import gr.cite.notification.data.TenantEntity; 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.notification.query.TenantQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
@ -26,13 +26,13 @@ import java.util.UUID;
public class TenantDeleter implements Deleter { public class TenantDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory; protected final QueryFactory queryFactory;
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
@Autowired @Autowired
public TenantDeleter( public TenantDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory, QueryFactory queryFactory,
DeleterFactory deleterFactory DeleterFactory deleterFactory
) { ) {

Some files were not shown because too many files have changed in this diff Show More