diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/PrincipalController.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/PrincipalController.java index 597a2a3b7..09ab574b4 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/PrincipalController.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/PrincipalController.java @@ -1,5 +1,6 @@ package gr.cite.annotation.web.controllers; +import gr.cite.annotation.authorization.ClaimNames; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; @@ -80,7 +81,7 @@ public class PrincipalController { logger.debug("my-tenants"); MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); - List tenants = this.claimExtractor.asStrings(principal, TenantScope.TenantCodesClaimName); + List tenants = this.claimExtractor.asStrings(principal, ClaimNames.TenantCodesClaimName); this.auditService.track(AuditableAction.Tenants_Lookup); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action); diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java index f73d0b019..26a315dba 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByCodeCacheService.java @@ -13,64 +13,66 @@ import java.util.UUID; @Service public class TenantByCodeCacheService extends CacheService { - public static class TenantByCodeCacheValue { + public static class TenantByCodeCacheValue { - public TenantByCodeCacheValue() { - } + public TenantByCodeCacheValue() { + } - public TenantByCodeCacheValue(String tenantCode, UUID tenantId) { - this.tenantCode = tenantCode; - this.tenantId = tenantId; - } + public TenantByCodeCacheValue(String tenantCode, UUID tenantId) { + this.tenantCode = tenantCode; + this.tenantId = tenantId; + } - private String tenantCode; + private String tenantCode; - public String getTenantCode() { - return tenantCode; - } + public String getTenantCode() { + return tenantCode; + } - public void setTenantCode(String tenantCode) { - this.tenantCode = tenantCode; - } + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } - private UUID tenantId; + private UUID tenantId; - public UUID getTenantId() { - return tenantId; - } + public UUID getTenantId() { + return tenantId; + } - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - } + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + } - private final ConventionService conventionService; + private final ConventionService conventionService; - @Autowired - public TenantByCodeCacheService(TenantByCodeCacheOptions options, ConventionService conventionService) { - super(options); - this.conventionService = conventionService; - } + @Autowired + public TenantByCodeCacheService(TenantByCodeCacheOptions options, ConventionService conventionService) { + super(options); + this.conventionService = conventionService; + } - @EventListener - public void handleTenantTouchedEvent(TenantTouchedEvent event) { - if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantCode())); - if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode())) this.evict(this.buildKey(event.getPreviousTenantCode())); - } + @EventListener + public void handleTenantTouchedEvent(TenantTouchedEvent event) { + if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) + this.evict(this.buildKey(event.getTenantCode())); + if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode())) + this.evict(this.buildKey(event.getPreviousTenantCode())); + } - @Override - protected Class valueClass() { - return TenantByCodeCacheValue.class; - } + @Override + protected Class valueClass() { + return TenantByCodeCacheValue.class; + } - @Override - public String keyOf(TenantByCodeCacheValue value) { - return this.buildKey(value.getTenantCode()); - } + @Override + public String keyOf(TenantByCodeCacheValue value) { + return this.buildKey(value.getTenantCode()); + } - public String buildKey(String code) { - return this.generateKey(new HashMap<>() {{ - put("$code$", code); - }}); - } + public String buildKey(String code) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$code$", code); + return this.generateKey(keyParts); + } } diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByIdCacheService.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByIdCacheService.java index 4fd3db8f6..9f97df8bc 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByIdCacheService.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantByIdCacheService.java @@ -14,63 +14,64 @@ import java.util.UUID; @Service public class TenantByIdCacheService extends CacheService { - public static class TenantByIdCacheValue { + public static class TenantByIdCacheValue { - public TenantByIdCacheValue() { - } + public TenantByIdCacheValue() { + } - public TenantByIdCacheValue(String tenantCode, UUID tenantId) { - this.tenantCode = tenantCode; - this.tenantId = tenantId; - } + public TenantByIdCacheValue(String tenantCode, UUID tenantId) { + this.tenantCode = tenantCode; + this.tenantId = tenantId; + } - private String tenantCode; + private String tenantCode; - public String getTenantCode() { - return tenantCode; - } + public String getTenantCode() { + return tenantCode; + } - public void setTenantCode(String tenantCode) { - this.tenantCode = tenantCode; - } + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } - private UUID tenantId; + private UUID tenantId; - public UUID getTenantId() { - return tenantId; - } + public UUID getTenantId() { + return tenantId; + } - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - } + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + } - private final ConventionService conventionService; + private final ConventionService conventionService; - @Autowired - public TenantByIdCacheService(TenantByIdCacheOptions options, ConventionService conventionService) { - super(options); - this.conventionService = conventionService; - } + @Autowired + public TenantByIdCacheService(TenantByIdCacheOptions options, ConventionService conventionService) { + super(options); + this.conventionService = conventionService; + } - @EventListener - public void handleTenantTouchedEvent(TenantTouchedEvent event) { - if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantId())); - } + @EventListener + public void handleTenantTouchedEvent(TenantTouchedEvent event) { + if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) + this.evict(this.buildKey(event.getTenantId())); + } - @Override - protected Class valueClass() { - return TenantByIdCacheValue.class; - } + @Override + protected Class valueClass() { + return TenantByIdCacheValue.class; + } - @Override - public String keyOf(TenantByIdCacheValue value) { - return this.buildKey(value.getTenantId()); - } + @Override + public String keyOf(TenantByIdCacheValue value) { + return this.buildKey(value.getTenantId()); + } - public String buildKey(UUID id) { - return this.generateKey(new HashMap<>() {{ - put("$tenantId$", id.toString().toLowerCase(Locale.ROOT)); - }}); - } + public String buildKey(UUID id) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$tenantId$", id.toString().toLowerCase(Locale.ROOT)); + return this.generateKey(keyParts); + } } diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantInterceptor.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantInterceptor.java index 9bbc9df2d..ad0c8d8de 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantInterceptor.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantInterceptor.java @@ -1,8 +1,7 @@ package gr.cite.annotation.web.scope.tenant; -import gr.cite.commons.web.authz.service.AuthorizationService; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; + +import gr.cite.annotation.authorization.ClaimNames; import gr.cite.annotation.authorization.Permission; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.tenant.TenantScope; @@ -11,16 +10,20 @@ import gr.cite.annotation.data.TenantUserEntity; import gr.cite.annotation.data.UserEntity; import gr.cite.annotation.data.tenant.TenantScopedBaseEntity; import gr.cite.annotation.errorcode.ErrorThesaurusProperties; +import gr.cite.annotation.query.utils.BuildSubQueryInput; +import gr.cite.annotation.query.utils.QueryUtilsService; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; 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 org.hibernate.Session; +import org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -34,6 +37,7 @@ import org.springframework.web.context.request.WebRequestInterceptor; import javax.management.InvalidApplicationException; import java.util.List; import java.util.Locale; +import java.util.UUID; @Component public class TenantInterceptor implements WebRequestInterceptor { @@ -44,11 +48,10 @@ public class TenantInterceptor implements WebRequestInterceptor { private final CurrentPrincipalResolver currentPrincipalResolver; private final ClaimExtractor claimExtractor; private final ApplicationContext applicationContext; - private final ErrorThesaurusProperties errorThesaurusProperties; private final TenantScopeProperties tenantScopeProperties; private final UserAllowedTenantCacheService userAllowedTenantCacheService; private final ErrorThesaurusProperties errors; - + private final QueryUtilsService queryUtilsService; @PersistenceContext public EntityManager entityManager; @@ -59,60 +62,70 @@ public class TenantInterceptor implements WebRequestInterceptor { CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor, ApplicationContext applicationContext, - ErrorThesaurusProperties errorThesaurusProperties, TenantScopeProperties tenantScopeProperties, UserAllowedTenantCacheService userAllowedTenantCacheService, - ErrorThesaurusProperties errors) { + ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService) { this.tenantScope = tenantScope; this.userScope = userScope; this.currentPrincipalResolver = currentPrincipalResolver; this.claimExtractor = claimExtractor; this.applicationContext = applicationContext; - this.errorThesaurusProperties = errorThesaurusProperties; this.tenantScopeProperties = tenantScopeProperties; this.userAllowedTenantCacheService = userAllowedTenantCacheService; this.errors = errors; + this.queryUtilsService = queryUtilsService; } @Override - public void preHandle(WebRequest request) throws InvalidApplicationException { + public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException, InterruptedException { if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.tenantScope.isMultitenant()) return; boolean isAllowedNoTenant = this.applicationContext.getBean(AuthorizationService.class).authorize(Permission.AllowNoTenant); if (tenantScope.isSet() && this.entityManager != null) { - List currentPrincipalTenantCodes = this.claimExtractor.asStrings(this.currentPrincipalResolver.currentPrincipal(), TenantScope.TenantCodesClaimName); + List currentPrincipalTenantCodes = this.claimExtractor.asStrings(this.currentPrincipalResolver.currentPrincipal(), ClaimNames.TenantCodesClaimName); if ((currentPrincipalTenantCodes == null || !currentPrincipalTenantCodes.contains(tenantScope.getTenantCode())) && !isAllowedNoTenant) { logger.warn("tenant not allowed {}", this.tenantScope.getTenant()); - //throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); + throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); } boolean isUserAllowedTenant = false; - UserAllowedTenantCacheService.UserAllowedTenantCacheValue cacheValue = this.userAllowedTenantCacheService.lookup(this.userAllowedTenantCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.getTenant())); - if (cacheValue != null) { - isUserAllowedTenant = cacheValue.isAllowed(); + if (this.tenantScope.isDefaultTenant()){ + isUserAllowedTenant = true; } else { - isUserAllowedTenant = this.isUserAllowedTenant(); - this.userAllowedTenantCacheService.put(new UserAllowedTenantCacheService.UserAllowedTenantCacheValue(this.userScope.getUserId(), this.tenantScope.getTenant(), isUserAllowedTenant)); + UserAllowedTenantCacheService.UserAllowedTenantCacheValue cacheValue = this.userAllowedTenantCacheService.lookup(this.userAllowedTenantCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.getTenant())); + if (cacheValue != null) { + isUserAllowedTenant = cacheValue.isAllowed(); + } else { + isUserAllowedTenant = this.isUserAllowedTenant(); + this.userAllowedTenantCacheService.put(new UserAllowedTenantCacheService.UserAllowedTenantCacheValue(this.userScope.getUserId(), this.tenantScope.getTenant(), isUserAllowedTenant)); + } } if (isUserAllowedTenant) { - this.entityManager - .unwrap(Session.class) - .enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); + if(!tenantScope.isDefaultTenant()) { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString()); + } else { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } else { if (isAllowedNoTenant || this.isWhiteListedEndpoint(request)) { tenantScope.setTenant(null, null); } else { logger.warn("tenant not allowed {}", this.tenantScope.getTenant()); - //throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); + throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); } } } else { if (!isAllowedNoTenant) { if (!this.isWhiteListedEndpoint(request)) { logger.warn("tenant scope not provided"); - throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); + throw new MyForbiddenException(this.errors.getMissingTenant().getCode(), this.errors.getMissingTenant().getMessage()); } } } @@ -130,26 +143,36 @@ public class TenantInterceptor implements WebRequestInterceptor { return false; } - private boolean isUserAllowedTenant() throws InvalidApplicationException { + private boolean isUserAllowedTenant() throws InvalidApplicationException, InterruptedException { if (userScope.isSet()) { CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); + CriteriaQuery query = criteriaBuilder.createQuery(UserEntity.class); Root root = query.from(UserEntity.class); - Subquery subQuery = query.subquery(TenantUserEntity.class); - Root subQueryRoot = subQuery.from(TenantUserEntity.class); query.where(criteriaBuilder.and( criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active), - criteriaBuilder.in(root.get(UserEntity._id)).value(subQuery.where( - criteriaBuilder.and( - criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()), - criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()), - criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active) - )).select(subQueryRoot.get(TenantUserEntity._userId)).distinct(true) + criteriaBuilder.in(root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class) + .query(query) + .criteriaBuilder(criteriaBuilder) + .keyPathFunc((subQueryRoot) -> subQueryRoot.get(TenantUserEntity._userId)) + .filterFunc((subQueryRoot, cb) -> + { + try { + return cb.and( + criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()), + criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()), + criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active) + ); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } + ) + )) ) )); query.multiselect(root.get(UserEntity._id).alias(UserEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); - if (results.size() > 0) return true; + List results = this.entityManager.createQuery(query).getResultList(); + return !results.isEmpty(); } return false; diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeClaimInterceptor.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeClaimInterceptor.java index 4f8b6d11b..ca58331e6 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeClaimInterceptor.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeClaimInterceptor.java @@ -1,19 +1,20 @@ package gr.cite.annotation.web.scope.tenant; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -import gr.cite.commons.web.oidc.principal.MyPrincipal; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; + +import gr.cite.annotation.authorization.ClaimNames; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.data.TenantEntity; import gr.cite.annotation.errorcode.ErrorThesaurusProperties; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.MyPrincipal; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; 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; @@ -32,165 +33,152 @@ import java.util.UUID; @Component public class TenantScopeClaimInterceptor implements WebRequestInterceptor { - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScopeClaimInterceptor.class)); - private final TenantScope tenantScope; - private final ConventionService conventionService; - private final TenantScopeProperties tenantScopeProperties; - private final ErrorThesaurusProperties errorThesaurusProperties; - private final ClaimExtractor claimExtractor; - private final CurrentPrincipalResolver currentPrincipalResolver; - private final String clientTenantClaimName; - private final ClaimExtractorContext claimExtractorContext; - private final TenantByCodeCacheService tenantByCodeCacheService; - private final TenantByIdCacheService tenantByIdCacheService; - @PersistenceContext - public EntityManager entityManager; + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScopeClaimInterceptor.class)); + private final TenantScope tenantScope; + private final ConventionService conventionService; + private final TenantScopeProperties tenantScopeProperties; + private final ErrorThesaurusProperties errorThesaurusProperties; + private final ClaimExtractor claimExtractor; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final String clientTenantClaimName; + private final ClaimExtractorContext claimExtractorContext; + private final TenantByCodeCacheService tenantByCodeCacheService; + private final TenantByIdCacheService tenantByIdCacheService; + @PersistenceContext + public EntityManager entityManager; - @Autowired - public TenantScopeClaimInterceptor( - TenantScope tenantScope, - ConventionService conventionService, - ClaimExtractor claimExtractor, - CurrentPrincipalResolver currentPrincipalResolver, - ErrorThesaurusProperties errorThesaurusProperties, - TenantScopeProperties tenantScopeProperties, - ClaimExtractorContext claimExtractorContext, - TenantByCodeCacheService tenantByCodeCacheService, - TenantByIdCacheService tenantByIdCacheService - ) { - this.tenantScope = tenantScope; - this.conventionService = conventionService; - this.currentPrincipalResolver = currentPrincipalResolver; - this.claimExtractor = claimExtractor; - this.errorThesaurusProperties = errorThesaurusProperties; - this.tenantScopeProperties = tenantScopeProperties; - this.claimExtractorContext = claimExtractorContext; - this.tenantByCodeCacheService = tenantByCodeCacheService; - this.tenantByIdCacheService = tenantByIdCacheService; - this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + TenantScope.TenantClaimName; - } + @Autowired + public TenantScopeClaimInterceptor( + TenantScope tenantScope, + ConventionService conventionService, + ClaimExtractor claimExtractor, + CurrentPrincipalResolver currentPrincipalResolver, + ErrorThesaurusProperties errorThesaurusProperties, + TenantScopeProperties tenantScopeProperties, + ClaimExtractorContext claimExtractorContext, + TenantByCodeCacheService tenantByCodeCacheService, + TenantByIdCacheService tenantByIdCacheService + ) { + this.tenantScope = tenantScope; + this.conventionService = conventionService; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractor = claimExtractor; + this.errorThesaurusProperties = errorThesaurusProperties; + this.tenantScopeProperties = tenantScopeProperties; + this.claimExtractorContext = claimExtractorContext; + this.tenantByCodeCacheService = tenantByCodeCacheService; + this.tenantByIdCacheService = tenantByIdCacheService; + this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + ClaimNames.TenantClaimName; + } - @Override - public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException { - if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; - if (!this.tenantScope.isMultitenant()) return; + @Override + public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException { + if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; + if (!this.tenantScope.isMultitenant()) return; - MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); - if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) { - Boolean scoped = this.scopeByPrincipal(this.tenantScope, principal); - if (!scoped) scoped = this.scopeByClient(this.tenantScope, principal); - if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant()) throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); - } - } + MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); + if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) { + boolean scoped = this.scopeByPrincipal(principal); + if (!scoped) scoped = this.scopeByClient(principal); + if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant()) + throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); + } + } - private Boolean scopeByPrincipal(TenantScope scope, MyPrincipal principal) { - String tenantCode = this.claimExtractor.tenantString(principal); - if (tenantCode == null || tenantCode.isBlank()) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); + private boolean scopeByPrincipal(MyPrincipal principal) { + String tenantCode = this.claimExtractor.tenantString(principal); + if (this.conventionService.isNullOrEmpty(tenantCode)) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); + if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return false; - UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); - if (tenantId == null && tenantCode == null) return false; - if (tenantId == null) { - TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); - if (cacheValue != null) { - tenantId = cacheValue.getTenantId(); - } else { - tenantId = this.getTenantIdFromDatabase(tenantCode); - this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); - this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); - } - } else { - logger.debug("tenant claim was set to {}", tenantId); - TenantByIdCacheService.TenantByIdCacheValue cacheValue = this.tenantByIdCacheService.lookup(this.tenantByIdCacheService.buildKey(tenantId)); + if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){ + logger.debug("parsed tenant header and set tenant to default tenant"); + this.tenantScope.setTenant(null, tenantCode); + this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); + return true; + } + + UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); + if (tenantId == null) { + TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); + if (cacheValue != null) { + tenantId = cacheValue.getTenantId(); + } else { + tenantId = this.getTenantIdFromDatabase(tenantCode); + this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); + this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); + } + } else { + logger.debug("tenant claim was set to {}", tenantId); + TenantByIdCacheService.TenantByIdCacheValue cacheValue = this.tenantByIdCacheService.lookup(this.tenantByIdCacheService.buildKey(tenantId)); - if (cacheValue != null) { - tenantCode = cacheValue.getTenantCode(); - } else { - tenantCode = this.getTenantCodeFromDatabase(tenantId); - this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); - this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); - } - } + if (cacheValue != null) { + tenantCode = cacheValue.getTenantCode(); + } else { + tenantCode = this.getTenantCodeFromDatabase(tenantId); + this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); + this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); + } + } - if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { - logger.debug("parsed tenant header and set tenant id to {}", tenantId); - this.tenantScope.setTenant(tenantId, tenantCode); - this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); - } - return tenantId != null; - } + if (tenantId != null) { + logger.debug("parsed tenant header and set tenant id to {}", tenantId); + this.tenantScope.setTenant(tenantId, tenantCode); + this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); + return true; + } + return false; + } - private Boolean scopeByClient(TenantScope scope, MyPrincipal principal) throws InvalidApplicationException { - String client = this.claimExtractor.client(principal); + private boolean scopeByClient(MyPrincipal principal) throws InvalidApplicationException { + String client = this.claimExtractor.client(principal); - Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client); - logger.debug("client is whitelisted : {}, scope is set: {}, with value {}", isWhiteListed, scope.isSet(), (scope.isSet() ? scope.getTenant() : null)); + Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client); + logger.debug("client is whitelisted : {}, scope is set: {}, with value {}", isWhiteListed, this.tenantScope.isSet(), (this.tenantScope.isSet() ? this.tenantScope.getTenant() : null)); - return isWhiteListed && scope.isSet(); - } + return isWhiteListed && this.tenantScope.isSet(); + } - private UUID getTenantIdFromDatabase(String tenantCode) { - CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); - Root root = query.from(TenantEntity.class); - query = query.where( - criteriaBuilder.and( - criteriaBuilder.equal(root.get(TenantEntity._code), tenantCode), - criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) - ) - ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); - if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._id); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return (UUID) o; - } catch (ClassCastException e) { - return null; - } - } - return null; - } + private UUID getTenantIdFromDatabase(String tenantCode) { + CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); + Root root = query.from(TenantEntity.class); + query = query.where( + criteriaBuilder.and( + criteriaBuilder.equal(root.get(TenantEntity._code), tenantCode), + criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) + ) + ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); + List results = this.entityManager.createQuery(query).getResultList(); + if (results.size() == 1) { + return results.getFirst().getId(); + } + return null; + } - private String getTenantCodeFromDatabase(UUID tenantId) { - CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); - Root root = query.from(TenantEntity.class); - query = query.where( - criteriaBuilder.and( - criteriaBuilder.equal(root.get(TenantEntity._id), tenantId), - criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) - ) - ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); - List results = this.entityManager.createQuery(query).getResultList(); - if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._code); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return (String) o; - } catch (ClassCastException e) { - return null; - } - } - return null; - } + private String getTenantCodeFromDatabase(UUID tenantId) { + CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); + Root root = query.from(TenantEntity.class); + query = query.where( + criteriaBuilder.and( + criteriaBuilder.equal(root.get(TenantEntity._id), tenantId), + criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) + ) + ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); + List results = this.entityManager.createQuery(query).getResultList(); + if (results.size() == 1) { + return results.getFirst().getCode(); + } + return null; + } - @Override - public void postHandle(@NonNull WebRequest request, ModelMap model) { - this.tenantScope.setTenant(null, null); - this.claimExtractorContext.removeReplaceParameter(TenantScope.TenantReplaceParameter); - } + @Override + public void postHandle(@NonNull WebRequest request, ModelMap model) { + this.tenantScope.setTenant(null, null); + this.claimExtractorContext.removeReplaceParameter(TenantScope.TenantReplaceParameter); + } - @Override - public void afterCompletion(@NonNull WebRequest request, Exception ex) { - } + @Override + public void afterCompletion(@NonNull WebRequest request, Exception ex) { + } } diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeHeaderInterceptor.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeHeaderInterceptor.java index 52acfc28f..cce792db8 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeHeaderInterceptor.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeHeaderInterceptor.java @@ -1,18 +1,20 @@ package gr.cite.annotation.web.scope.tenant; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; + +import gr.cite.annotation.authorization.ClaimNames; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.data.TenantEntity; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; import gr.cite.tools.logging.LoggerService; 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 org.jetbrains.annotations.NotNull; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; @@ -21,7 +23,6 @@ import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; -import javax.management.InvalidApplicationException; import java.util.List; import java.util.UUID; @@ -55,16 +56,22 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { } @Override - public void preHandle(WebRequest request) throws InvalidApplicationException { + public void preHandle(@NotNull WebRequest request) { if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.tenantScope.isMultitenant()) return; - String tenantCode = request.getHeader(TenantScope.TenantClaimName); - logger.debug("retrieved request tenant header is: {header}", tenantCode); - if (this.conventionService.isNullOrEmpty(tenantCode)) return; + String tenantCode = request.getHeader(ClaimNames.TenantClaimName); + logger.debug("retrieved request tenant header is: {}", tenantCode); + if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return; + + if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){ + logger.debug("parsed tenant header and set tenant to default tenant"); + this.tenantScope.setTenant(null, tenantCode); + this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); + return; + } UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); - if (tenantId == null && tenantCode == null) return; if (tenantId == null) { TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); if (cacheValue != null) { @@ -85,8 +92,8 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { } } - if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { - logger.debug("parsed tenant header and set tenant id to {tenant}", tenantId); + if (tenantId != null) { + logger.debug("parsed tenant header and set tenant id to {}", tenantId); this.tenantScope.setTenant(tenantId, tenantCode); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); } @@ -94,7 +101,7 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { private UUID getTenantIdFromDatabase(String tenantCode) { CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); Root root = query.from(TenantEntity.class); query = query.where( criteriaBuilder.and( @@ -102,27 +109,16 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) ) ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); + List results = this.entityManager.createQuery(query).getResultList(); if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._id); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return UUID.class.cast(o); - } catch (ClassCastException e) { - return null; - } + return results.getFirst().getId(); } return null; } private String getTenantCodeFromDatabase(UUID tenantId) { CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); Root root = query.from(TenantEntity.class); query = query.where( criteriaBuilder.and( @@ -130,20 +126,9 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor { criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) ) ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); - List results = this.entityManager.createQuery(query).getResultList(); + List results = this.entityManager.createQuery(query).getResultList(); if (results.size() == 1) { - Object o; - try { - o = results.get(0).get(TenantEntity._code); - } catch (IllegalArgumentException e) { - return null; - } - if (o == null) return null; - try { - return String.class.cast(o); - } catch (ClassCastException e) { - return null; - } + return results.getFirst().getCode(); } return null; } diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeProperties.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeProperties.java index da4705961..53e8e649f 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeProperties.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/TenantScopeProperties.java @@ -40,4 +40,5 @@ public class TenantScopeProperties { public void setEnforceTrustedTenant(Boolean enforceTrustedTenant) { this.enforceTrustedTenant = enforceTrustedTenant; } + } diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheOptions.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheOptions.java index a24fe89d6..d6787db90 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheOptions.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheOptions.java @@ -8,3 +8,5 @@ import org.springframework.context.annotation.Configuration; @ConfigurationProperties(prefix = "cache.user-allowed-tenant") public class UserAllowedTenantCacheOptions extends CacheOptions { } + + diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheService.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheService.java index a31d8ace4..8f32190ab 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheService.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/tenant/UserAllowedTenantCacheService.java @@ -14,77 +14,78 @@ import java.util.UUID; @Service public class UserAllowedTenantCacheService extends CacheService { - public static class UserAllowedTenantCacheValue { + public static class UserAllowedTenantCacheValue { - public UserAllowedTenantCacheValue() { - } + public UserAllowedTenantCacheValue() { + } - public UserAllowedTenantCacheValue(UUID userId, UUID tenantId, boolean isAllowed) { - this.userId = userId; - this.tenantId = tenantId; - this.isAllowed = isAllowed; - } + public UserAllowedTenantCacheValue(UUID userId, UUID tenantId, boolean isAllowed) { + this.userId = userId; + this.tenantId = tenantId; + this.isAllowed = isAllowed; + } - private UUID userId; + private UUID userId; - public UUID getUserId() { - return userId; - } + public UUID getUserId() { + return userId; + } - public void setUserId(UUID userId) { - this.userId = userId; - } + public void setUserId(UUID userId) { + this.userId = userId; + } - private UUID tenantId; + private UUID tenantId; - public UUID getTenantId() { - return tenantId; - } + public UUID getTenantId() { + return tenantId; + } - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } - private boolean isAllowed; + private boolean isAllowed; - public boolean isAllowed() { - return isAllowed; - } + public boolean isAllowed() { + return isAllowed; + } - public void setAllowed(boolean allowed) { - isAllowed = allowed; - } - } + public void setAllowed(boolean allowed) { + isAllowed = allowed; + } + } - @Autowired - public UserAllowedTenantCacheService(UserAllowedTenantCacheOptions options) { - super(options); - } + @Autowired + public UserAllowedTenantCacheService(UserAllowedTenantCacheOptions options) { + super(options); + } - @EventListener - public void handleUserRemovedFromTenantEvent(UserRemovedFromTenantEvent event) { - this.evict(this.buildKey(event.getUserId(), event.getTenantId())); - } + @EventListener + public void handleUserRemovedFromTenantEvent(UserRemovedFromTenantEvent event) { + this.evict(this.buildKey(event.getUserId(), event.getTenantId())); + } - @EventListener - public void handleUserAddedToTenantEvent(UserAddedToTenantEvent event) { - this.evict(this.buildKey(event.getUserId(), event.getTenantId())); - } + @EventListener + public void handleUserAddedToTenantEvent(UserAddedToTenantEvent event) { + this.evict(this.buildKey(event.getUserId(), event.getTenantId())); + } - @Override - protected Class valueClass() { - return UserAllowedTenantCacheValue.class; - } + @Override + protected Class valueClass() { + return UserAllowedTenantCacheValue.class; + } - @Override - public String keyOf(UserAllowedTenantCacheValue value) { - return this.buildKey(value.getUserId(), value.getTenantId()); - } + @Override + public String keyOf(UserAllowedTenantCacheValue value) { + return this.buildKey(value.getUserId(), value.getTenantId()); + } - public String buildKey(UUID userId, UUID tenantId) { - return this.generateKey(new HashMap<>() {{ - put("$user_id$", userId.toString().toLowerCase(Locale.ROOT)); - put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT)); - }}); - } + public String buildKey(UUID userId, UUID tenantId) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$user_id$", userId.toString().toLowerCase(Locale.ROOT)); + keyParts.put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT)); + return this.generateKey(keyParts); + } } + diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptor.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptor.java index 63ac88829..8bf390cad 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptor.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptor.java @@ -1,154 +1,81 @@ -//package gr.cite.notification.web.scope.user; -// -// -//import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -//import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; -//import gr.cite.notification.common.enums.IsActive; -//import gr.cite.notification.common.scope.user.UserScope; -//import gr.cite.notification.data.UserEntity; -//import gr.cite.notification.locale.LocaleService; -//import gr.cite.tools.logging.LoggerService; -//import org.slf4j.LoggerFactory; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.lang.NonNull; -//import org.springframework.stereotype.Component; -//import org.springframework.transaction.PlatformTransactionManager; -//import org.springframework.transaction.TransactionDefinition; -//import org.springframework.transaction.TransactionStatus; -//import org.springframework.transaction.support.DefaultTransactionDefinition; -//import org.springframework.ui.ModelMap; -//import org.springframework.web.context.request.WebRequest; -//import org.springframework.web.context.request.WebRequestInterceptor; -// -//import javax.management.InvalidApplicationException; -//import jakarta.persistence.EntityManager; -//import jakarta.persistence.PersistenceContext; -//import jakarta.persistence.Tuple; -//import jakarta.persistence.criteria.CriteriaBuilder; -//import jakarta.persistence.criteria.CriteriaQuery; -//import jakarta.persistence.criteria.Root; -//import java.time.Instant; -//import java.util.List; -//import java.util.UUID; -// -//@Component -//public class UserInterceptor implements WebRequestInterceptor { -// private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class)); -// private final UserScope userScope; -// private final ClaimExtractor claimExtractor; -// private final CurrentPrincipalResolver currentPrincipalResolver; -// private final LocaleService localeService; -// private final PlatformTransactionManager transactionManager; -// private final UserInterceptorCacheService userInterceptorCacheService; -// @PersistenceContext -// public EntityManager entityManager; -// -// @Autowired -// public UserInterceptor( -// UserScope userScope, -// LocaleService localeService, -// ClaimExtractor claimExtractor, -// CurrentPrincipalResolver currentPrincipalResolver, -// PlatformTransactionManager transactionManager, -// UserInterceptorCacheService userInterceptorCacheService -// ) { -// this.userScope = userScope; -// this.localeService = localeService; -// this.currentPrincipalResolver = currentPrincipalResolver; -// this.claimExtractor = claimExtractor; -// this.transactionManager = transactionManager; -// this.userInterceptorCacheService = userInterceptorCacheService; -// } -// -// @Override -// public void preHandle(WebRequest request) throws InvalidApplicationException { -// UUID userId = null; -// if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { -// String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); -// -// UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); -// if (cacheValue != null) { -// userId = cacheValue.getUserId(); -// } else { -// userId = this.getUserIdFromDatabase(subjectId); -// if (userId == null) userId = this.createUser(subjectId); -// -// this.userInterceptorCacheService.put(new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId)); -// } -// } -// this.userScope.setUserId(userId); -// } -// -// private UUID getUserIdFromDatabase(String subjectId) throws InvalidApplicationException { -// CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); -// CriteriaQuery query = criteriaBuilder.createQuery(Tuple.class); -// Root root = query.from(UserEntity.class); -// query.where( -// criteriaBuilder.and( -//// criteriaBuilder.equal(root.get(UserEntity._subjectId), subjectId), -// criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active) -// )); -// -// query.multiselect(root.get(UserEntity._id).alias(UserEntity._id)); -// -// List results = this.entityManager.createQuery(query).getResultList(); -// if (results.size() == 1) { -// Object o; -// try { -// o = results.get(0).get(UserEntity._id); -// } catch (IllegalArgumentException e) { -// return null; -// } -// if (o == null) return null; -// try { -// return UUID.class.cast(o); -// } catch (ClassCastException e) { -// return null; -// } -// } -// return null; -// } -// -// private UUID createUser(String subjectId) { -// String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal()); -// String familyName = this.claimExtractor.familyName(this.currentPrincipalResolver.currentPrincipal()); -// if (name == null) name = subjectId; -// UserEntity user = new UserEntity(); -// user.setId(UUID.randomUUID()); -// user.setCreatedAt(Instant.now()); -// user.setUpdatedAt(Instant.now()); -// user.setName(name); -//// user.setLastName(familyName == null ? name : familyName); -// user.setIsActive(IsActive.Active); -//// user.setSubjectId(subjectId); -//// user.setCulture(this.localeService.cultureName()); -//// user.setTimezone(this.localeService.timezoneName()); -//// user.setLanguage(this.localeService.language()); -// -// DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); -// definition.setName(UUID.randomUUID().toString()); -// definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); -// definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); -// TransactionStatus status = null; -// try { -// status = transactionManager.getTransaction(definition); -// this.entityManager.persist(user); -// -// this.entityManager.flush(); -// transactionManager.commit(status); -// } catch (Exception ex) { -// if (status != null) transactionManager.rollback(status); -// throw ex; -// } -// return user.getId(); -// } -// -// @Override -// public void postHandle(@NonNull WebRequest request, ModelMap model) { -// this.userScope.setUserId(null); -// } -// -// @Override -// public void afterCompletion(@NonNull WebRequest request, Exception ex) { -// } -//} +package gr.cite.annotation.web.scope.user; + + +import gr.cite.annotation.common.scope.user.UserScope; +import gr.cite.annotation.data.UserCredentialEntity; +import gr.cite.annotation.model.UserCredential; +import gr.cite.annotation.query.UserCredentialQuery; +import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; +import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.exception.MyForbiddenException; +import gr.cite.tools.fieldset.BaseFieldSet; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.ui.ModelMap; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.WebRequestInterceptor; + +import java.util.UUID; + +@Component +public class UserInterceptor implements WebRequestInterceptor { + private final UserScope userScope; + private final ClaimExtractor claimExtractor; + private final CurrentPrincipalResolver currentPrincipalResolver; + private final UserInterceptorCacheService userInterceptorCacheService; + private final QueryFactory queryFactory; + + @Autowired + public UserInterceptor( + UserScope userScope, + ClaimExtractor claimExtractor, + CurrentPrincipalResolver currentPrincipalResolver, + UserInterceptorCacheService userInterceptorCacheService, + QueryFactory queryFactory) { + this.userScope = userScope; + this.currentPrincipalResolver = currentPrincipalResolver; + this.claimExtractor = claimExtractor; + this.userInterceptorCacheService = userInterceptorCacheService; + this.queryFactory = queryFactory; + } + + @Override + public void preHandle(@NotNull WebRequest request) { + UUID userId = null; + if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { + String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); + if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed"); + + UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); + if (cacheValue != null) { + userId = cacheValue.getUserId(); + } else { + userId = this.findExistingUserFromDb(subjectId); + if (userId != null) { + cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId); + this.userInterceptorCacheService.put(cacheValue); + } + } + } + this.userScope.setUserId(userId); + } + private UUID findExistingUserFromDb(String subjectId) { + UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user)); + if (userCredential != null) { + return userCredential.getUserId(); + } + return null; + } + @Override + public void postHandle(@NonNull WebRequest request, ModelMap model) { + this.userScope.setUserId(null); + } + + @Override + public void afterCompletion(@NonNull WebRequest request, Exception ex) { + } +} + diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheOptions.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheOptions.java index ceaf7996e..d1e503a67 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheOptions.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheOptions.java @@ -1,10 +1,10 @@ -//package gr.cite.notification.web.scope.user; -// -//import gr.cite.tools.cache.CacheOptions; -//import org.springframework.boot.context.properties.ConfigurationProperties; -//import org.springframework.context.annotation.Configuration; -// -//@Configuration -//@ConfigurationProperties(prefix = "cache.user-by-subject-id") -//public class UserInterceptorCacheOptions extends CacheOptions { -//} +package gr.cite.annotation.web.scope.user; + +import gr.cite.tools.cache.CacheOptions; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "cache.user-by-subject-id") +public class UserInterceptorCacheOptions extends CacheOptions { +} diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheService.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheService.java index 338a69f03..de1d089ab 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheService.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/scope/user/UserInterceptorCacheService.java @@ -1,77 +1,67 @@ -//package gr.cite.notification.web.scope.user; -// -//import gr.cite.notification.convention.ConventionService; -//import gr.cite.notification.event.UserTouchedEvent; -//import gr.cite.tools.cache.CacheService; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.context.event.EventListener; -//import org.springframework.stereotype.Service; -// -//import java.util.HashMap; -//import java.util.UUID; -// -//@Service -//public class UserInterceptorCacheService extends CacheService { -// -// public static class UserInterceptorCacheValue { -// -// public UserInterceptorCacheValue() { -// } -// -// public UserInterceptorCacheValue(String subjectId, UUID userId) { -// this.subjectId = subjectId; -// this.userId = userId; -// } -// -// private String subjectId; -// -// public String getSubjectId() { -// return subjectId; -// } -// -// public void setSubjectId(String subjectId) { -// this.subjectId = subjectId; -// } -// -// private UUID userId; -// -// public UUID getUserId() { -// return userId; -// } -// -// public void setUserId(UUID userId) { -// this.userId = userId; -// } -// } -// -// private final ConventionService conventionService; -// -// @Autowired -// public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) { -// super(options); -// this.conventionService = conventionService; -// } -// -// @EventListener -// public void handleUserTouchedEvent(UserTouchedEvent event) { -// if (!this.conventionService.isNullOrEmpty(event.getSubjectId())) this.evict(this.buildKey(event.getSubjectId())); -// if (!this.conventionService.isNullOrEmpty(event.getPreviousSubjectId())) this.evict(this.buildKey(event.getPreviousSubjectId())); -// } -// -// @Override -// protected Class valueClass() { -// return UserInterceptorCacheValue.class; -// } -// -// @Override -// public String keyOf(UserInterceptorCacheValue value) { -// return this.buildKey(value.getSubjectId()); -// } -// -// -// public String buildKey(String subject) { -// return this.generateKey(new HashMap<>() {{ -// put("$subject$", subject); -// }}); -// } -//} +package gr.cite.annotation.web.scope.user; + +import gr.cite.annotation.convention.ConventionService; +import gr.cite.tools.cache.CacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.UUID; + +@Service +public class UserInterceptorCacheService extends CacheService { + + public static class UserInterceptorCacheValue { + + public UserInterceptorCacheValue() { + } + + public UserInterceptorCacheValue(String subjectId, UUID userId) { + this.subjectId = subjectId; + this.userId = userId; + } + + + public String getSubjectId() { + return subjectId; + } + + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } + + private String subjectId; + private UUID userId; + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + } + + + @Autowired + public UserInterceptorCacheService(UserInterceptorCacheOptions options, ConventionService conventionService) { + super(options); + } + + @Override + protected Class valueClass() { + return UserInterceptorCacheValue.class; + } + + @Override + public String keyOf(UserInterceptorCacheValue value) { + return this.buildKey(value.getSubjectId()); + } + + + public String buildKey(String subject) { + HashMap keyParts = new HashMap<>(); + keyParts.put("$subject$", subject); + return this.generateKey(keyParts); + } +} diff --git a/annotation-service/annotation-web/src/main/resources/config/db.yml b/annotation-service/annotation-web/src/main/resources/config/db.yml index d8c511a44..163ff2f80 100644 --- a/annotation-service/annotation-web/src/main/resources/config/db.yml +++ b/annotation-service/annotation-web/src/main/resources/config/db.yml @@ -10,7 +10,7 @@ spring: dialect: org.hibernate.dialect.PostgreSQLDialect hibernate: naming: - physical-strategy: gr.cite.notification.config.db.PrefixPhysicalNamingStrategy + physical-strategy: gr.cite.annotation.data.namingstrategy.PrefixPhysicalNamingStrategy implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy datasource: url: ${DB_CONNECTION_STRING} diff --git a/annotation-service/annotation-web/src/main/resources/config/errors.yml b/annotation-service/annotation-web/src/main/resources/config/errors.yml index 79762a9e7..6b359871b 100644 --- a/annotation-service/annotation-web/src/main/resources/config/errors.yml +++ b/annotation-service/annotation-web/src/main/resources/config/errors.yml @@ -41,3 +41,9 @@ error-thesaurus: overlapping-tenant-configuration-notifier-list: code: 119 message: Overlapping Tenant Configuration Notifier List + tenant-not-allowed: + code: 113 + message: tenant not allowed + tenant-tampering: + code: 123 + message: Tenant tampering diff --git a/annotation-service/annotation-web/src/main/resources/config/queue.yml b/annotation-service/annotation-web/src/main/resources/config/queue.yml index d7961d2ec..50e85a269 100644 --- a/annotation-service/annotation-web/src/main/resources/config/queue.yml +++ b/annotation-service/annotation-web/src/main/resources/config/queue.yml @@ -25,9 +25,6 @@ queue: enable: false options: exchange: null - forget-me-completed-topic: forgetme.completed - what-you-know-about-me-completed-topic: whatyouknowaboutme.completed - generate-file-topic: generate.file rabbitmq: enable: false interval-seconds: 30 diff --git a/annotation-service/annotation-web/src/main/resources/config/tenant-devel.yml b/annotation-service/annotation-web/src/main/resources/config/tenant-devel.yml new file mode 100644 index 000000000..f2149021a --- /dev/null +++ b/annotation-service/annotation-web/src/main/resources/config/tenant-devel.yml @@ -0,0 +1,7 @@ +tenant: + multitenancy: + is-multitenant: true + default-tenant-code: default + interceptor: + client-claims-prefix: client_ + enforce-trusted-tenant: false \ No newline at end of file diff --git a/annotation-service/annotation-web/src/main/resources/config/tenant.yml b/annotation-service/annotation-web/src/main/resources/config/tenant.yml index 7fbc2f91c..1b389e623 100644 --- a/annotation-service/annotation-web/src/main/resources/config/tenant.yml +++ b/annotation-service/annotation-web/src/main/resources/config/tenant.yml @@ -2,7 +2,6 @@ tenant: multitenancy: is-multitenant: false interceptor: - client-claims-prefix: client_ white-listed-clients: [ ] enforce-trusted-tenant: false - white-listed-endpoints: [ '/api/principal/my-tenants', '/api/principal/me','/api/user/user-settings', '/error', '/api/tenant-request' ] + white-listed-endpoints: [ '/api/principal/me' ] \ No newline at end of file diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/authorization/ClaimNames.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/authorization/ClaimNames.java new file mode 100644 index 000000000..a28967fb9 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/authorization/ClaimNames.java @@ -0,0 +1,9 @@ +package gr.cite.annotation.authorization; + +public class ClaimNames { + public static final String ExternalProviderName = "ExternalProviderName"; + public static final String TenantCodesClaimName = "TenantCodes"; + public static final String TenantClaimName = "x-tenant"; + public static final String GlobalRolesClaimName = "GlobalRoles"; + public static final String TenantRolesClaimName = "TenantRoles"; +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/MultitenancyProperties.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/MultitenancyProperties.java index 584a66925..258e6bdfe 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/MultitenancyProperties.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/MultitenancyProperties.java @@ -5,6 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "tenant.multitenancy") public class MultitenancyProperties { private boolean isMultitenant; + private String defaultTenantCode; public boolean isMultitenant() { return isMultitenant; @@ -13,4 +14,16 @@ public class MultitenancyProperties { public void setIsMultitenant(boolean multitenant) { isMultitenant = multitenant; } + + public void setMultitenant(boolean multitenant) { + isMultitenant = multitenant; + } + + public String getDefaultTenantCode() { + return defaultTenantCode; + } + + public void setDefaultTenantCode(String defaultTenantCode) { + this.defaultTenantCode = defaultTenantCode; + } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/TenantScope.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/TenantScope.java index e477668ed..3c0ac7095 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/TenantScope.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/common/scope/tenant/TenantScope.java @@ -1,30 +1,25 @@ package gr.cite.annotation.common.scope.tenant; import gr.cite.annotation.data.tenant.TenantScopedBaseEntity; -import gr.cite.tools.logging.LoggerService; import jakarta.persistence.EntityManager; import org.hibernate.Session; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; import javax.management.InvalidApplicationException; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; @Component @RequestScope public class TenantScope { public static final String TenantReplaceParameter = "::TenantCode::"; - public static final String TenantCodesClaimName = "TenantCodes"; - public static final String TenantClaimName = "x-tenant"; - - private MultitenancyProperties multitenancy; - private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScope.class)); - private UUID tenant = null; - private String tenantCode = null; - private UUID initialTenant = null; - private String initialTenantCode = null; + private final MultitenancyProperties multitenancy; + private final AtomicReference tenant = new AtomicReference<>(); + private final AtomicReference tenantCode = new AtomicReference<>(); + private final AtomicReference initialTenant = new AtomicReference<>(); + private final AtomicReference initialTenantCode = new AtomicReference<>(); @Autowired public TenantScope(MultitenancyProperties multitenancy) { @@ -35,49 +30,79 @@ public class TenantScope { return multitenancy.isMultitenant(); } + public String getDefaultTenantCode() { + return multitenancy.getDefaultTenantCode(); + } + public Boolean isSet() { - if (!this.isMultitenant()) return true; - return this.tenant != null; + if (!this.isMultitenant()) + return Boolean.TRUE; + return this.tenant.get() != null || this.isDefaultTenant(); + } + + public Boolean isDefaultTenant() { + if (!this.isMultitenant()) + return Boolean.TRUE; + return this.multitenancy.getDefaultTenantCode().equalsIgnoreCase(this.tenantCode.get()); } public UUID getTenant() throws InvalidApplicationException { - if (!this.isMultitenant()) return null; - if (this.tenant == null) throw new InvalidApplicationException("tenant not set"); - return this.tenant; + if (!this.isMultitenant()) + return null; + if (this.tenant.get() == null && !this.isDefaultTenant()) + throw new InvalidApplicationException("tenant not set"); + return this.isDefaultTenant() ? null : this.tenant.get(); } public String getTenantCode() throws InvalidApplicationException { - if (!this.isMultitenant()) return null; - if (this.tenant == null) throw new InvalidApplicationException("tenant not set"); - return this.tenantCode; + if (!this.isMultitenant()) + return null; + if (this.tenantCode.get() == null) + throw new InvalidApplicationException("tenant not set"); + return this.tenantCode.get(); } public void setTempTenant(EntityManager entityManager, UUID tenant, String tenantCode) { - this.tenant = tenant; + this.tenant.set(tenant); + this.tenantCode.set(tenantCode); - if(this.tenant != null) { - entityManager - .unwrap(Session.class) - .enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.tenant.toString()); + if (this.tenant.get() != null && !this.isDefaultTenant()) { + if(!this.isDefaultTenant()) { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, this.tenant.get().toString()); + } else { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } } - public void removeTempTenant(EntityManager entityManager) { - this.tenant = this.initialTenant; - this.tenantCode = this.initialTenantCode; - if(this.initialTenant != null) { - entityManager - .unwrap(Session.class) - .enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.initialTenant.toString()); + public void removeTempTenant(EntityManager entityManager) { + this.tenant.set(this.initialTenant.get()); + this.tenantCode.set(this.initialTenantCode.get()); + if (this.initialTenant.get() != null && !this.isDefaultTenant()) { + if(!this.isDefaultTenant()) { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, this.tenant.get().toString()); + } else { + entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } } public void setTenant(UUID tenant, String tenantCode) { if (this.isMultitenant()) { - this.tenant = tenant; - this.initialTenant = tenant; - this.tenantCode = tenantCode; - this.initialTenantCode = tenantCode; + this.tenant.set(tenant); + this.initialTenant.set(tenant); + this.tenantCode.set(tenantCode); + this.initialTenantCode.set(tenantCode); } } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/AnnotationEntity.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/AnnotationEntity.java index 146650d20..63b076f00 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/AnnotationEntity.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/AnnotationEntity.java @@ -4,6 +4,7 @@ import gr.cite.annotation.common.enums.AnnotationProtectionType; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.conventers.AnnotationProtectionTypeConverter; import gr.cite.annotation.data.conventers.IsActiveConverter; +import gr.cite.annotation.data.tenant.TenantScopedBaseEntity; import jakarta.persistence.*; import java.time.Instant; @@ -11,7 +12,7 @@ import java.util.UUID; @Entity @Table(name = "\"Annotation\"") -public class AnnotationEntity { +public class AnnotationEntity extends TenantScopedBaseEntity { @Id @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/EntityUserEntity.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/EntityUserEntity.java index d03602122..935228cb9 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/EntityUserEntity.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/EntityUserEntity.java @@ -2,6 +2,7 @@ package gr.cite.annotation.data; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.conventers.IsActiveConverter; +import gr.cite.annotation.data.tenant.TenantScopedBaseEntity; import jakarta.persistence.*; import java.time.Instant; @@ -9,7 +10,7 @@ import java.util.UUID; @Entity @Table(name = "\"EntityUser\"") -public class EntityUserEntity { +public class EntityUserEntity extends TenantScopedBaseEntity { @Id @Column(name = "\"id\"", columnDefinition = "uuid", updatable = false, nullable = false) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java new file mode 100644 index 000000000..6ff264787 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java @@ -0,0 +1,115 @@ +package gr.cite.annotation.data; + +import gr.cite.annotation.common.scope.tenant.TenantScope; +import gr.cite.annotation.common.scope.tenant.TenantScoped; +import gr.cite.annotation.data.tenant.TenantScopedBaseEntity; +import gr.cite.annotation.errorcode.ErrorThesaurusProperties; +import gr.cite.tools.exception.MyForbiddenException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.PersistenceContext; +import org.hibernate.Session; +import org.springframework.stereotype.Service; +import org.springframework.web.context.annotation.RequestScope; + +import javax.management.InvalidApplicationException; + +@Service +@RequestScope +public class TenantEntityManager { + @PersistenceContext + private EntityManager entityManager; + private final TenantScope tenantScope; + private final ErrorThesaurusProperties errors; + + public TenantEntityManager(TenantScope tenantScope, ErrorThesaurusProperties errors) { + this.tenantScope = tenantScope; + this.errors = errors; + } + + + public void persist(Object entity) { + this.entityManager.persist(entity); + } + + public T merge(T entity) throws InvalidApplicationException { + if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!tenantScope.isDefaultTenant()) { + if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } else if (tenantScopedEntity.getTenantId() != null) { + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + } + return this.entityManager.merge(entity); + } + + public void remove(Object entity) throws InvalidApplicationException { + if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!tenantScope.isDefaultTenant()) { + if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } else if (tenantScopedEntity.getTenantId() != null) { + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + } + this.entityManager.remove(entity); + } + + public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { + T entity = this.entityManager.find(entityClass, primaryKey); + + if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) return null; + } + return entity; + } + + public void flush() { + this.entityManager.flush(); + } + + + public void setFlushMode(FlushModeType flushMode) { + this.entityManager.setFlushMode(flushMode); + + } + + public FlushModeType getFlushMode() { + return this.entityManager.getFlushMode(); + } + + public void clear() { + this.entityManager.clear(); + } + + public void enableTenantFilters() throws InvalidApplicationException { + if(!tenantScope.isDefaultTenant()) { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString()); + } else { + this.entityManager + .unwrap(Session.class) + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } + } + + public void disableTenantFilters(){ + this.entityManager + .unwrap(Session.class) + .disableFilter(TenantScopedBaseEntity.TENANT_FILTER); + + this.entityManager + .unwrap(Session.class) + .disableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } + + public EntityManager getEntityManager() { + return entityManager; + } + + public void setEntityManager(EntityManager entityManager) { + this.entityManager = entityManager; + } + +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantScopedEntityManager.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantScopedEntityManager.java deleted file mode 100644 index e6675a4f0..000000000 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantScopedEntityManager.java +++ /dev/null @@ -1,97 +0,0 @@ -package gr.cite.annotation.data; - -import gr.cite.commons.web.authz.service.AuthorizationService; -import gr.cite.annotation.authorization.Permission; -import gr.cite.annotation.common.scope.tenant.TenantScope; -import gr.cite.annotation.common.scope.tenant.TenantScoped; -import gr.cite.tools.exception.MyForbiddenException; -import jakarta.persistence.EntityManager; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Query; -import org.hibernate.Session; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Service; - -import javax.management.InvalidApplicationException; -import java.util.UUID; - -@Service -@Scope -public class TenantScopedEntityManager { - @PersistenceContext - private EntityManager entityManager; - private final AuthorizationService authorizationService; - - private final TenantScope tenantScope; - - public TenantScopedEntityManager(AuthorizationService authorizationService, TenantScope tenantScope) { - this.authorizationService = authorizationService; - this.tenantScope = tenantScope; - } - - public int getBulkSize() { - Session session = this.entityManager.unwrap(Session.class); - return session.getJdbcBatchSize(); - } - - public void setBulkSize(int size) { - Session session = this.entityManager.unwrap(Session.class); - session.setJdbcBatchSize(size); - } - public Query createQuery(String query){ - return this.entityManager.createQuery(query); - } - public void persist(Object entity) { - this.entityManager.persist(entity); - } - - public T merge(T entity) throws InvalidApplicationException { - if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { - boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant); - - final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; - if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering"); - } - return this.entityManager.merge(entity); - } - - public void remove(Object entity) throws InvalidApplicationException { - if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { - final UUID tenantId = tenantScope.getTenant(); - if (!tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering"); - } - this.entityManager.remove(entity); - } - - public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { - T entity = this.entityManager.find(entityClass, primaryKey); - - if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { - boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant); - - final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; - if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) return null; - } - return entity; - } - - public void flush() { - this.entityManager.flush(); - } - - - public void setFlushMode(FlushModeType flushMode) { - this.entityManager.setFlushMode(flushMode); - - } - - public FlushModeType getFlushMode() { - return this.entityManager.getFlushMode(); - } - - public void clear() { - this.entityManager.clear(); - } - -} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserContactInfoEntity.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserContactInfoEntity.java index 05bc96dd7..f360d53de 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserContactInfoEntity.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserContactInfoEntity.java @@ -12,7 +12,7 @@ import java.util.UUID; @Entity @Table(name = "\"UserContactInfo\"") -public class UserContactInfoEntity extends TenantScopedBaseEntity { +public class UserContactInfoEntity { @Id @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserCredentialEntity.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserCredentialEntity.java index 0186287bb..35ef7c853 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserCredentialEntity.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/UserCredentialEntity.java @@ -10,7 +10,7 @@ import java.util.UUID; @Entity @Table(name = "\"UserCredential\"") -public class UserCredentialEntity extends TenantScopedBaseEntity { +public class UserCredentialEntity { @Id @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/config/db/NamingStrategyProperties.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/namingstrategy/NamingStrategyProperties.java similarity index 90% rename from annotation-service/annotation/src/main/java/gr/cite/annotation/config/db/NamingStrategyProperties.java rename to annotation-service/annotation/src/main/java/gr/cite/annotation/data/namingstrategy/NamingStrategyProperties.java index f9621650a..a19a24967 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/config/db/NamingStrategyProperties.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/namingstrategy/NamingStrategyProperties.java @@ -1,4 +1,4 @@ -package gr.cite.annotation.config.db; +package gr.cite.annotation.data.namingstrategy; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.bind.ConstructorBinding; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/config/db/PrefixPhysicalNamingStrategy.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/namingstrategy/PrefixPhysicalNamingStrategy.java similarity index 96% rename from annotation-service/annotation/src/main/java/gr/cite/annotation/config/db/PrefixPhysicalNamingStrategy.java rename to annotation-service/annotation/src/main/java/gr/cite/annotation/data/namingstrategy/PrefixPhysicalNamingStrategy.java index e833fa8a4..eccf89595 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/config/db/PrefixPhysicalNamingStrategy.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/namingstrategy/PrefixPhysicalNamingStrategy.java @@ -1,4 +1,4 @@ -package gr.cite.annotation.config.db; +package gr.cite.annotation.data.namingstrategy; import gr.cite.annotation.common.StringUtils; import org.hibernate.boot.model.naming.Identifier; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantFilterAspect.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantFilterAspect.java index 7fe28f065..1f5338325 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantFilterAspect.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantFilterAspect.java @@ -1,7 +1,5 @@ package gr.cite.annotation.data.tenant; -import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; -import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.annotation.common.scope.tenant.TenantScope; import jakarta.persistence.EntityManager; import org.aspectj.lang.JoinPoint; @@ -9,7 +7,6 @@ import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import javax.management.InvalidApplicationException; @@ -19,30 +16,28 @@ import javax.management.InvalidApplicationException; public class TenantFilterAspect { private final TenantScope tenantScope; - private final ClaimExtractor claimExtractor; - private final CurrentPrincipalResolver currentPrincipalResolver; - private final ApplicationContext applicationContext; @Autowired public TenantFilterAspect( - TenantScope tenantScope, - ClaimExtractor claimExtractor, - CurrentPrincipalResolver currentPrincipalResolver, - ApplicationContext applicationContext + TenantScope tenantScope ) { this.tenantScope = tenantScope; - this.currentPrincipalResolver = currentPrincipalResolver; - this.claimExtractor = claimExtractor; - this.applicationContext = applicationContext; } @AfterReturning( - pointcut="bean(entityManagerFactory) && execution(* createEntityManager(..))", - returning="retVal") + pointcut = "bean(entityManagerFactory) && execution(* createEntityManager(..))", + returning = "retVal") public void getSessionAfter(JoinPoint joinPoint, Object retVal) throws InvalidApplicationException { - if (retVal != null && EntityManager.class.isInstance(retVal) && tenantScope.isSet() && tenantScope.isMultitenant()) { + if (retVal instanceof EntityManager && tenantScope.isSet()) { Session session = ((EntityManager) retVal).unwrap(Session.class); - session.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); + if(!tenantScope.isDefaultTenant()) { + session + .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) + .setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString()); + } else { + session + .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); + } } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantListener.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantListener.java index ae9ccc9d8..ed3af35f1 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantListener.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantListener.java @@ -2,6 +2,7 @@ package gr.cite.annotation.data.tenant; import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.common.scope.tenant.TenantScoped; +import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; import jakarta.persistence.PrePersist; @@ -17,18 +18,28 @@ public class TenantListener { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantListener.class)); private final TenantScope tenantScope; + private final ErrorThesaurusProperties errors; + + @Autowired public TenantListener( - TenantScope tenantScope + TenantScope tenantScope, ErrorThesaurusProperties errors ) { this.tenantScope = tenantScope; + this.errors = errors; } @PrePersist public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { if (tenantScope.isMultitenant()) { - final UUID tenantId = tenantScope.getTenant(); - entity.setTenantId(tenantId); + if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) { + logger.error("somebody tried to set not login tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + if (!tenantScope.isDefaultTenant()) { + final UUID tenantId = tenantScope.getTenant(); + entity.setTenantId(tenantId); + } } else { entity.setTenantId(null); } @@ -38,22 +49,30 @@ public class TenantListener { @PreRemove public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { if (tenantScope.isMultitenant()) { - if (entity.getTenantId() == null) { - logger.error("somebody tried to set null tenant"); - throw new MyForbiddenException("tenant tampering"); - } - if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { - logger.error("somebody tried to change an entries tenant"); - throw new MyForbiddenException("tenant tampering"); - } + if (!tenantScope.isDefaultTenant()) { + if (entity.getTenantId() == null) { + logger.error("somebody tried to set null tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { + logger.error("somebody tried to change an entries tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } - final UUID tenantId = tenantScope.getTenant(); - entity.setTenantId(tenantId); + final UUID tenantId = tenantScope.getTenant(); + entity.setTenantId(tenantId); + } else { + if (entity.getTenantId() != null) { + logger.error("somebody tried to set null tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); + } + } } else { - if (entity.getTenantId() != null) { - logger.error("somebody tried to set non null tenant"); - throw new MyForbiddenException("tenant tampering"); + if (entity.getTenantId() != null && (!this.tenantScope.isDefaultTenant() ||entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) { + logger.error("somebody tried to change an entries tenant"); + throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } } + } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantScopedBaseEntity.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantScopedBaseEntity.java index dc5acf522..c34be941e 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantScopedBaseEntity.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/tenant/TenantScopedBaseEntity.java @@ -16,23 +16,20 @@ import java.util.UUID; //@Getter //@Setter //@NoArgsConstructor -@FilterDef(name = TenantScopedBaseEntity.tenantFilter, parameters = {@ParamDef(name = TenantScopedBaseEntity.tenantFilterTenantParam, type = String.class)}) -@Filter(name = "tenantFilter", condition = "tenant = (cast(:tenantId as uuid))") +@FilterDef(name = TenantScopedBaseEntity.TENANT_FILTER, parameters = {@ParamDef(name = TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, type = String.class)}) +@FilterDef(name = TenantScopedBaseEntity.DEFAULT_TENANT_FILTER) +@Filter(name = TenantScopedBaseEntity.DEFAULT_TENANT_FILTER, condition = "(tenant = tenant is null)") +@Filter(name = TenantScopedBaseEntity.TENANT_FILTER, condition = "(tenant = (cast(:tenantId as uuid)) or tenant is null)") @EntityListeners(TenantListener.class) public abstract class TenantScopedBaseEntity implements TenantScoped, Serializable { - - @Serial private static final long serialVersionUID = 1L; + public static final String TENANT_FILTER = "tenantFilter"; + public static final String DEFAULT_TENANT_FILTER = "defaultTenantFilter"; + public static final String TENANT_FILTER_TENANT_PARAM = "tenantId"; - public static final String tenantFilter = "tenantFilter"; - - public static final String tenantFilterTenantParam = "tenantId"; - - @Column(name = "tenant", columnDefinition = "uuid") + @Column(name = "tenant", columnDefinition = "uuid", nullable = true) private UUID tenantId; - public static final String _tenantId = "tenantId"; - public UUID getTenantId() { return tenantId; } @@ -41,5 +38,5 @@ public abstract class TenantScopedBaseEntity implements TenantScoped, Serializab public void setTenantId(UUID tenantId) { this.tenantId = tenantId; } - } + diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/errorcode/ErrorThesaurusProperties.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/errorcode/ErrorThesaurusProperties.java index 990ab50ee..d8fcce302 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/errorcode/ErrorThesaurusProperties.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/errorcode/ErrorThesaurusProperties.java @@ -15,6 +15,8 @@ public class ErrorThesaurusProperties { private ErrorDescription singleTenantConfigurationPerTypeSupported; private ErrorDescription incompatibleTenantConfigurationTypes; private ErrorDescription overlappingTenantConfigurationNotifierList; + private ErrorDescription tenantNotAllowed; + private ErrorDescription tenantTampering; public ErrorDescription getHashConflict() { return hashConflict; @@ -89,4 +91,19 @@ public class ErrorThesaurusProperties { this.overlappingTenantConfigurationNotifierList = overlappingTenantConfigurationNotifierList; } + public ErrorDescription getTenantNotAllowed() { + return tenantNotAllowed; + } + + public void setTenantNotAllowed(ErrorDescription tenantNotAllowed) { + this.tenantNotAllowed = tenantNotAllowed; + } + + public ErrorDescription getTenantTampering() { + return tenantTampering; + } + + public void setTenantTampering(ErrorDescription tenantTampering) { + this.tenantTampering = tenantTampering; + } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java index d37870bc0..ccfda4f8c 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java @@ -17,6 +17,7 @@ import gr.cite.queueinbox.entity.QueueInboxStatus; import gr.cite.queueinbox.repository.CandidateInfo; import gr.cite.queueinbox.repository.InboxRepository; import gr.cite.queueinbox.task.MessageOptions; +import gr.cite.rabbitmq.IntegrationEventMessageConstants; import gr.cite.rabbitmq.consumer.InboxCreatorParams; import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.QueryFactory; @@ -242,7 +243,8 @@ public class InboxRepositoryImpl implements InboxRepository { QueueInboxEntity queueMessage = new QueueInboxEntity(); queueMessage.setId(UUID.randomUUID()); - queueMessage.setTenantId(null); + Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null; + if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId); queueMessage.setExchange(this.inboxProperties.getExchange()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setQueue(inboxCreatorParams.getQueueName()); @@ -279,7 +281,7 @@ public class InboxRepositoryImpl implements InboxRepository { logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId()); } else { - EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); + EventProcessingStatus status = this.processMessage(queueInboxMessage); switch (status) { case Success: { queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); @@ -322,23 +324,23 @@ public class InboxRepositoryImpl implements InboxRepository { return success; } - private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { + private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) { IntegrationEventHandler handler; - logger.debug("Processing message with routing key '{}'", routingKey); - if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic())) + logger.debug("Processing message with routing key '{}'", queueInboxMessage.getRoute()); + if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantRemovalTopic())) handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantTouchTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantTouchTopic())) handler = this.applicationContext.getBean(TenantTouchedIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserRemovalTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getUserRemovalTopic())) handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getUserTouchTopic())) handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesTouchTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getAnnotationEntitiesTouchTopic())) handler = this.applicationContext.getBean(AnnotationEntitiesTouchedIntegrationEventHandler.class); - else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesRemovalTopic())) + else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getAnnotationEntitiesRemovalTopic())) handler = this.applicationContext.getBean(AnnotationEntitiesRemovalIntegrationEventHandler.class); else { - logger.error("No handler found for message routing key '{}'. Discarding.", routingKey); + logger.error("No handler found for message routing key '{}'. Discarding.", queueInboxMessage.getRoute()); handler = null; } @@ -346,16 +348,17 @@ public class InboxRepositoryImpl implements InboxRepository { return EventProcessingStatus.Discard; IntegrationEventProperties properties = new IntegrationEventProperties(); - properties.setAppId(appId); - properties.setMessageId(messageId); + properties.setAppId(queueInboxMessage.getApplicationId()); + properties.setMessageId(queueInboxMessage.getMessageId().toString()); + properties.setTenantId(queueInboxMessage.getTenantId()); - TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, message); + TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, queueInboxMessage.getMessage()); // using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag)) // { try { - return handler.handle(properties, message); + return handler.handle(properties, queueInboxMessage.getMessage()); } catch (Exception ex) { - logger.error("problem handling event from routing key " + routingKey + ". Setting nack and continuing...", ex); + logger.error("problem handling event from routing key " + queueInboxMessage.getRoute() + ". Setting nack and continuing...", ex); return EventProcessingStatus.Error; } // } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/IntegrationEventProperties.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/IntegrationEventProperties.java index 2e504fa83..535036121 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/IntegrationEventProperties.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/IntegrationEventProperties.java @@ -1,10 +1,13 @@ package gr.cite.annotation.integrationevent.inbox; +import java.util.UUID; + public class IntegrationEventProperties { private String messageId; private String appId; + private UUID tenantId; public String getMessageId() { return messageId; @@ -22,4 +25,11 @@ public class IntegrationEventProperties { this.appId = appId; } + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java index 46f94ffe4..612dc8098 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java @@ -4,15 +4,21 @@ import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.fake.FakeRequestScope; +import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.data.EntityUserEntity; +import gr.cite.annotation.data.TenantEntity; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; +import gr.cite.annotation.model.Tenant; import gr.cite.annotation.query.EntityUserQuery; +import gr.cite.annotation.query.TenantQuery; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.validation.ValidatorFactory; import jakarta.persistence.EntityManager; @@ -62,6 +68,20 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann EntityTransaction transaction = null; try (FakeRequestScope ignored = new FakeRequestScope()) { try { + TenantScope scope = this.applicationContext.getBean(TenantScope.class); + if (scope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + scope.setTenant(properties.getTenantId(), tenant.getCode()); + } else if (scope.isMultitenant()) { +// logger.error("missing tenant from event message"); +// return EventProcessingStatus.Error; + scope.setTenant(null, scope.getDefaultTenantCode()); + } + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties)); @@ -75,6 +95,9 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann try { + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + tenantEntityManager.disableTenantFilters(); EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); List items = entityUserQuery .entityIds(event.getEntityIds()) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java index b63d2db44..191942bef 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java @@ -4,15 +4,21 @@ import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.fake.FakeRequestScope; +import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.data.EntityUserEntity; +import gr.cite.annotation.data.TenantEntity; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; +import gr.cite.annotation.model.Tenant; import gr.cite.annotation.query.EntityUserQuery; +import gr.cite.annotation.query.TenantQuery; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.tools.auditing.AuditService; import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.validation.ValidatorFactory; import jakarta.persistence.EntityManager; @@ -64,6 +70,20 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann EntityTransaction transaction = null; try (FakeRequestScope ignored = new FakeRequestScope()) { try { + TenantScope scope = this.applicationContext.getBean(TenantScope.class); + if (scope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (tenant == null) { + logger.error("missing tenant from event message"); + return EventProcessingStatus.Error; + } + scope.setTenant(properties.getTenantId(), tenant.getCode()); + } else if (scope.isMultitenant()) { +// logger.error("missing tenant from event message"); +// return EventProcessingStatus.Error; + scope.setTenant(null, scope.getDefaultTenantCode()); + } + CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties)); @@ -76,6 +96,9 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann transaction.begin(); try { + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + tenantEntityManager.disableTenantFilters(); for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) { EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java index 87fb9f3b4..3b7921a37 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java @@ -1,5 +1,6 @@ package gr.cite.annotation.integrationevent.inbox.tenantremoval; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; @@ -35,15 +36,10 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn private final ApplicationContext applicationContext; - private final ErrorThesaurusProperties errors; - private final MessageSource messageSource; - - public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) { + public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext) { this.jsonHandlingService = jsonHandlingService; this.applicationContext = applicationContext; - this.errors = errors; - this.messageSource = messageSource; } @Override @@ -70,6 +66,9 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn transaction.begin(); try { + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); + tenantEntityManager.disableTenantFilters(); TenantService tenantService = this.applicationContext.getBean(TenantService.class); tenantService.deleteAndSave(event.getId()); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java index 559874974..4c03dcdd0 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java @@ -1,5 +1,6 @@ package gr.cite.annotation.integrationevent.inbox.tenanttouch; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; @@ -67,6 +68,9 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn 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); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java index b0a3e9564..3e7b05a5b 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEvent.java @@ -8,7 +8,6 @@ public class UserRemovalIntegrationEvent extends TrackedEvent { private UUID userId; - private UUID tenant; public UUID getUserId() { return userId; @@ -17,12 +16,4 @@ public class UserRemovalIntegrationEvent extends TrackedEvent { public void setUserId(UUID userId) { this.userId = userId; } - - public UUID getTenant() { - return tenant; - } - - public void setTenant(UUID tenant) { - this.tenant = tenant; - } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java index 07e0b5346..cef55829a 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -1,5 +1,6 @@ package gr.cite.annotation.integrationevent.inbox.userremoval; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; @@ -75,16 +76,16 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr try { QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && event.getTenant() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (scope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); if (tenant == null) { logger.error("missing tenant from event message"); return EventProcessingStatus.Error; } - scope.setTenant(event.getTenant(), tenant.getCode()); + scope.setTenant(properties.getTenantId(), tenant.getCode()); } else if (scope.isMultitenant()) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; +// logger.error("missing tenant from event message"); + scope.setTenant(null, scope.getDefaultTenantCode()); } CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); @@ -101,6 +102,8 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr transaction.begin(); try { + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); UserService userService = this.applicationContext.getBean(UserService.class); userService.deleteAndSave(event.getUserId()); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEvent.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEvent.java index 5df47faca..bf5d7cb76 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEvent.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEvent.java @@ -6,6 +6,7 @@ import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.integrationevent.TrackedEvent; import gr.cite.annotation.model.persist.UserTouchedIntegrationEventPersist; +import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.specification.Specification; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.MessageSource; @@ -23,22 +24,24 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { public static final String _id = "id"; - private UUID tenant; - private String name; public static final String _name = "name"; public static final int _nameLength = 200; - private String subjectId; - - public static final String _subjectId = "subjectId"; - private UserProfile profile; private List userContactInfo; + public static final String _userContactInfo = "userContactInfo"; + private List tenantUsers; + + public static final String _tenantUsers = "tenantUsers"; + private List credentials; + + public static final String _credentials = "credentials"; + public UUID getId() { return id; } @@ -47,14 +50,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.id = id; } - public UUID getTenant() { - return tenant; - } - - public void setTenant(UUID tenant) { - this.tenant = tenant; - } - public String getName() { return name; } @@ -63,12 +58,20 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.name = name; } - public String getSubjectId() { - return subjectId; + public List getTenantUsers() { + return tenantUsers; } - public void setSubjectId(String subjectId) { - this.subjectId = subjectId; + public void setTenantUsers(List tenantUsers) { + this.tenantUsers = tenantUsers; + } + + public List getCredentials() { + return credentials; + } + + public void setCredentials(List credentials) { + this.credentials = credentials; } public UserProfile getProfile() { @@ -124,9 +127,15 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { private ContactInfoType type; + public static final String _type = "type"; + private String value; - private int ordinal; + public static final String _value = "value"; + + private Integer ordinal; + + public static final String _ordinal = "ordinal"; public ContactInfoType getType() { return type; @@ -144,13 +153,132 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.value = value; } - public int getOrdinal() { + public Integer getOrdinal() { return ordinal; } - public void setOrdinal(int ordinal) { + public void setOrdinal(Integer ordinal) { this.ordinal = ordinal; } + + @Component(UserTouchedIntegrationEvent.UserContactInfo.UserTouchedIntegrationUserContactInfoEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationUserContactInfoEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationUserContactInfoEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationUserContactInfoEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return UserContactInfo.class; + } + + @Override + protected List specifications(UserContactInfo item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isEmpty(item.getValue())) + .failOn(UserContactInfo._value).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._value}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getType())) + .failOn(UserContactInfo._type).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._type}, LocaleContextHolder.getLocale())), + this.spec() + .must(() -> !this.isNull(item.getOrdinal())) + .failOn(UserContactInfo._ordinal).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._ordinal}, LocaleContextHolder.getLocale())) + ); + } + } + } + + 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(UserCredential.UserTouchedIntegrationUserCredentialEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationUserCredentialEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationUserCredentialEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationUserCredentialEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return UserCredential.class; + } + + @Override + protected List specifications(UserCredential item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isEmpty(item.getSubjectId())) + .failOn(UserCredential._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserCredential._subjectId}, LocaleContextHolder.getLocale())) + ); + } + } + } + + public static class TenantUser { + + private UUID tenant; + public static final String _tenant = "tenant"; + + public UUID getTenant() { + return tenant; + } + + public void setTenant(UUID tenant) { + this.tenant = tenant; + } + + @Component(TenantUser.UserTouchedIntegrationTenantUserEventValidator.ValidatorName) + @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public static class UserTouchedIntegrationTenantUserEventValidator extends BaseValidator { + + public static final String ValidatorName = "UserTouchedIntegrationTenantUserEventValidator"; + + private final MessageSource messageSource; + + protected UserTouchedIntegrationTenantUserEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + super(conventionService, errors); + this.messageSource = messageSource; + } + + @Override + protected Class modelClass() { + return TenantUser.class; + } + + @Override + protected List specifications(TenantUser item) { + return Arrays.asList( + this.spec() + .must(() -> !this.isNull(item.getTenant())) + .failOn(TenantUser._tenant).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantUser._tenant}, LocaleContextHolder.getLocale())) + ); + } + } } @Component(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.ValidatorName) @@ -161,9 +289,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { private final MessageSource messageSource; - protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { + private final ValidatorFactory validatorFactory; + + protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) { super(conventionService, errors); this.messageSource = messageSource; + this.validatorFactory = validatorFactory; } @Override @@ -185,10 +316,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { .iff(() -> !this.isEmpty(item.getName())) .must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEventPersist._nameLength)) .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), - this.spec() - .must(() -> !this.isEmpty(item.getSubjectId())) - .failOn(UserTouchedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale())) - ); + this.navSpec() + .iff(() -> !this.isListNullOrEmpty(item.getUserContactInfo())) + .on(UserTouchedIntegrationEvent._userContactInfo) + .over(item.getUserContactInfo()) + .using((i) -> this.validatorFactory.validator(UserContactInfo.UserTouchedIntegrationUserContactInfoEventValidator.class)) + ); } } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java index 60288eb09..0bb00b430 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java @@ -1,5 +1,6 @@ package gr.cite.annotation.integrationevent.inbox.usertouch; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.common.JsonHandlingService; @@ -67,20 +68,18 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr try { QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); TenantScope scope = this.applicationContext.getBean(TenantScope.class); - if (scope.isMultitenant() && event.getTenant() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); + if (scope.isMultitenant() && properties.getTenantId() != null) { + TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); if (tenant == null) { logger.error("missing tenant from event message"); return EventProcessingStatus.Error; } - scope.setTenant(event.getTenant(), tenant.getCode()); + scope.setTenant(properties.getTenantId(), tenant.getCode()); } else if (scope.isMultitenant()) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; +// logger.error("missing tenant from event message"); +// return EventProcessingStatus.Error; + scope.setTenant(null, scope.getDefaultTenantCode()); } -// -// ValidationService validator = this.applicationContext.getBean(ValidationService.class); -// validator.validateForce(model); CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); currentPrincipalResolver.push(InboxPrincipal.build(properties)); @@ -92,6 +91,8 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr transaction.begin(); try { + TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class); + tenantEntityManager.setEntityManager(entityManager); UserService userService = this.applicationContext.getBean(UserService.class); userService.persist(event, null); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxProperties.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxProperties.java index 669a7bc42..2130633a6 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxProperties.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxProperties.java @@ -7,52 +7,13 @@ public class OutboxProperties { private final String exchange; - private final String tenantTouchTopic; - private final String tenantRemovalTopic; - - private final String userTouchTopic; - - private final String userRemovalTopic; - - private final String notifyTopic; - - public OutboxProperties(String exchange, - String tenantTouchTopic, - String tenantRemovalTopic, - String userTouchTopic, - String userRemovalTopic, - String notifyTopic + public OutboxProperties(String exchange ) { this.exchange = exchange; - this.tenantTouchTopic = tenantTouchTopic; - this.tenantRemovalTopic = tenantRemovalTopic; - this.userTouchTopic = userTouchTopic; - this.userRemovalTopic = userRemovalTopic; - this.notifyTopic = notifyTopic; } public String getExchange() { return exchange; } - - public String getTenantTouchTopic() { - return tenantTouchTopic; - } - - public String getTenantRemovalTopic() { - return tenantRemovalTopic; - } - - public String getUserTouchTopic() { - return userTouchTopic; - } - - public String getUserRemovalTopic() { - return userRemovalTopic; - } - - public String getNotifyTopic() { - return notifyTopic; - } } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxRepositoryImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxRepositoryImpl.java index 61f663054..5ce77cb02 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxRepositoryImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxRepositoryImpl.java @@ -23,7 +23,6 @@ import org.springframework.context.ApplicationContext; import java.time.Instant; import java.util.List; -import java.util.Random; import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -31,9 +30,11 @@ import java.util.stream.Collectors; public class OutboxRepositoryImpl implements OutboxRepository { protected final ApplicationContext applicationContext; - private final Random random = new Random(); + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class)); + private final JsonHandlingService jsonHandlingService; + private final OutboxProperties outboxProperties; public OutboxRepositoryImpl( @@ -87,14 +88,17 @@ public class OutboxRepositoryImpl implements OutboxRepository { } catch (OptimisticLockException ex) { // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); candidate = null; } catch (Exception ex) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); candidate = null; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); @@ -136,10 +140,12 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); success = false; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -181,10 +187,12 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); success = false; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -236,10 +244,12 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); success = false; } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -265,7 +275,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); if (queueOutboxMessages == null) { - logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", confirmedMessages.stream().map(UUID::toString).collect(Collectors.joining(","))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -280,9 +290,11 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -307,7 +319,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); if (queueOutboxMessages == null) { - logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", nackedMessages.stream().map(UUID::toString).collect(Collectors.joining(","))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -322,9 +334,11 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); + if (transaction != null) + transaction.rollback(); } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -335,7 +349,6 @@ public class OutboxRepositoryImpl implements OutboxRepository { public QueueOutbox create(IntegrationEvent item) { EntityTransaction transaction = null; EntityManager entityManager = null; - boolean success = false; QueueOutboxEntity queueMessage = null; try (FakeRequestScope ignored = new FakeRequestScope()) { try { @@ -353,10 +366,11 @@ public class OutboxRepositoryImpl implements OutboxRepository { transaction.commit(); } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); - if (transaction != null) transaction.rollback(); - success = false; + if (transaction != null) + transaction.rollback(); } finally { - if (entityManager != null) entityManager.close(); + if (entityManager != null) + entityManager.close(); } } catch (Exception ex) { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); @@ -374,13 +388,14 @@ public class OutboxRepositoryImpl implements OutboxRepository { } // UUID correlationId = UUID.randomUUID(); -// if (event.getEvent() != null) event.getEvent().setTrackingContextTag(correlationId.toString()); +// if (event.getEvent() != null) +// event.getEvent().setTrackingContextTag(correlationId.toString()); // event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent())); // //this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}"); // // QueueOutboxEntity queueMessage = new QueueOutboxEntity(); // queueMessage.setId(UUID.randomUUID()); -// queueMessage.setTenantId(null); +// queueMessage.setTenantId(event.getTenantId()); // queueMessage.setExchange(this.outboxProperties.getExchange()); // queueMessage.setRoute(routingKey); // queueMessage.setMessageId(event.getMessageId()); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxService.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxService.java new file mode 100644 index 000000000..ff1861abe --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxService.java @@ -0,0 +1,5 @@ +package gr.cite.annotation.integrationevent.outbox; + +public interface OutboxService { + void publish(OutboxIntegrationEvent event); +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxServiceImpl.java new file mode 100644 index 000000000..e3a8936e0 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/outbox/OutboxServiceImpl.java @@ -0,0 +1,34 @@ +package gr.cite.annotation.integrationevent.outbox; + + +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +@Component +@RequestScope +public class OutboxServiceImpl implements OutboxService { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxServiceImpl.class)); + + private final ApplicationEventPublisher eventPublisher; + + public OutboxServiceImpl( + ApplicationEventPublisher eventPublisher + ) { + this.eventPublisher = eventPublisher; + } + + @Override + public void publish(OutboxIntegrationEvent event) { + try { + eventPublisher.publishEvent(event); + } catch (Exception ex) { + logger.error(new MapLogEntry(String.format("Could not save message ", event.getMessage())).And("message", event.getMessage()).And("ex", ex)); + //Still want to skip it from processing + } + + } +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/Annotation.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/Annotation.java index 363f8dfac..5d7bbe05a 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/Annotation.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/Annotation.java @@ -75,6 +75,10 @@ public class Annotation { private List authorizationFlags; public static final String _authorizationFlags = "authorizationFlags"; + + private Boolean belongsToCurrentTenant; + public static final String _belongsToCurrentTenant = "belongsToCurrentTenant"; + public UUID getId() { return id; } @@ -202,4 +206,5 @@ public class Annotation { public void setAuthorizationFlags(List authorizationFlags) { this.authorizationFlags = authorizationFlags; } + } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/TenantUser.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/TenantUser.java new file mode 100644 index 000000000..510ca44c8 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/TenantUser.java @@ -0,0 +1,98 @@ +package gr.cite.annotation.model; + + +import gr.cite.annotation.common.enums.IsActive; + +import java.time.Instant; +import java.util.UUID; + +public class TenantUser { + + private UUID id; + public final static String _id = "id"; + + private User user; + public final static String _user = "user"; + + private Tenant tenant; + public final static String _tenant = "tenant"; + + private IsActive isActive; + public final static String _isActive = "isActive"; + + private Instant createdAt; + public final static String _createdAt = "createdAt"; + + private Instant updatedAt; + public final static String _updatedAt = "updatedAt"; + + private String hash; + public final static String _hash = "hash"; + + private Boolean belongsToCurrentTenant; + public static final String _belongsToCurrentTenant = "belongsToCurrentTenant"; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Tenant getTenant() { + return tenant; + } + + public void setTenant(Tenant tenant) { + this.tenant = tenant; + } + + public IsActive getIsActive() { + return isActive; + } + + public void setIsActive(IsActive isActive) { + this.isActive = isActive; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = updatedAt; + } + + public String getHash() { + return hash; + } + + public void setHash(String hash) { + this.hash = hash; + } + + public Boolean getBelongsToCurrentTenant() { + return belongsToCurrentTenant; + } + + public void setBelongsToCurrentTenant(Boolean belongsToCurrentTenant) { + this.belongsToCurrentTenant = belongsToCurrentTenant; + } +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/UserContactInfo.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/UserContactInfo.java new file mode 100644 index 000000000..0bde01f50 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/UserContactInfo.java @@ -0,0 +1,76 @@ +package gr.cite.annotation.model; + + +import gr.cite.annotation.common.enums.ContactInfoType; + +import java.time.Instant; +import java.util.UUID; + +public class UserContactInfo { + private UUID id; + public static final String _id = "id"; + + private String value; + public static final String _value = "value"; + + private ContactInfoType type; + public static final String _type = "type"; + + private int ordinal; + public static final String _ordinal = "ordinal"; + + private User user; + public static final String _user = "user"; + + private Instant createdAt; + + public static final String _createdAt = "createdAt"; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public ContactInfoType getType() { + return type; + } + + public void setType(ContactInfoType type) { + this.type = type; + } + + public int getOrdinal() { + return ordinal; + } + + public void setOrdinal(int ordinal) { + this.ordinal = ordinal; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/AnnotationDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/AnnotationDeleter.java index b78014ab4..d2af087cc 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/AnnotationDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/AnnotationDeleter.java @@ -2,10 +2,8 @@ package gr.cite.annotation.model.deleter; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.AnnotationEntity; -import gr.cite.annotation.data.TenantScopedEntityManager; -import gr.cite.annotation.data.UserCredentialEntity; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.query.AnnotationQuery; -import gr.cite.annotation.query.UserCredentialQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; @@ -28,13 +26,13 @@ public class AnnotationDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final QueryFactory queryFactory; @Autowired public AnnotationDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory ) { this.entityManager = entityManager; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/EntityUserDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/EntityUserDeleter.java index 3da0db0bb..ce96c4852 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/EntityUserDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/EntityUserDeleter.java @@ -2,7 +2,7 @@ package gr.cite.EntityUser.model.deleter; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.EntityUserEntity; -import gr.cite.annotation.data.TenantScopedEntityManager; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.query.EntityUserQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.query.QueryFactory; @@ -26,13 +26,13 @@ public class EntityUserDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EntityUserDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final QueryFactory queryFactory; @Autowired public EntityUserDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory ) { this.entityManager = entityManager; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantConfigurationDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantConfigurationDeleter.java index 9a12418e1..527aa5fe6 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantConfigurationDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantConfigurationDeleter.java @@ -2,7 +2,7 @@ package gr.cite.annotation.model.deleter; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.TenantConfigurationEntity; -import gr.cite.annotation.data.TenantScopedEntityManager; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.query.TenantConfigurationQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -27,13 +27,13 @@ import java.util.stream.Collectors; public class TenantConfigurationDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantConfigurationDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; private final DeleterFactory deleterFactory; @Autowired public TenantConfigurationDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantDeleter.java index 9e4e6c8b4..afbd33859 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantDeleter.java @@ -2,7 +2,7 @@ package gr.cite.annotation.model.deleter; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.data.TenantEntity; -import gr.cite.annotation.data.TenantScopedEntityManager; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.query.TenantQuery; import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.DeleterFactory; @@ -26,13 +26,13 @@ import java.util.UUID; public class TenantDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; private final DeleterFactory deleterFactory; @Autowired public TenantDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java new file mode 100644 index 000000000..a79a96ace --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/TenantUserDeleter.java @@ -0,0 +1,74 @@ +package gr.cite.annotation.model.deleter; + +import gr.cite.annotation.common.enums.IsActive; +import gr.cite.annotation.data.TenantEntityManager; +import gr.cite.annotation.data.TenantUserEntity; +import gr.cite.annotation.query.TenantUserQuery; +import gr.cite.tools.data.deleter.Deleter; +import gr.cite.tools.data.deleter.DeleterFactory; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.management.InvalidApplicationException; +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantUserDeleter implements Deleter { + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantUserDeleter.class)); + + private final TenantEntityManager entityManager; + private final QueryFactory queryFactory; + private final DeleterFactory deleterFactory; + + @Autowired + public TenantUserDeleter( + TenantEntityManager entityManager, + QueryFactory queryFactory, + DeleterFactory deleterFactory + ) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + this.deleterFactory = deleterFactory; + } + + public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(e -> e.size()).orElse(0)).And("ids", ids)); + List datas = this.queryFactory.query(TenantUserQuery.class).ids(ids).collect(); + logger.trace("retrieved {} items", Optional.ofNullable(datas).map(e -> e.size()).orElse(0)); + this.deleteAndSave(datas); + } + + public void deleteAndSave(List datas) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(datas).map(e -> e.size()).orElse(0)); + this.delete(datas); + logger.trace("saving changes"); + this.entityManager.flush(); + logger.trace("changes saved"); + } + + public void delete(List datas) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(datas).map(x -> x.size()).orElse(0)); + if (datas == null || datas.isEmpty()) return; + + Instant now = Instant.now(); + + for (TenantUserEntity item : datas) { + logger.trace("deleting item {}", item.getId()); + item.setIsActive(IsActive.Inactive); + item.setUpdatedAt(now); + logger.trace("updating item"); + this.entityManager.merge(item); + logger.trace("updated item"); + } + } +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserContactInfoDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserContactInfoDeleter.java new file mode 100644 index 000000000..54a4224b7 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserContactInfoDeleter.java @@ -0,0 +1,68 @@ +package gr.cite.annotation.model.deleter; + +import gr.cite.annotation.data.TenantEntityManager; +import gr.cite.annotation.data.UserContactInfoEntity; +import gr.cite.annotation.query.UserContactInfoQuery; +import gr.cite.tools.data.deleter.Deleter; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.management.InvalidApplicationException; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserContactInfoDeleter implements Deleter { + + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserContactInfoDeleter.class)); + private final TenantEntityManager entityManager; + + protected final QueryFactory queryFactory; + + + @Autowired + public UserContactInfoDeleter( + TenantEntityManager entityManager, + QueryFactory queryFactory + ) { + this.entityManager = entityManager; + this.queryFactory = queryFactory; + } + + public void deleteAndSaveByIds(List ids) throws InvalidApplicationException { + logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids)); + List data = this.queryFactory.query(UserContactInfoQuery.class).ids(ids).collect(); + logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.deleteAndSave(data); + } + + public void deleteAndSave(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + this.delete(data); + logger.trace("saving changes"); + this.entityManager.flush(); + logger.trace("changes saved"); + } + + public void delete(List data) throws InvalidApplicationException { + logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0)); + if (data == null || data.isEmpty()) + return; + + for (UserContactInfoEntity item : data) { + logger.trace("deleting item {}", item.getId()); + logger.trace("deleting item"); + this.entityManager.remove(item); + logger.trace("deleted item"); + } + } + +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserCredentialDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserCredentialDeleter.java index d08e71b71..a1357f37d 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserCredentialDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserCredentialDeleter.java @@ -1,7 +1,7 @@ package gr.cite.annotation.model.deleter; import gr.cite.annotation.common.enums.IsActive; -import gr.cite.annotation.data.TenantScopedEntityManager; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.data.UserCredentialEntity; import gr.cite.annotation.query.UserCredentialQuery; import gr.cite.tools.data.deleter.Deleter; @@ -26,13 +26,13 @@ public class UserCredentialDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class)); - private final TenantScopedEntityManager entityManager; + private final TenantEntityManager entityManager; private final QueryFactory queryFactory; @Autowired public UserCredentialDeleter( - TenantScopedEntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory ) { this.entityManager = entityManager; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserDeleter.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserDeleter.java index 11656c29b..2045bfea1 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserDeleter.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/model/deleter/UserDeleter.java @@ -1,8 +1,9 @@ package gr.cite.annotation.model.deleter; import gr.cite.annotation.common.enums.IsActive; -import gr.cite.annotation.data.UserCredentialEntity; -import gr.cite.annotation.data.UserEntity; +import gr.cite.annotation.data.*; +import gr.cite.annotation.query.TenantUserQuery; +import gr.cite.annotation.query.UserContactInfoQuery; import gr.cite.annotation.query.UserCredentialQuery; import gr.cite.annotation.query.UserQuery; import gr.cite.tools.data.deleter.Deleter; @@ -10,7 +11,6 @@ import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; -import jakarta.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -30,7 +30,7 @@ public class UserDeleter implements Deleter { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserDeleter.class)); - private final EntityManager entityManager; + private final TenantEntityManager entityManager; protected final QueryFactory queryFactory; @@ -38,7 +38,7 @@ public class UserDeleter implements Deleter { @Autowired public UserDeleter( - EntityManager entityManager, + TenantEntityManager entityManager, QueryFactory queryFactory, DeleterFactory deleterFactory ) { @@ -76,6 +76,18 @@ public class UserDeleter implements Deleter { deleter.delete(items); } + { + logger.debug("checking related - {}", UserContactInfoEntity.class.getSimpleName()); + List items = this.queryFactory.query(UserContactInfoQuery.class).userIds(ids).collect(); + UserContactInfoDeleter deleter = this.deleterFactory.deleter(UserContactInfoDeleter.class); + deleter.delete(items); + } + { + logger.debug("checking related - {}", TenantUserEntity.class.getSimpleName()); + List items = this.queryFactory.query(TenantUserQuery.class).userIds(ids).collect(); + TenantUserDeleter deleter = this.deleterFactory.deleter(TenantUserDeleter.class); + deleter.delete(items); + } Instant now = Instant.now(); for (UserEntity item : data) { diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/query/TenantUserQuery.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/query/TenantUserQuery.java new file mode 100644 index 000000000..1fb4acef9 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/query/TenantUserQuery.java @@ -0,0 +1,209 @@ +package gr.cite.annotation.query; + +import gr.cite.annotation.authorization.AuthorizationFlags; +import gr.cite.annotation.authorization.Permission; +import gr.cite.annotation.common.enums.IsActive; +import gr.cite.annotation.common.scope.user.UserScope; +import gr.cite.annotation.data.TenantUserEntity; +import gr.cite.annotation.data.UserEntity; +import gr.cite.annotation.model.Tenant; +import gr.cite.annotation.model.TenantUser; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.query.FieldResolver; +import gr.cite.tools.data.query.QueryBase; +import gr.cite.tools.data.query.QueryContext; +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Predicate; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; + + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class TenantUserQuery extends QueryBase { + + private Collection ids; + private Collection userIds; + private Collection tenantIds; + private Collection isActives; + private UserQuery userQuery; + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + private final UserScope userScope; + private final AuthorizationService authService; + + public TenantUserQuery( + UserScope userScope, + AuthorizationService authService + ) { + this.userScope = userScope; + this.authService = authService; + } + + public TenantUserQuery ids(UUID value) { + this.ids = List.of(value); + return this; + } + + public TenantUserQuery ids(UUID... value) { + this.ids = Arrays.asList(value); + return this; + } + + public TenantUserQuery ids(Collection values) { + this.ids = values; + return this; + } + + public TenantUserQuery userIds(UUID value) { + this.userIds = List.of(value); + return this; + } + + public TenantUserQuery userIds(UUID... value) { + this.userIds = Arrays.asList(value); + return this; + } + + public TenantUserQuery userIds(Collection values) { + this.userIds = values; + return this; + } + + public TenantUserQuery tenantIds(UUID value) { + this.tenantIds = List.of(value); + return this; + } + + public TenantUserQuery tenantIds(UUID... value) { + this.tenantIds = Arrays.asList(value); + return this; + } + + public TenantUserQuery tenantIds(Collection values) { + this.tenantIds = values; + return this; + } + + public TenantUserQuery isActive(IsActive value) { + this.isActives = List.of(value); + return this; + } + + public TenantUserQuery isActive(IsActive... value) { + this.isActives = Arrays.asList(value); + return this; + } + + public TenantUserQuery isActive(Collection values) { + this.isActives = values; + return this; + } + + public TenantUserQuery userSubQuery(UserQuery subQuery) { + this.userQuery = subQuery; + return this; + } + + public TenantUserQuery authorize(EnumSet values) { + this.authorize = values; + return this; + } + + @Override + protected Class entityClass() { + return TenantUserEntity.class; + } + + @Override + protected Boolean isFalseQuery() { + return this.isEmpty(this.ids) || this.isEmpty(this.userIds) || this.isEmpty(this.tenantIds) || this.isEmpty(this.isActives) || this.isFalseQuery(this.userQuery); + } + + + @Override + protected Predicate applyAuthZ(QueryContext queryContext) { + if (this.authorize.contains(AuthorizationFlags.None)) return null; + if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseTenant)) return null; + UUID ownerId = null; + if (this.authorize.contains(AuthorizationFlags.Owner)) ownerId = this.userScope.getUserIdSafe(); + + List predicates = new ArrayList<>(); + if (ownerId != null) { + predicates.add(queryContext.CriteriaBuilder.equal(queryContext.Root.get(TenantUserEntity._userId), ownerId)); + } + if (!predicates.isEmpty()) { + Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); + return queryContext.CriteriaBuilder.and(predicatesArray); + } else { + return queryContext.CriteriaBuilder.or(); //Creates a false query + } + } + + @Override + protected Predicate applyFilters(QueryContext queryContext) { + List predicates = new ArrayList<>(); + if (this.ids != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._id)); + for (UUID item : this.ids) inClause.value(item); + predicates.add(inClause); + } + if (this.userIds != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._userId)); + for (UUID item : this.userIds) inClause.value(item); + predicates.add(inClause); + } + if (this.tenantIds != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._tenantId)); + for (UUID item : this.tenantIds) inClause.value(item); + predicates.add(inClause); + } + if (this.isActives != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._isActive)); + for (IsActive item : this.isActives) inClause.value(item); + predicates.add(inClause); + } + if (this.userQuery != null) { + QueryContext subQuery = this.applySubQuery(this.userQuery, queryContext, UUID.class, root -> root.get(TenantUserEntity._userId)); + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._userId)).value(subQuery.Query)); + } + if (!predicates.isEmpty()) { + Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); + return queryContext.CriteriaBuilder.and(predicatesArray); + } else { + return null; + } + } + + @Override + protected TenantUserEntity convert(Tuple tuple, Set columns) { + TenantUserEntity item = new TenantUserEntity(); + item.setId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._id, UUID.class)); + item.setTenantId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._tenantId, UUID.class)); + item.setUserId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._userId, UUID.class)); + item.setTenantId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._tenantId, UUID.class)); + item.setCreatedAt(QueryBase.convertSafe(tuple, columns, TenantUserEntity._createdAt, Instant.class)); + item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, TenantUserEntity._updatedAt, Instant.class)); + item.setIsActive(QueryBase.convertSafe(tuple, columns, TenantUserEntity._isActive, IsActive.class)); + return item; + } + + @Override + protected String fieldNameOf(FieldResolver item) { + if (item.match(TenantUser._id)) return TenantUserEntity._id; + else if (item.match(TenantUser._tenant, Tenant._id)) return TenantUserEntity._tenantId; + else if (item.prefix(TenantUser._tenant)) return TenantUserEntity._tenantId; + else if (item.match(TenantUser._isActive)) return TenantUserEntity._isActive; + else if (item.match(TenantUser._createdAt)) return TenantUserEntity._createdAt; + else if (item.match(TenantUser._updatedAt)) return TenantUserEntity._updatedAt; + else if (item.match(TenantUser._hash)) return TenantUserEntity._updatedAt; + else if (item.match(TenantUser._user, UserEntity._id)) return TenantUserEntity._userId; + else if (item.prefix(TenantUser._user)) return TenantUserEntity._userId; + else if (item.match(TenantUser._belongsToCurrentTenant)) return TenantUserEntity._tenantId; + else return null; + } +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/query/UserContactInfoQuery.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/query/UserContactInfoQuery.java new file mode 100644 index 000000000..f67e40507 --- /dev/null +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/query/UserContactInfoQuery.java @@ -0,0 +1,236 @@ +package gr.cite.annotation.query; + +import gr.cite.annotation.authorization.AuthorizationFlags; +import gr.cite.annotation.authorization.Permission; +import gr.cite.annotation.common.enums.ContactInfoType; +import gr.cite.annotation.common.scope.user.UserScope; +import gr.cite.annotation.data.UserContactInfoEntity; +import gr.cite.annotation.model.UserContactInfo; +import gr.cite.commons.web.authz.service.AuthorizationService; +import gr.cite.tools.data.query.FieldResolver; +import gr.cite.tools.data.query.QueryBase; +import gr.cite.tools.data.query.QueryContext; +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Predicate; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; + +@Component +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class UserContactInfoQuery extends QueryBase { + private Collection ids; + private Collection excludedIds; + private Collection excludedUserIds; + private Collection userIds; + private Collection values; + private Collection types; + + private EnumSet authorize = EnumSet.of(AuthorizationFlags.None); + + private final UserScope userScope; + private final AuthorizationService authService; + public UserContactInfoQuery(UserScope userScope, AuthorizationService authService) { + this.userScope = userScope; + this.authService = authService; + } + + public UserContactInfoQuery ids(UUID value) { + this.ids = List.of(value); + return this; + } + + public UserContactInfoQuery ids(UUID... value) { + this.ids = Arrays.asList(value); + return this; + } + + public UserContactInfoQuery ids(Collection values) { + this.ids = values; + return this; + } + + public UserContactInfoQuery excludedIds(Collection values) { + this.excludedIds = values; + return this; + } + + public UserContactInfoQuery excludedIds(UUID value) { + this.excludedIds = List.of(value); + return this; + } + + public UserContactInfoQuery excludedIds(UUID... value) { + this.excludedIds = Arrays.asList(value); + return this; + } + + public UserContactInfoQuery excludedUserIds(Collection values) { + this.excludedUserIds = values; + return this; + } + + public UserContactInfoQuery excludedUserIds(UUID value) { + this.excludedUserIds = List.of(value); + return this; + } + + public UserContactInfoQuery excludedUserIds(UUID... value) { + this.excludedUserIds = Arrays.asList(value); + return this; + } + + public UserContactInfoQuery userIds(UUID value) { + this.userIds = List.of(value); + return this; + } + + public UserContactInfoQuery userIds(UUID... value) { + this.userIds = Arrays.asList(value); + return this; + } + + public UserContactInfoQuery userIds(Collection values) { + this.userIds = values; + return this; + } + + public UserContactInfoQuery values(String value) { + this.values = List.of(value); + return this; + } + + public UserContactInfoQuery values(String... value) { + this.values = Arrays.asList(value); + return this; + } + + public UserContactInfoQuery values(Collection values) { + this.values = values; + return this; + } + + public UserContactInfoQuery types(ContactInfoType value) { + this.types = List.of(value); + return this; + } + + public UserContactInfoQuery types(ContactInfoType... value) { + this.types = Arrays.asList(value); + return this; + } + + public UserContactInfoQuery types(Collection values) { + this.types = values; + return this; + } + + public UserContactInfoQuery authorize(EnumSet values) { + this.authorize = values; + return this; + } + @Override + protected Boolean isFalseQuery() { + return + this.isEmpty(this.ids) || + this.isEmpty(this.userIds) || + this.isEmpty(this.excludedIds) || + this.isEmpty(this.values) || + this.isEmpty(this.excludedIds); + } + + @Override + protected Class entityClass() { + return UserContactInfoEntity.class; + } + + @Override + protected Predicate applyAuthZ(QueryContext queryContext) { + if (this.authorize.contains(AuthorizationFlags.None)) return null; + if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUser)) return null; + UUID userId; + if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe(); + else userId = null; + + List predicates = new ArrayList<>(); + if (userId != null) { + predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._userId)).value(userId)); + } + if (!predicates.isEmpty()) { + Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); + return queryContext.CriteriaBuilder.and(predicatesArray); + } else { + return queryContext.CriteriaBuilder.or(); //Creates a false query + } + } + + @Override + protected Predicate applyFilters(QueryContext queryContext) { + List predicates = new ArrayList<>(); + if (this.ids != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._id)); + for (UUID item : this.ids) + inClause.value(item); + predicates.add(inClause); + } + if (this.userIds != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._userId)); + for (UUID item : this.userIds) + inClause.value(item); + predicates.add(inClause); + } + if (this.excludedIds != null) { + CriteriaBuilder.In notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._id)); + for (UUID item : this.excludedIds) + notInClause.value(item); + predicates.add(notInClause.not()); + } + if (this.excludedUserIds != null) { + CriteriaBuilder.In notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._userId)); + for (UUID item : this.excludedUserIds) + notInClause.value(item); + predicates.add(notInClause.not()); + } + if (this.values != null) { + CriteriaBuilder.In inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._value)); + for (String item : this.values) + inClause.value(item); + predicates.add(inClause); + } + if (!predicates.isEmpty()) { + Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); + return queryContext.CriteriaBuilder.and(predicatesArray); + } else { + return null; + } + } + + @Override + protected String fieldNameOf(FieldResolver item) { + if (item.match(UserContactInfo._id)) return UserContactInfoEntity._id; + else if (item.match(UserContactInfo._value)) return UserContactInfoEntity._value; + else if (item.match(UserContactInfo._ordinal)) return UserContactInfoEntity._ordinal; + else if (item.prefix(UserContactInfo._user)) return UserContactInfoEntity._userId; + else if (item.match(UserContactInfo._user)) return UserContactInfoEntity._userId; + else if (item.match(UserContactInfo._type)) return UserContactInfoEntity._type; + else if (item.match(UserContactInfo._createdAt) ) return UserContactInfoEntity._createdAt; + else return null; + } + + @Override + protected UserContactInfoEntity convert(Tuple tuple, Set columns) { + UserContactInfoEntity item = new UserContactInfoEntity(); + item.setId(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._id, UUID.class)); + item.setValue(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._value, String.class)); + item.setType(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._type, ContactInfoType.class)); + item.setOrdinal(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._ordinal, Integer.class)); + item.setUserId(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._userId, UUID.class)); + item.setCreatedAt(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._createdAt, Instant.class)); + return item; + } + +} diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/annotation/AnnotationServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/annotation/AnnotationServiceImpl.java index 8dd30ef82..f9aa5815e 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/annotation/AnnotationServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/annotation/AnnotationServiceImpl.java @@ -7,6 +7,7 @@ import gr.cite.annotation.authorization.authorizationcontentresolver.Authorizati import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.scope.user.UserScope; import gr.cite.annotation.data.AnnotationEntity; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.model.Annotation; import gr.cite.annotation.model.builder.AnnotationBuilder; import gr.cite.annotation.model.deleter.AnnotationDeleter; @@ -22,7 +23,6 @@ import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; -import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; @@ -44,7 +44,7 @@ public class AnnotationServiceImpl implements AnnotationService { private final DeleterFactory deleterFactory; - private final EntityManager entityManager; + private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; @@ -57,7 +57,7 @@ public class AnnotationServiceImpl implements AnnotationService { public AnnotationServiceImpl( AuthorizationService authorizationService, DeleterFactory deleterFactory, - EntityManager entityManager, + TenantEntityManager entityManager, BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java index eb82cd50f..b9f1a37d3 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java @@ -1,6 +1,7 @@ package gr.cite.annotation.service.tenant; import com.fasterxml.jackson.core.JsonProcessingException; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.Permission; @@ -21,7 +22,6 @@ import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; -import jakarta.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -42,14 +42,14 @@ public class TenantServiceImpl implements TenantService { private final ConventionService conventionService; - private final EntityManager entityManager; + private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; public TenantServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, - EntityManager entityManager, + TenantEntityManager entityManager, BuilderFactory builderFactory) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenantconfiguration/TenantConfigurationServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenantconfiguration/TenantConfigurationServiceImpl.java index bae4060ac..3dec2e3bd 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenantconfiguration/TenantConfigurationServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenantconfiguration/TenantConfigurationServiceImpl.java @@ -10,7 +10,7 @@ import gr.cite.annotation.common.types.tenantconfiguration.DefaultUserLocaleConf import gr.cite.annotation.common.types.tenantconfiguration.EmailClientConfigurationDataContainer; import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.data.TenantConfigurationEntity; -import gr.cite.annotation.data.TenantScopedEntityManager; +import gr.cite.annotation.data.TenantEntityManager; import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.model.TenantConfiguration; import gr.cite.annotation.model.builder.TenantConfigurationBuilder; @@ -51,11 +51,11 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic private final MessageSource messageSource; private final BuilderFactory builderFactory; - private final TenantScopedEntityManager dbContext; + private final TenantEntityManager dbContext; private final DeleterFactory deleterFactory; @Autowired - public TenantConfigurationServiceImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, AuthorizationService authorizationService, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, BuilderFactory builderFactory, TenantScopedEntityManager dbContext, DeleterFactory deleterFactory) { + public TenantConfigurationServiceImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, AuthorizationService authorizationService, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, BuilderFactory builderFactory, TenantEntityManager dbContext, DeleterFactory deleterFactory) { this.applicationContext = applicationContext; this.jsonHandlingService = jsonHandlingService; this.authorizationService = authorizationService; diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java index 6567068b3..684e7ba7e 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java @@ -1,19 +1,23 @@ package gr.cite.annotation.service.user; import com.fasterxml.jackson.core.JsonProcessingException; -import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.Permission; import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.convention.ConventionService; -import gr.cite.annotation.data.UserContactInfoEntity; -import gr.cite.annotation.data.UserCredentialEntity; -import gr.cite.annotation.data.UserEntity; +import gr.cite.annotation.data.*; import gr.cite.annotation.integrationevent.inbox.usertouch.UserTouchedIntegrationEvent; import gr.cite.annotation.model.User; import gr.cite.annotation.model.builder.UserBuilder; +import gr.cite.annotation.model.deleter.TenantUserDeleter; +import gr.cite.annotation.model.deleter.UserContactInfoDeleter; +import gr.cite.annotation.model.deleter.UserCredentialDeleter; import gr.cite.annotation.model.deleter.UserDeleter; +import gr.cite.annotation.query.TenantUserQuery; +import gr.cite.annotation.query.UserContactInfoQuery; +import gr.cite.annotation.query.UserCredentialQuery; +import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.query.QueryFactory; @@ -25,16 +29,17 @@ import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; -import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; import java.time.Instant; +import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; @Service public class UserServiceImpl implements UserService { @@ -47,28 +52,28 @@ public class UserServiceImpl implements UserService { private final ConventionService conventionService; - private final EntityManager entityManager; + private final TenantEntityManager entityManager; private final BuilderFactory builderFactory; private final QueryFactory queryFactory; + private final JsonHandlingService jsonHandlingService; public UserServiceImpl(AuthorizationService authorizationService, DeleterFactory deleterFactory, ConventionService conventionService, - EntityManager entityManager, - BuilderFactory builderFactory, - QueryFactory queryFactory, + TenantEntityManager entityManager, + BuilderFactory builderFactory, QueryFactory queryFactory, JsonHandlingService jsonHandlingService) { this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; this.conventionService = conventionService; this.entityManager = entityManager; this.builderFactory = builderFactory; - this.queryFactory = queryFactory; - this.jsonHandlingService = jsonHandlingService; + this.queryFactory = queryFactory; + this.jsonHandlingService = jsonHandlingService; } @Override @@ -94,11 +99,6 @@ public class UserServiceImpl implements UserService { this.entityManager.persist(data); - for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) { - this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId())); - } - - this.entityManager.persist(this.buildUserCredentialEntityFromEventData(model)); } else { data.setName(model.getName()); data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile())); @@ -114,6 +114,12 @@ public class UserServiceImpl implements UserService { this.entityManager.flush(); + this.persistContactInfo(model.getUserContactInfo(), data.getId()); + this.persistUserCredential(model.getCredentials(), data.getId()); + this.persistTenantUser(model.getTenantUsers(), data.getId()); + + this.entityManager.flush(); + return this.builderFactory.builder(UserBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, User._id), data); } @@ -126,30 +132,92 @@ public class UserServiceImpl implements UserService { this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); } - private UserContactInfoEntity buildContactInfoEntityFromEventData(UserTouchedIntegrationEvent.UserContactInfo eventC, UUID userId) { - UserContactInfoEntity contactInfo = new UserContactInfoEntity(); - contactInfo.setId(UUID.randomUUID()); - contactInfo.setUserId(userId); - contactInfo.setType(eventC.getType()); - contactInfo.setValue(eventC.getValue()); - contactInfo.setOrdinal(eventC.getOrdinal()); - contactInfo.setCreatedAt(Instant.now()); - contactInfo.setUpdatedAt(Instant.now()); - contactInfo.setIsActive(IsActive.Active); + private void persistContactInfo(List models, UUID userId) throws InvalidApplicationException { + List items = this.queryFactory.query(UserContactInfoQuery.class) + .userIds(userId) + .collect(); + List updatedCreatedIds = new ArrayList<>(); + if (models != null) { + for (UserTouchedIntegrationEvent.UserContactInfo model : models) { + UserContactInfoEntity data = items.stream().filter(x -> x.getType().equals(model.getType()) && x.getValue().equals(model.getValue())).findFirst().orElse(null); + if (data == null) { + data = new UserContactInfoEntity(); + data.setId(UUID.randomUUID()); + data.setUserId(userId); + data.setType(model.getType()); + data.setValue(model.getValue()); + data.setOrdinal(model.getOrdinal()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + entityManager.persist(data); + } else { + data.setOrdinal(model.getOrdinal()); + entityManager.merge(data); + } + updatedCreatedIds.add(data.getId()); + } + } + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(UserContactInfoDeleter.class).delete(toDelete); - return contactInfo; + entityManager.flush(); } - private UserCredentialEntity buildUserCredentialEntityFromEventData(UserTouchedIntegrationEvent event) { - UserCredentialEntity credentialEntity = new UserCredentialEntity(); - credentialEntity.setId(UUID.randomUUID()); - credentialEntity.setUserId(event.getId()); - credentialEntity.setExternalId(event.getSubjectId()); - credentialEntity.setCreatedAt(Instant.now()); - credentialEntity.setUpdatedAt(Instant.now()); - credentialEntity.setIsActive(IsActive.Active); + private void persistUserCredential(List models, UUID userId) throws InvalidApplicationException { + List items = this.queryFactory.query(UserCredentialQuery.class) + .userIds(userId) + .isActive(IsActive.Active) + .collect(); + List updatedCreatedIds = new ArrayList<>(); + if (models != null) { + for (UserTouchedIntegrationEvent.UserCredential model : models) { + UserCredentialEntity data = items.stream().filter(x -> x.getExternalId().equals(model.getSubjectId())).findFirst().orElse(null); + if (data == null) { + data = new UserCredentialEntity(); + data.setId(UUID.randomUUID()); + data.setUserId(userId); + data.setExternalId(model.getSubjectId()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + entityManager.persist(data); + } + updatedCreatedIds.add(data.getId()); + } + } + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(UserCredentialDeleter.class).delete(toDelete); - return credentialEntity; + entityManager.flush(); + } + + private void persistTenantUser(List models, UUID userId) throws InvalidApplicationException { + List items = this.queryFactory.query(TenantUserQuery.class) + .userIds(userId) + .isActive(IsActive.Active) + .collect(); + List updatedCreatedIds = new ArrayList<>(); + if (models != null) { + for (UserTouchedIntegrationEvent.TenantUser model : models) { + TenantUserEntity data = items.stream().filter(x -> x.getTenantId().equals(model.getTenant())).findFirst().orElse(null); + if (data == null) { + data = new TenantUserEntity(); + data.setId(UUID.randomUUID()); + data.setUserId(userId); + data.setTenantId(model.getTenant()); + data.setCreatedAt(Instant.now()); + data.setUpdatedAt(Instant.now()); + data.setIsActive(IsActive.Active); + entityManager.persist(data); + } + updatedCreatedIds.add(data.getId()); + } + } + List toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList()); + deleterFactory.deleter(TenantUserDeleter.class).delete(toDelete); + + entityManager.flush(); } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java b/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java index 9089ce296..cd9435b72 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java +++ b/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java @@ -108,5 +108,13 @@ public class TenantEntityManager { .unwrap(Session.class) .disableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + + public EntityManager getEntityManager() { + return entityManager; + } + + public void setEntityManager(EntityManager entityManager) { + this.entityManager = entityManager; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/AppRabbitConfigurer.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/AppRabbitConfigurer.java index 2b72864d2..bc431d288 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/AppRabbitConfigurer.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/AppRabbitConfigurer.java @@ -21,11 +21,9 @@ public class AppRabbitConfigurer extends RabbitConfigurer { private ApplicationContext applicationContext; - private InboxProperties inboxProperties; - public AppRabbitConfigurer(ApplicationContext applicationContext, InboxProperties inboxProperties) { + public AppRabbitConfigurer(ApplicationContext applicationContext) { this.applicationContext = applicationContext; - this.inboxProperties = inboxProperties; } // @Bean diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java index bfdcc132a..630d25439 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxRepositoryImpl.java @@ -11,6 +11,7 @@ import gr.cite.queueinbox.entity.QueueInboxStatus; import gr.cite.queueinbox.repository.CandidateInfo; import gr.cite.queueinbox.repository.InboxRepository; import gr.cite.queueinbox.task.MessageOptions; +import gr.cite.rabbitmq.IntegrationEventMessageConstants; import gr.cite.rabbitmq.consumer.InboxCreatorParams; import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.QueryFactory; @@ -233,10 +234,10 @@ public class InboxRepositoryImpl implements InboxRepository { } private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) { - QueueInboxEntity queueMessage = new QueueInboxEntity(); queueMessage.setId(UUID.randomUUID()); - queueMessage.setTenantId(null); + Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null; + if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId); queueMessage.setExchange(this.inboxProperties.getExchange()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setQueue(inboxCreatorParams.getQueueName()); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java index 958a343de..98342b1fa 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxRepositoryImpl.java @@ -275,7 +275,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); if (queueOutboxMessages == null) { - logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", confirmedMessages.stream().map(UUID::toString).collect(Collectors.joining(","))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -319,7 +319,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { List queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); if (queueOutboxMessages == null) { - logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); + logger.warn("Could not lookup messages {} to process. Continuing...", nackedMessages.stream().map(UUID::toString).collect(Collectors.joining(","))); } else { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { @@ -349,7 +349,6 @@ public class OutboxRepositoryImpl implements OutboxRepository { public QueueOutbox create(IntegrationEvent item) { EntityTransaction transaction = null; EntityManager entityManager = null; - boolean success = false; QueueOutboxEntity queueMessage = null; try (FakeRequestScope ignored = new FakeRequestScope()) { try { @@ -369,7 +368,6 @@ public class OutboxRepositoryImpl implements OutboxRepository { logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); if (transaction != null) transaction.rollback(); - success = false; } finally { if (entityManager != null) entityManager.close(); @@ -453,7 +451,7 @@ public class OutboxRepositoryImpl implements OutboxRepository { QueueOutboxEntity queueMessage = new QueueOutboxEntity(); queueMessage.setId(UUID.randomUUID()); - queueMessage.setTenantId(null); + queueMessage.setTenantId(event.getTenantId()); queueMessage.setExchange(this.outboxProperties.getExchange()); queueMessage.setRoute(routingKey); queueMessage.setMessageId(event.getMessageId()); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxServiceImpl.java index 75805e7c6..ee76cea4e 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/OutboxServiceImpl.java @@ -14,17 +14,11 @@ import org.springframework.web.context.annotation.RequestScope; public class OutboxServiceImpl implements OutboxService { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxServiceImpl.class)); - private final OutboxProperties config; - private final JsonHandlingService jsonHandlingService; private final ApplicationEventPublisher eventPublisher; public OutboxServiceImpl( - OutboxProperties config, - JsonHandlingService jsonHandlingService, ApplicationEventPublisher eventPublisher ) { - this.config = config; - this.jsonHandlingService = jsonHandlingService; this.eventPublisher = eventPublisher; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandler.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandler.java index b5c82a3e0..486f03c34 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandler.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandler.java @@ -1,9 +1,10 @@ package eu.eudat.integrationevent.outbox.annotationentityremoval; +import javax.management.InvalidApplicationException; import java.util.UUID; public interface AnnotationEntityRemovalIntegrationEventHandler { - void handleDescription(UUID descriptionId); - void handleDmp(UUID dmpId); + void handleDescription(UUID descriptionId) throws InvalidApplicationException; + void handleDmp(UUID dmpId) throws InvalidApplicationException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandlerImpl.java index 7b40dcc79..971e66a5f 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentityremoval/AnnotationEntityRemovalIntegrationEventHandlerImpl.java @@ -1,6 +1,7 @@ package eu.eudat.integrationevent.outbox.annotationentityremoval; import eu.eudat.commons.enums.IsActive; +import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.data.DescriptionEntity; import eu.eudat.data.DmpUserEntity; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; @@ -17,6 +18,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -30,22 +32,25 @@ public class AnnotationEntityRemovalIntegrationEventHandlerImpl implements Annot private final OutboxService outboxService; private final QueryFactory queryFactory; + private final TenantScope tenantScope; - public AnnotationEntityRemovalIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) { + public AnnotationEntityRemovalIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory, TenantScope tenantScope) { this.outboxService = outboxService; this.queryFactory = queryFactory; + this.tenantScope = tenantScope; } - private void handle(AnnotationEntitiesRemovalIntegrationEvent event) { + private void handle(AnnotationEntitiesRemovalIntegrationEvent event) throws InvalidApplicationException { OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE); message.setEvent(event); + if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant()); this.outboxService.publish(message); } @Override - public void handleDescription(UUID descriptionId) { + public void handleDescription(UUID descriptionId) throws InvalidApplicationException { AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent(); event.setEntityIds(List.of(descriptionId)); @@ -53,7 +58,7 @@ public class AnnotationEntityRemovalIntegrationEventHandlerImpl implements Annot } @Override - public void handleDmp(UUID dmpId) { + public void handleDmp(UUID dmpId) throws InvalidApplicationException { List descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id)); AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent(); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentitytouch/AnnotationEntityTouchedIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentitytouch/AnnotationEntityTouchedIntegrationEventHandlerImpl.java index 6401ead08..a48c05bcb 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentitytouch/AnnotationEntityTouchedIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/annotationentitytouch/AnnotationEntityTouchedIntegrationEventHandlerImpl.java @@ -36,29 +36,31 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot this.queryFactory = queryFactory; } - private void handle(AnnotationEntitiesTouchedIntegrationEvent event) { + private void handle(AnnotationEntitiesTouchedIntegrationEvent event, UUID tenantId) { OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH); message.setEvent(event); + message.setTenantId(tenantId); this.outboxService.publish(message); } @Override public void handleDescription(UUID descriptionId) { - DescriptionEntity entity = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).firstAs(new BaseFieldSet().ensure(Description._dmp)); + DescriptionEntity entity = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).firstAs(new BaseFieldSet().ensure(Description._dmp).ensure(DescriptionEntity._tenantId)); if (entity == null) return; List dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(entity.getDmpId()).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user)); AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent(); event.setEvents(List.of(this.buildEventItem(descriptionId, dmpUsers))); - this.handle(event); + this.handle(event, entity.getTenantId()); } @Override public void handleDmp(UUID dmpId) { List descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id)); + if (descriptionEntities == null || descriptionEntities.isEmpty()) return; List dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user)); AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent(); @@ -67,7 +69,7 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot for (DescriptionEntity description : descriptionEntities) event.getEvents().add(this.buildEventItem(description.getId(), dmpUsers)); - this.handle(event); + this.handle(event, descriptionEntities.getFirst().getTenantId()); } private AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent buildEventItem(UUID entityId, List dmpUsers){ diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEvent.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEvent.java index f5e0e7a7f..c230deccf 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEvent.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEvent.java @@ -9,8 +9,6 @@ public class NotifyIntegrationEvent extends TrackedEvent { private UUID userId; - private UUID tenantId; - private UUID notificationType; private NotificationContactType contactTypeHint; @@ -32,14 +30,6 @@ public class NotifyIntegrationEvent extends TrackedEvent { this.userId = userId; } - public UUID getTenantId() { - return tenantId; - } - - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - public UUID getNotificationType() { return notificationType; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEventHandlerImpl.java index 6885f6418..2f673eb2d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/notification/NotifyIntegrationEventHandlerImpl.java @@ -1,5 +1,6 @@ package eu.eudat.integrationevent.outbox.notification; +import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxService; import gr.cite.tools.logging.LoggerService; @@ -8,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; +import javax.management.InvalidApplicationException; import java.util.UUID; @Component @@ -17,19 +19,22 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotifyIntegrationEventHandlerImpl.class)); private final OutboxService outboxService; + private final TenantScope tenantScope; @Autowired public NotifyIntegrationEventHandlerImpl( - OutboxService outboxService) { + OutboxService outboxService, TenantScope tenantScope) { this.outboxService = outboxService; + this.tenantScope = tenantScope; } @Override - public void handle(NotifyIntegrationEvent event) { + public void handle(NotifyIntegrationEvent event) throws InvalidApplicationException { OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.NOTIFY); message.setEvent(event); + if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant()); this.outboxService.publish(message); } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java index 59289e081..733854812 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java @@ -40,6 +40,7 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.TENANT_REMOVE); message.setEvent(event); + message.setTenantId(event.getId()); this.outboxService.publish(message); } @@ -55,6 +56,7 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn TenantRemovalIntegrationEvent event = new TenantRemovalIntegrationEvent(); event.setId(tenantId); message.setEvent(event); + message.setTenantId(event.getId()); this.outboxService.publish(message); } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java index 579b2e9c7..4ef529ac2 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java @@ -1,5 +1,7 @@ package eu.eudat.integrationevent.outbox.tenanttouched; +import javax.management.InvalidApplicationException; + public interface TenantTouchedIntegrationEventHandler { void handle(TenantTouchedIntegrationEvent event); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java index b7c2b53f0..06cd22296 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java @@ -1,10 +1,26 @@ package eu.eudat.integrationevent.outbox.tenanttouched; +import eu.eudat.commons.scope.tenant.TenantScope; +import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; +import eu.eudat.integrationevent.outbox.OutboxService; +import java.util.UUID; + public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler { + private final OutboxService outboxService; - @Override + public TenantTouchedIntegrationEventHandlerImpl(OutboxService outboxService) { + this.outboxService = outboxService; + } + + @Override public void handle(TenantTouchedIntegrationEvent event) { + OutboxIntegrationEvent message = new OutboxIntegrationEvent(); + message.setMessageId(UUID.randomUUID()); + message.setType(OutboxIntegrationEvent.TENANT_TOUCH); + message.setTenantId(event.getId()); + message.setEvent(event); + this.outboxService.publish(message); } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEvent.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEvent.java index e994aac74..9a3cd8ed1 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEvent.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEvent.java @@ -8,8 +8,6 @@ public class UserRemovalIntegrationEvent extends TrackedEvent { private UUID userId; - private UUID tenant; - public UUID getUserId() { return userId; } @@ -18,11 +16,4 @@ public class UserRemovalIntegrationEvent extends TrackedEvent { this.userId = userId; } - public UUID getTenant() { - return tenant; - } - - public void setTenant(UUID tenant) { - this.tenant = tenant; - } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandler.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandler.java index a1aa0131f..9a21f9d2a 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandler.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandler.java @@ -1,9 +1,10 @@ package eu.eudat.integrationevent.outbox.userremoval; +import javax.management.InvalidApplicationException; import java.util.UUID; public interface UserRemovalIntegrationEventHandler { - void handle(UUID userId); + void handle(UUID userId) throws InvalidApplicationException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java index 89155c131..d2c37b5a2 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -1,5 +1,6 @@ package eu.eudat.integrationevent.outbox.userremoval; +import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.data.UserEntity; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxService; @@ -14,6 +15,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.util.UUID; @Component("outboxuserremovalintegrationeventhandler") @@ -25,14 +27,16 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr private final OutboxService outboxService; private final ApplicationContext applicationContext; + private final TenantScope tenantScope; - public UserRemovalIntegrationEventHandlerImpl(OutboxService outboxService, ApplicationContext applicationContext) { + public UserRemovalIntegrationEventHandlerImpl(OutboxService outboxService, ApplicationContext applicationContext, TenantScope tenantScope) { this.outboxService = outboxService; this.applicationContext = applicationContext; + this.tenantScope = tenantScope; } @Override - public void handle(UUID userId) { + public void handle(UUID userId) throws InvalidApplicationException { UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); if (!userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(userId))) return; @@ -40,9 +44,9 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.USER_REMOVE); + if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant()); UserRemovalIntegrationEvent event = new UserRemovalIntegrationEvent(); event.setUserId(userId); - event.setTenant(null); //TODO message.setEvent(event); this.outboxService.publish(message); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java index 9bd56130b..a752bffa1 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEvent.java @@ -2,6 +2,7 @@ package eu.eudat.integrationevent.outbox.usertouched; import eu.eudat.commons.enums.ContactInfoType; import eu.eudat.integrationevent.TrackedEvent; +import eu.eudat.model.UserContactInfo; import java.util.List; import java.util.UUID; @@ -10,15 +11,13 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { private UUID id; - private UUID tenant; - private String name; - private String subjectId; - private UserProfile profile; private List userContactInfo; + private List tenantUsers; + private List credentials; public UUID getId() { return id; @@ -28,14 +27,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.id = id; } - public UUID getTenant() { - return tenant; - } - - public void setTenant(UUID tenant) { - this.tenant = tenant; - } - public String getName() { return name; } @@ -44,13 +35,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.name = name; } - public String getSubjectId() { - return subjectId; - } - - public void setSubjectId(String subjectId) { - this.subjectId = subjectId; - } public UserProfile getProfile() { return profile; @@ -68,6 +52,22 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { this.userContactInfo = userContactInfo; } + public List getTenantUsers() { + return tenantUsers; + } + + public void setTenantUsers(List tenantUsers) { + this.tenantUsers = tenantUsers; + } + + public List getCredentials() { + return credentials; + } + + public void setCredentials(List credentials) { + this.credentials = credentials; + } + public static class UserProfile { private String timezone; @@ -133,4 +133,32 @@ public class UserTouchedIntegrationEvent extends TrackedEvent { } } + + + + public static class UserCredential { + private String subjectId; + + public String getSubjectId() { + return subjectId; + } + + public void setSubjectId(String subjectId) { + this.subjectId = subjectId; + } + } + + public static class TenantUser { + + private UUID tenant; + + public UUID getTenant() { + return tenant; + } + + public void setTenant(UUID tenant) { + this.tenant = tenant; + } + } + } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandler.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandler.java index a4004384d..b32a440e8 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandler.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandler.java @@ -1,9 +1,10 @@ package eu.eudat.integrationevent.outbox.usertouched; +import javax.management.InvalidApplicationException; import java.util.UUID; public interface UserTouchedIntegrationEventHandler { - void handle(UUID userId); + void handle(UUID userId) throws InvalidApplicationException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java index b80078437..17b133c3d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java @@ -1,20 +1,18 @@ package eu.eudat.integrationevent.outbox.usertouched; import eu.eudat.commons.JsonHandlingService; +import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.commons.types.user.AdditionalInfoEntity; -import eu.eudat.data.UserContactInfoEntity; -import eu.eudat.data.UserCredentialEntity; -import eu.eudat.data.UserEntity; +import eu.eudat.data.*; +import eu.eudat.elastic.data.DmpElasticEntity; +import eu.eudat.elastic.elasticbuilder.DmpElasticBuilder; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxService; -import eu.eudat.model.Reference; -import eu.eudat.model.User; -import eu.eudat.model.UserContactInfo; -import eu.eudat.model.UserCredential; +import eu.eudat.model.*; import eu.eudat.model.builder.UserAdditionalInfoBuilder; -import eu.eudat.query.UserContactInfoQuery; -import eu.eudat.query.UserCredentialQuery; -import eu.eudat.query.UserQuery; +import eu.eudat.query.*; +import gr.cite.tools.data.query.Ordering; +import gr.cite.tools.data.query.Paging; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.fieldset.BaseFieldSet; @@ -24,8 +22,10 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.stereotype.Component; +import javax.management.InvalidApplicationException; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -39,58 +39,89 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr private final OutboxService outboxService; private final JsonHandlingService jsonHandlingService; private final MessageSource messageSource; + private final TenantScope tenantScope; + private final TenantEntityManager entityManager; private final QueryFactory queryFactory; public UserTouchedIntegrationEventHandlerImpl( - OutboxService outboxService, JsonHandlingService jsonHandlingService, MessageSource messageSource, QueryFactory queryFactory) { + OutboxService outboxService, JsonHandlingService jsonHandlingService, MessageSource messageSource, TenantScope tenantScope, TenantEntityManager entityManager, QueryFactory queryFactory) { this.outboxService = outboxService; this.jsonHandlingService = jsonHandlingService; this.messageSource = messageSource; + this.tenantScope = tenantScope; + this.entityManager = entityManager; this.queryFactory = queryFactory; } @Override - public void handle(UUID userId) { + public void handle(UUID userId) throws InvalidApplicationException { OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); message.setType(OutboxIntegrationEvent.USER_TOUCH); + if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant()); - UserEntity user = this.queryFactory.query(UserQuery.class).ids(userId) - .firstAs(new BaseFieldSet().ensure(User._name).ensure(User._additionalInfo)); - if (user == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale())); + try { + this.entityManager.disableTenantFilters(); - List userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId) - .collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value)); + UserEntity user = this.queryFactory.query(UserQuery.class).ids(userId) + .firstAs(new BaseFieldSet().ensure(User._name).ensure(User._additionalInfo)); + if (user == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale())); - List userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(userId) - .collectAs(new BaseFieldSet().ensure(UserCredential._id, UserCredential._externalId)); + List userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId) + .collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value)); - UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent(); - event.setId(userId); - event.setTenant(null); //TODO - event.setName(user.getName()); - if (userCredentialEntities != null && !userCredentialEntities.isEmpty()) - event.setSubjectId(userCredentialEntities.getFirst().getExternalId()); - event.setProfile(new UserTouchedIntegrationEvent.UserProfile()); - AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo()); - if (definition != null) { - event.getProfile().setCulture(definition.getCulture()); - event.getProfile().setLanguage(definition.getLanguage()); - event.getProfile().setTimezone(definition.getTimezone()); - } + List userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(userId) + .collectAs(new BaseFieldSet().ensure(UserCredential._id, UserCredential._externalId)); + + List tenantUserEntities = this.queryFactory.query(TenantUserQuery.class).userIds(userId) + .collectAs(new BaseFieldSet().ensure(TenantUser._id, TenantUser._tenant)); + + UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent(); + event.setId(userId); + event.setName(user.getName()); + if (userCredentialEntities != null && !userCredentialEntities.isEmpty()) { + event.setCredentials(new ArrayList<>()); + for (UserCredentialEntity userCredential : userCredentialEntities){ + UserTouchedIntegrationEvent.UserCredential userCredentialEvent = new UserTouchedIntegrationEvent.UserCredential(); + userCredentialEvent.setSubjectId(userCredential.getExternalId()); + event.getCredentials().add(userCredentialEvent); + } + } + + if (tenantUserEntities != null && !tenantUserEntities.isEmpty()) { + event.setTenantUsers(new ArrayList<>()); + for (TenantUserEntity tenantUserEntity : tenantUserEntities){ + UserTouchedIntegrationEvent.TenantUser tenantUser = new UserTouchedIntegrationEvent.TenantUser(); + tenantUser.setTenant(tenantUserEntity.getTenantId()); + event.getTenantUsers().add(tenantUser); + } + } + + AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo()); + if (definition != null) { + event.setProfile(new UserTouchedIntegrationEvent.UserProfile()); + event.getProfile().setCulture(definition.getCulture()); + event.getProfile().setLanguage(definition.getLanguage()); + event.getProfile().setTimezone(definition.getTimezone()); + } + + if (userContactInfoEntities != null&& !userContactInfoEntities.isEmpty()){ + event.setUserContactInfo(new ArrayList<>()); + for (UserContactInfoEntity contactInfoEntity : userContactInfoEntities){ + UserTouchedIntegrationEvent.UserContactInfo contactInfo = new UserTouchedIntegrationEvent.UserContactInfo(); + contactInfo.setType(contactInfoEntity.getType()); + contactInfo.setValue(contactInfoEntity.getValue()); + contactInfo.setOrdinal(contactInfoEntity.getOrdinal()); + event.getUserContactInfo().add(contactInfo); + } + } + + message.setEvent(event); + }finally { + this.entityManager.enableTenantFilters(); + } - event.setUserContactInfo(new ArrayList<>()); - if (userContactInfoEntities != null){ - for (UserContactInfoEntity contactInfoEntity : userContactInfoEntities){ - UserTouchedIntegrationEvent.UserContactInfo contactInfo = new UserTouchedIntegrationEvent.UserContactInfo(); - contactInfo.setType(contactInfoEntity.getType()); - contactInfo.setValue(contactInfoEntity.getValue()); - contactInfo.setOrdinal(contactInfoEntity.getOrdinal()); - event.getUserContactInfo().add(contactInfo); - } - } - message.setEvent(event); this.outboxService.publish(message); } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java index 0bb0ecf7d..8169f896b 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/DescriptionQuery.java @@ -316,6 +316,7 @@ public class DescriptionQuery extends QueryBase { else if (item.prefix(Description._dmp)) return DescriptionEntity._dmpId; else if (item.match(Description._dmp)) return DescriptionEntity._dmpId; else if (item.match(Description._belongsToCurrentTenant)) return DescriptionEntity._tenantId; + else if (item.match(DescriptionEntity._tenantId)) return DescriptionEntity._tenantId; else return null; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/StorageFileQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/StorageFileQuery.java index 1b88a00ae..f3b8ff108 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/StorageFileQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/StorageFileQuery.java @@ -164,16 +164,25 @@ public class StorageFileQuery extends QueryBase { predicates.add(queryContext.CriteriaBuilder.greaterThan(queryContext.Root.get(StorageFileEntity._createdAt), this.createdAfter)); } if (this.canPurge != null) { - predicates.add( - queryContext.CriteriaBuilder.and( - queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgeAt)).not(), - queryContext.CriteriaBuilder.lessThan(queryContext.Root.get(StorageFileEntity._purgeAt), Instant.now()) - ) - ); + if (this.canPurge) { + predicates.add( + queryContext.CriteriaBuilder.and( + queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(StorageFileEntity._purgeAt)), + queryContext.CriteriaBuilder.lessThan(queryContext.Root.get(StorageFileEntity._purgeAt), Instant.now()) + ) + ); + } else { + predicates.add( + queryContext.CriteriaBuilder.or( + queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgeAt)), + queryContext.CriteriaBuilder.greaterThan(queryContext.Root.get(StorageFileEntity._purgeAt), Instant.now()) + ) + ); + } } if (this.isPurged != null) { - if (!this.isPurged){ - predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgedAt)).not()); + if (this.isPurged){ + predicates.add(queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(StorageFileEntity._purgedAt))); } else { predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgedAt))); } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java index 0e2d38b6b..bc7c086ba 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/descriptiontemplate/DescriptionTemplateServiceImpl.java @@ -260,7 +260,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate, NotificationContactType type) throws InvalidApplicationException { NotifyIntegrationEvent event = new NotifyIntegrationEvent(); - event.setTenantId(tenantScope.getTenant()); event.setUserId(userScope.getUserIdSafe()); UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId()); diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java b/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java index 7ff2c597a..0a6161186 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/storage/StorageFileCleanupTask.java @@ -3,8 +3,11 @@ package eu.eudat.service.storage; import eu.eudat.commons.fake.FakeRequestScope; import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.data.StorageFileEntity; +import eu.eudat.data.TenantEntity; +import eu.eudat.data.TenantEntityManager; import eu.eudat.model.StorageFile; import eu.eudat.query.StorageFileQuery; +import eu.eudat.query.TenantQuery; import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; @@ -19,6 +22,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.io.Closeable; import java.io.IOException; @@ -115,10 +119,8 @@ public class StorageFileCleanupTask implements Closeable, ApplicationListener query = criteriaBuilder.createQuery(UserEntity.class); + CriteriaQuery query = criteriaBuilder.createQuery(TenantEntity.class); Root root = query.from(TenantEntity.class); query = query.where( criteriaBuilder.and( @@ -150,7 +150,7 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor { criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) ) ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); - List results = this.entityManager.createQuery(query).getResultList(); + List results = this.entityManager.createQuery(query).getResultList(); if (results.size() == 1) { return results.getFirst().getId(); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/interceptors/user/UserInterceptor.java b/dmp-backend/web/src/main/java/eu/eudat/interceptors/user/UserInterceptor.java index 282413e60..7d68d7499 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/interceptors/user/UserInterceptor.java +++ b/dmp-backend/web/src/main/java/eu/eudat/interceptors/user/UserInterceptor.java @@ -43,6 +43,7 @@ import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; +import javax.management.InvalidApplicationException; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -93,7 +94,7 @@ public class UserInterceptor implements WebRequestInterceptor { } @Override - public void preHandle(WebRequest request) throws InterruptedException { + public void preHandle(WebRequest request) throws InterruptedException, InvalidApplicationException { UUID userId = null; if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());