From 0472e96c6df6d0b4b19ce8c9340f3dd4e7f8ac49 Mon Sep 17 00:00:00 2001 From: amentis Date: Thu, 30 May 2024 16:22:08 +0300 Subject: [PATCH 1/2] add tenant persist code validation pattern --- .../org/opencdmp/model/persist/TenantPersist.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/TenantPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/TenantPersist.java index 87279c56b..033f6ce5f 100644 --- a/backend/core/src/main/java/org/opencdmp/model/persist/TenantPersist.java +++ b/backend/core/src/main/java/org/opencdmp/model/persist/TenantPersist.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.List; import java.util.UUID; +import java.util.regex.Pattern; public class TenantPersist { @@ -115,6 +116,10 @@ public class TenantPersist { .iff(() -> !this.isEmpty(item.getCode())) .must(() -> this.lessEqualLength(item.getCode(), TenantEntity._codeLength)) .failOn(TenantPersist._code).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{TenantPersist._code}, LocaleContextHolder.getLocale())), + this.spec() + .iff(() -> !this.isEmpty(item.getCode())) + .must(() -> this.validateCodePattern(item.getCode())) + .failOn(TenantPersist._code).failWith(messageSource.getMessage("Validation_UnexpectedValue", new Object[]{TenantPersist._code}, LocaleContextHolder.getLocale())), this.spec() .must(() -> !this.isEmpty(item.getName())) .failOn(TenantPersist._name).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantPersist._name}, LocaleContextHolder.getLocale())), @@ -128,6 +133,13 @@ public class TenantPersist { ); } + + private boolean validateCodePattern(String code){ + if (this.isEmpty(code)) return false; + + Pattern pattern = Pattern.compile("^[a-z0-9_]*$"); + return pattern.matcher(code).matches(); + } } } From 9a8238ac73dafd953541e175800b177c4771ce9c Mon Sep 17 00:00:00 2001 From: sgiannopoulos Date: Thu, 30 May 2024 17:10:53 +0300 Subject: [PATCH 2/2] tenant fixes --- .../annotation/data/TenantEntityManager.java | 18 +++++++-- .../data/tenant/TenantListener.java | 7 +++- ...iesRemovalIntegrationEventHandlerImpl.java | 28 ++++---------- ...serTouchedIntegrationEventHandlerImpl.java | 6 +-- .../opencdmp/data/TenantEntityManager.java | 34 +++++++++++------ .../org/opencdmp/data/TenantUserEntity.java | 15 ++++---- .../opencdmp/data/tenant/TenantListener.java | 10 ++++- .../model/persist/UserRolePatchPersist.java | 14 +++---- .../service/tenant/TenantServiceImpl.java | 38 +++++++++++++------ .../service/user/UserServiceImpl.java | 13 +++++++ .../data/TenantEntityManager.java | 27 ++++++++----- .../data/tenant/TenantListener.java | 26 ++++++++----- 12 files changed, 147 insertions(+), 89 deletions(-) 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 index f0cf76365..438d5fcc9 100644 --- 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 @@ -21,10 +21,13 @@ public class TenantEntityManager { private EntityManager entityManager; private final TenantScope tenantScope; private final ErrorThesaurusProperties errors; + + boolean tenantFiltersDisabled; public TenantEntityManager(TenantScope tenantScope, ErrorThesaurusProperties errors) { this.tenantScope = tenantScope; this.errors = errors; + this.tenantFiltersDisabled = false; } @@ -33,7 +36,7 @@ public class TenantEntityManager { } public T merge(T entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (!this.tenantScope.isDefaultTenant()) { if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } else if (tenantScopedEntity.getTenantId() != null) { @@ -44,7 +47,7 @@ public class TenantEntityManager { } public void remove(Object entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (!this.tenantScope.isDefaultTenant()) { if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } else if (tenantScopedEntity.getTenantId() != null) { @@ -57,7 +60,7 @@ public class TenantEntityManager { public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { T entity = this.entityManager.find(entityClass, primaryKey); - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) return null; } return entity; @@ -66,7 +69,7 @@ public class TenantEntityManager { public T find(Class entityClass, Object primaryKey, boolean disableTracking) throws InvalidApplicationException { T entity = this.entityManager.find(entityClass, primaryKey); - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) return null; } if (disableTracking) this.entityManager.detach(entity); @@ -106,6 +109,7 @@ public class TenantEntityManager { .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + this.tenantFiltersDisabled = false; } public void loadExplictTenantFilters() throws InvalidApplicationException { @@ -123,6 +127,7 @@ public class TenantEntityManager { .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + this.tenantFiltersDisabled = false; } public void disableTenantFilters() { @@ -137,6 +142,11 @@ public class TenantEntityManager { this.entityManager .unwrap(Session.class) .disableFilter(TenantScopedBaseEntity.TENANT_FILTER_EXPLICT); + this.tenantFiltersDisabled = true; + } + + public boolean isTenantFiltersDisabled() { + return tenantFiltersDisabled; } public EntityManager getEntityManager() { 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 ed3af35f1..fbf00d649 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.data.TenantEntityManager; import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; @@ -19,18 +20,21 @@ public class TenantListener { private final TenantScope tenantScope; private final ErrorThesaurusProperties errors; + private final TenantEntityManager tenantEntityManager; @Autowired public TenantListener( - TenantScope tenantScope, ErrorThesaurusProperties errors + TenantScope tenantScope, ErrorThesaurusProperties errors, TenantEntityManager tenantEntityManager ) { this.tenantScope = tenantScope; this.errors = errors; + this.tenantEntityManager = tenantEntityManager; } @PrePersist public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { + if (this.tenantEntityManager.isTenantFiltersDisabled()) return; if (tenantScope.isMultitenant()) { if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) { logger.error("somebody tried to set not login tenant"); @@ -48,6 +52,7 @@ public class TenantListener { @PreUpdate @PreRemove public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { + if (this.tenantEntityManager.isTenantFiltersDisabled()) return; if (tenantScope.isMultitenant()) { if (!tenantScope.isDefaultTenant()) { if (entity.getTenantId() == null) { 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 72a04485e..2f779e78f 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 @@ -74,24 +74,11 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann EventProcessingStatus status = EventProcessingStatus.Success; try { - if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) { - TenantEntity tenant = queryFactory.query(TenantQuery.class).disableTracking().ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); - if (tenant == null) { - logger.error("missing tenant from event message"); - return EventProcessingStatus.Error; - } - this.tenantScope.setTempTenant(tenantEntityManager, properties.getTenantId(), tenant.getCode()); - } else if (this.tenantScope.isMultitenant()) { -// logger.error("missing tenant from event message"); -// return EventProcessingStatus.Error; - this.tenantScope.setTempTenant(tenantEntityManager, null, this.tenantScope.getDefaultTenantCode()); - } - + tenantEntityManager.disableTenantFilters(); currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties)); - tenantEntityManager.disableTenantFilters(); EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); List items = entityUserQuery .entityIds(event.getEntityIds()) @@ -101,7 +88,8 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(items); - + tenantEntityManager.flush(); + auditService.track(AuditableAction.User_Persist, Map.ofEntries( new AbstractMap.SimpleEntry("model", event) )); @@ -112,11 +100,11 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); } finally { currentPrincipalResolver.pop(); - try { - tenantScope.removeTempTenant(this.tenantEntityManager); - this.tenantEntityManager.reloadTenantFilters(); - } catch (InvalidApplicationException e) { - } + try { + tenantEntityManager.reloadTenantFilters(); + } catch (InvalidApplicationException ex) { + logger.error(ex.getMessage(), ex); + } } return status; } diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java index 9d6db50fb..e52f414ac 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouched/UserTouchedIntegrationEventHandlerImpl.java @@ -32,8 +32,6 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr private final JsonHandlingService jsonHandlingService; private final ValidatorFactory validatorFactory; - private final QueryFactory queryFactory; - private final TenantScope tenantScope; private final CurrentPrincipalResolver currentPrincipalResolver; private final ClaimExtractorProperties claimExtractorProperties; private final UserService userService; @@ -42,11 +40,9 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr public UserTouchedIntegrationEventHandlerImpl( JsonHandlingService jsonHandlingService, - ValidatorFactory validatorFactory, QueryFactory queryFactory, TenantScope tenantScope, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager) { + ValidatorFactory validatorFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, UserService userService, AuditService auditService, TenantEntityManager tenantEntityManager) { this.jsonHandlingService = jsonHandlingService; this.validatorFactory = validatorFactory; - this.queryFactory = queryFactory; - this.tenantScope = tenantScope; this.currentPrincipalResolver = currentPrincipalResolver; this.claimExtractorProperties = claimExtractorProperties; this.userService = userService; diff --git a/backend/core/src/main/java/org/opencdmp/data/TenantEntityManager.java b/backend/core/src/main/java/org/opencdmp/data/TenantEntityManager.java index 1361edf14..992b2aa8d 100644 --- a/backend/core/src/main/java/org/opencdmp/data/TenantEntityManager.java +++ b/backend/core/src/main/java/org/opencdmp/data/TenantEntityManager.java @@ -22,9 +22,12 @@ public class TenantEntityManager { private final TenantScope tenantScope; private final ErrorThesaurusProperties errors; + boolean tenantFiltersDisabled; + public TenantEntityManager(TenantScope tenantScope, ErrorThesaurusProperties errors) { this.tenantScope = tenantScope; this.errors = errors; + this.tenantFiltersDisabled = false; } @@ -33,7 +36,7 @@ public class TenantEntityManager { } public T merge(T entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (!this.tenantScope.isDefaultTenant()) { if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } else if (tenantScopedEntity.getTenantId() != null) { @@ -44,7 +47,7 @@ public class TenantEntityManager { } public void remove(Object entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (!this.tenantScope.isDefaultTenant()) { if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } else if (tenantScopedEntity.getTenantId() != null) { @@ -57,7 +60,7 @@ public class TenantEntityManager { public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { T entity = this.entityManager.find(entityClass, primaryKey); - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) return null; } return entity; @@ -66,7 +69,7 @@ public class TenantEntityManager { public T find(Class entityClass, Object primaryKey, boolean disableTracking) throws InvalidApplicationException { T entity = this.entityManager.find(entityClass, primaryKey); - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) return null; } if (disableTracking) this.entityManager.detach(entity); @@ -90,13 +93,13 @@ public class TenantEntityManager { public void clear() { this.entityManager.clear(); } - + public void reloadTenantFilters() throws InvalidApplicationException { this.disableTenantFilters(); - + if (!this.tenantScope.isSet()) return; - - if(!this.tenantScope.isDefaultTenant()) { + + if (!this.tenantScope.isDefaultTenant()) { this.entityManager .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) @@ -106,6 +109,7 @@ public class TenantEntityManager { .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + this.tenantFiltersDisabled = false; } public void loadExplictTenantFilters() throws InvalidApplicationException { @@ -113,7 +117,7 @@ public class TenantEntityManager { if (!this.tenantScope.isSet()) return; - if(!this.tenantScope.isDefaultTenant()) { + if (!this.tenantScope.isDefaultTenant()) { this.entityManager .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.TENANT_FILTER_EXPLICT) @@ -123,9 +127,10 @@ public class TenantEntityManager { .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + this.tenantFiltersDisabled = false; } - public void disableTenantFilters(){ + public void disableTenantFilters() { this.entityManager .unwrap(Session.class) .disableFilter(TenantScopedBaseEntity.TENANT_FILTER); @@ -137,8 +142,13 @@ public class TenantEntityManager { this.entityManager .unwrap(Session.class) .disableFilter(TenantScopedBaseEntity.TENANT_FILTER_EXPLICT); + this.tenantFiltersDisabled = true; } - + + public boolean isTenantFiltersDisabled() { + return this.tenantFiltersDisabled; + } + public EntityManager getEntityManager() { return this.entityManager; } @@ -146,5 +156,5 @@ public class TenantEntityManager { public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } - } + diff --git a/backend/core/src/main/java/org/opencdmp/data/TenantUserEntity.java b/backend/core/src/main/java/org/opencdmp/data/TenantUserEntity.java index 5223ed731..d83920fe8 100644 --- a/backend/core/src/main/java/org/opencdmp/data/TenantUserEntity.java +++ b/backend/core/src/main/java/org/opencdmp/data/TenantUserEntity.java @@ -1,9 +1,10 @@ package org.opencdmp.data; +import jakarta.persistence.*; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.data.converters.enums.IsActiveConverter; import org.opencdmp.data.tenant.TenantScopedBaseEntity; -import jakarta.persistence.*; + import java.time.Instant; import java.util.UUID; @@ -37,7 +38,7 @@ public class TenantUserEntity extends TenantScopedBaseEntity { public final static String _updatedAt = "updatedAt"; public UUID getId() { - return id; + return this.id; } public void setId(UUID id) { @@ -45,7 +46,7 @@ public class TenantUserEntity extends TenantScopedBaseEntity { } public UUID getUserId() { - return userId; + return this.userId; } public void setUserId(UUID userId) { @@ -53,7 +54,7 @@ public class TenantUserEntity extends TenantScopedBaseEntity { } public UUID getTenantId() { - return tenantId; + return this.tenantId; } public void setTenantId(UUID tenantId) { @@ -61,7 +62,7 @@ public class TenantUserEntity extends TenantScopedBaseEntity { } public IsActive getIsActive() { - return isActive; + return this.isActive; } public void setIsActive(IsActive isActive) { @@ -69,7 +70,7 @@ public class TenantUserEntity extends TenantScopedBaseEntity { } public Instant getCreatedAt() { - return createdAt; + return this.createdAt; } public void setCreatedAt(Instant createdAt) { @@ -77,7 +78,7 @@ public class TenantUserEntity extends TenantScopedBaseEntity { } public Instant getUpdatedAt() { - return updatedAt; + return this.updatedAt; } public void setUpdatedAt(Instant updatedAt) { diff --git a/backend/core/src/main/java/org/opencdmp/data/tenant/TenantListener.java b/backend/core/src/main/java/org/opencdmp/data/tenant/TenantListener.java index 5c8086146..4bd8ffff5 100644 --- a/backend/core/src/main/java/org/opencdmp/data/tenant/TenantListener.java +++ b/backend/core/src/main/java/org/opencdmp/data/tenant/TenantListener.java @@ -8,6 +8,7 @@ import jakarta.persistence.PreRemove; import jakarta.persistence.PreUpdate; import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.tenant.TenantScoped; +import org.opencdmp.data.TenantEntityManager; import org.opencdmp.errorcode.ErrorThesaurusProperties; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -20,18 +21,21 @@ public class TenantListener { private final TenantScope tenantScope; private final ErrorThesaurusProperties errors; + private final TenantEntityManager tenantEntityManager; @Autowired public TenantListener( - TenantScope tenantScope, ErrorThesaurusProperties errors + TenantScope tenantScope, ErrorThesaurusProperties errors, TenantEntityManager tenantEntityManager ) { this.tenantScope = tenantScope; this.errors = errors; + this.tenantEntityManager = tenantEntityManager; } @PrePersist public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { + if (this.tenantEntityManager.isTenantFiltersDisabled()) return; if (this.tenantScope.isMultitenant()) { if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(this.tenantScope.getTenant()) != 0)) { logger.error("somebody tried to set not login tenant"); @@ -49,6 +53,7 @@ public class TenantListener { @PreUpdate @PreRemove public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { + if (this.tenantEntityManager.isTenantFiltersDisabled()) return; if (this.tenantScope.isMultitenant()) { if (!this.tenantScope.isDefaultTenant()) { if (entity.getTenantId() == null) { @@ -74,6 +79,7 @@ public class TenantListener { throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } } - + } } + diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/UserRolePatchPersist.java b/backend/core/src/main/java/org/opencdmp/model/persist/UserRolePatchPersist.java index 57606a2aa..9c7fe6d2d 100644 --- a/backend/core/src/main/java/org/opencdmp/model/persist/UserRolePatchPersist.java +++ b/backend/core/src/main/java/org/opencdmp/model/persist/UserRolePatchPersist.java @@ -1,7 +1,7 @@ package org.opencdmp.model.persist; -import org.opencdmp.commons.validation.BaseValidator; import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.validation.BaseValidator; import org.opencdmp.convention.ConventionService; import org.opencdmp.errorcode.ErrorThesaurusProperties; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -27,7 +27,7 @@ public class UserRolePatchPersist { public static final String _hash = "hash"; public UUID getId() { - return id; + return this.id; } public void setId(UUID id) { @@ -35,7 +35,7 @@ public class UserRolePatchPersist { } public List getRoles() { - return roles; + return this.roles; } public void setRoles(List roles) { @@ -43,7 +43,7 @@ public class UserRolePatchPersist { } public String getHash() { - return hash; + return this.hash; } public void setHash(String hash) { @@ -74,14 +74,14 @@ public class UserRolePatchPersist { this.spec() .iff(() -> this.isValidGuid(item.getId())) .must(() -> this.isValidHash(item.getHash())) - .failOn(UserRolePatchPersist._hash).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserRolePatchPersist._hash}, LocaleContextHolder.getLocale())), + .failOn(UserRolePatchPersist._hash).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UserRolePatchPersist._hash}, LocaleContextHolder.getLocale())), this.spec() .iff(() -> !this.isValidGuid(item.getId())) .must(() -> !this.isValidHash(item.getHash())) - .failOn(UserRolePatchPersist._hash).failWith(messageSource.getMessage("Validation_OverPosting", new Object[]{}, LocaleContextHolder.getLocale())), + .failOn(UserRolePatchPersist._hash).failWith(this.messageSource.getMessage("Validation_OverPosting", new Object[]{}, LocaleContextHolder.getLocale())), this.spec() .must(() -> !this.isListNullOrEmpty(item.getRoles())) - .failOn(UserRolePatchPersist._roles).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserRolePatchPersist._roles}, LocaleContextHolder.getLocale())) + .failOn(UserRolePatchPersist._roles).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{UserRolePatchPersist._roles}, LocaleContextHolder.getLocale())) ); } } diff --git a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java index 5a0e845dc..ac5dac181 100644 --- a/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/tenant/TenantServiceImpl.java @@ -23,15 +23,13 @@ import org.opencdmp.authorization.Permission; import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.convention.ConventionService; -import org.opencdmp.data.TenantEntity; -import org.opencdmp.data.TenantEntityManager; -import org.opencdmp.data.UserCredentialEntity; -import org.opencdmp.data.UserRoleEntity; +import org.opencdmp.data.*; import org.opencdmp.errorcode.ErrorThesaurusProperties; import org.opencdmp.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEvent; import org.opencdmp.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEventHandler; import org.opencdmp.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent; import org.opencdmp.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEventHandler; +import org.opencdmp.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler; import org.opencdmp.model.Tenant; import org.opencdmp.model.builder.TenantBuilder; import org.opencdmp.model.deleter.TenantDeleter; @@ -76,6 +74,7 @@ public class TenantServiceImpl implements TenantService { private final ErrorThesaurusProperties errors; private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler; private final TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler; + private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler; private final KeycloakService keycloakService; private final AuthorizationProperties authorizationProperties; private final TenantScope tenantScope; @@ -92,7 +91,7 @@ public class TenantServiceImpl implements TenantService { BuilderFactory builderFactory, ConventionService conventionService, MessageSource messageSource, - ErrorThesaurusProperties errors, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor) { + ErrorThesaurusProperties errors, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractor claimExtractor) { this.entityManager = entityManager; this.authorizationService = authorizationService; this.deleterFactory = deleterFactory; @@ -102,6 +101,7 @@ public class TenantServiceImpl implements TenantService { this.errors = errors; this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler; this.tenantRemovalIntegrationEventHandler = tenantRemovalIntegrationEventHandler; + this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler; this.keycloakService = keycloakService; this.authorizationProperties = authorizationProperties; this.tenantScope = tenantScope; @@ -144,16 +144,16 @@ public class TenantServiceImpl implements TenantService { Long tenantsWithThisCode = this.queryFactory.query(TenantQuery.class).codes(data.getCode()).count(); if (tenantsWithThisCode > 1) throw new MyValidationException(this.errors.getTenantCodeExists().getCode(), this.errors.getTenantCodeExists().getMessage()); - - if (!isUpdate) { - this.keycloakService.createTenantGroups(data.getCode()); - this.autoAssignGlobalAdminsToNewTenant(data); - } TenantTouchedIntegrationEvent tenantTouchedIntegrationEvent = new TenantTouchedIntegrationEvent(); tenantTouchedIntegrationEvent.setId(data.getId()); tenantTouchedIntegrationEvent.setCode(data.getCode()); this.tenantTouchedIntegrationEventHandler.handle(tenantTouchedIntegrationEvent); + + if (!isUpdate) { + this.keycloakService.createTenantGroups(data.getCode()); + this.autoAssignGlobalAdminsToNewTenant(data); + } return this.builderFactory.builder(TenantBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Tenant._id), data); } @@ -170,6 +170,15 @@ public class TenantServiceImpl implements TenantService { List keycloakIdsToAddToTenantGroup = new ArrayList<>(); for (UUID userId : existingItems.stream().map(UserRoleEntity::getUserId).distinct().toList()) { + TenantUserEntity tenantUserEntity = new TenantUserEntity(); + tenantUserEntity.setId(UUID.randomUUID()); + tenantUserEntity.setUserId(userId); + tenantUserEntity.setIsActive(IsActive.Active); + tenantUserEntity.setTenantId(tenant.getId()); + tenantUserEntity.setCreatedAt(Instant.now()); + tenantUserEntity.setUpdatedAt(Instant.now()); + this.entityManager.persist(tenantUserEntity); + UserCredentialEntity userCredential = userCredentialEntities.stream().filter(x-> !this.conventionService.isNullOrEmpty(x.getExternalId()) && x.getUserId().equals(userId)).findFirst().orElse(null); if (userCredential == null) continue; UserRoleEntity item = new UserRoleEntity(); @@ -180,13 +189,18 @@ public class TenantServiceImpl implements TenantService { item.setCreatedAt(Instant.now()); this.entityManager.persist(item); keycloakIdsToAddToTenantGroup.add(userCredential.getExternalId()); - this.keycloakService.addUserToTenantRoleGroup(userCredential.getExternalId(), this.tenantScope.getTenantCode(), this.authorizationProperties.getTenantAdminRole()); } + this.entityManager.flush(); + + for (UUID userId : existingItems.stream().map(UserRoleEntity::getUserId).distinct().toList()) { + this.userTouchedIntegrationEventHandler.handle(userId); + } + this.entityManager.flush(); for (String externalId : keycloakIdsToAddToTenantGroup) { - this.keycloakService.addUserToTenantRoleGroup(externalId, this.tenantScope.getTenantCode(), this.authorizationProperties.getTenantAdminRole()); + this.keycloakService.addUserToTenantRoleGroup(externalId, tenant.getCode(), this.authorizationProperties.getTenantAdminRole()); } } finally { this.entityManager.reloadTenantFilters(); diff --git a/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java index 026e95def..3b2b66384 100644 --- a/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/user/UserServiceImpl.java @@ -379,6 +379,8 @@ public class UserServiceImpl implements UserService { UserRoleQuery userRoleQuery = this.queryFactory.query(UserRoleQuery.class).userIds(userId).roles(this.authorizationProperties.getAllowedTenantRoles()); if (this.tenantScope.isDefaultTenant()) userRoleQuery.tenantIsSet(false); else userRoleQuery.tenantIsSet(true).tenantIds(this.tenantScope.getTenant()); + + boolean hasTenantUser = this.queryFactory.query(TenantUserQuery.class).isActive(IsActive.Active).userIds(userId).count() > 0; List existingItems = userRoleQuery.collect(); List foundIds = new ArrayList<>(); @@ -396,6 +398,17 @@ public class UserServiceImpl implements UserService { foundIds.add(item.getId()); } + if (!hasTenantUser && !model.getRoles().isEmpty() && !this.tenantScope.isDefaultTenant()){ + TenantUserEntity tenantUserEntity = new TenantUserEntity(); + tenantUserEntity.setId(UUID.randomUUID()); + tenantUserEntity.setUserId(userId); + tenantUserEntity.setIsActive(IsActive.Active); + tenantUserEntity.setTenantId(this.tenantScope.getTenant()); + tenantUserEntity.setCreatedAt(Instant.now()); + tenantUserEntity.setUpdatedAt(Instant.now()); + this.entityManager.persist(tenantUserEntity); + } + this.entityManager.flush(); List toDelete = existingItems.stream().filter(x-> foundIds.stream().noneMatch(y-> y.equals(x.getId()))).collect(Collectors.toList()); diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java b/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java index 360e3031a..f16a8b986 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/TenantEntityManager.java @@ -22,9 +22,12 @@ public class TenantEntityManager { private final TenantScope tenantScope; private final ErrorThesaurusProperties errors; + boolean tenantFiltersDisabled; + public TenantEntityManager(TenantScope tenantScope, ErrorThesaurusProperties errors) { this.tenantScope = tenantScope; this.errors = errors; + this.tenantFiltersDisabled = false; } @@ -33,7 +36,7 @@ public class TenantEntityManager { } public T merge(T entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (!this.tenantScope.isDefaultTenant()) { if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } else if (tenantScopedEntity.getTenantId() != null) { @@ -44,7 +47,7 @@ public class TenantEntityManager { } public void remove(Object entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (!this.tenantScope.isDefaultTenant()) { if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } else if (tenantScopedEntity.getTenantId() != null) { @@ -57,7 +60,7 @@ public class TenantEntityManager { public T find(Class entityClass, Object primaryKey) throws InvalidApplicationException { T entity = this.entityManager.find(entityClass, primaryKey); - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) return null; } return entity; @@ -66,7 +69,7 @@ public class TenantEntityManager { public T find(Class entityClass, Object primaryKey, boolean disableTracking) throws InvalidApplicationException { T entity = this.entityManager.find(entityClass, primaryKey); - if (this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { + if (!this.tenantFiltersDisabled && this.tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) { if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(this.tenantScope.getTenant())) return null; } if (disableTracking) this.entityManager.detach(entity); @@ -96,7 +99,7 @@ public class TenantEntityManager { if (!this.tenantScope.isSet()) return; - if(!this.tenantScope.isDefaultTenant()) { + if (!this.tenantScope.isDefaultTenant()) { this.entityManager .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.TENANT_FILTER) @@ -106,6 +109,7 @@ public class TenantEntityManager { .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + this.tenantFiltersDisabled = false; } public void loadExplictTenantFilters() throws InvalidApplicationException { @@ -113,7 +117,7 @@ public class TenantEntityManager { if (!this.tenantScope.isSet()) return; - if(!this.tenantScope.isDefaultTenant()) { + if (!this.tenantScope.isDefaultTenant()) { this.entityManager .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.TENANT_FILTER_EXPLICT) @@ -123,9 +127,10 @@ public class TenantEntityManager { .unwrap(Session.class) .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); } + this.tenantFiltersDisabled = false; } - public void disableTenantFilters(){ + public void disableTenantFilters() { this.entityManager .unwrap(Session.class) .disableFilter(TenantScopedBaseEntity.TENANT_FILTER); @@ -137,6 +142,11 @@ public class TenantEntityManager { this.entityManager .unwrap(Session.class) .disableFilter(TenantScopedBaseEntity.TENANT_FILTER_EXPLICT); + this.tenantFiltersDisabled = true; + } + + public boolean isTenantFiltersDisabled() { + return tenantFiltersDisabled; } public EntityManager getEntityManager() { @@ -146,5 +156,4 @@ public class TenantEntityManager { public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } - -} \ No newline at end of file +} diff --git a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java index 4d376bdfa..a1527d51e 100644 --- a/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java +++ b/notification-service/notification/src/main/java/gr/cite/notification/data/tenant/TenantListener.java @@ -2,6 +2,7 @@ package gr.cite.notification.data.tenant; import gr.cite.notification.common.scope.tenant.TenantScope; import gr.cite.notification.common.scope.tenant.TenantScoped; +import gr.cite.notification.data.TenantEntityManager; import gr.cite.notification.errorcode.ErrorThesaurusProperties; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.logging.LoggerService; @@ -20,25 +21,28 @@ public class TenantListener { private final TenantScope tenantScope; private final ErrorThesaurusProperties errors; + private final TenantEntityManager tenantEntityManager; @Autowired public TenantListener( - TenantScope tenantScope, ErrorThesaurusProperties errors + TenantScope tenantScope, ErrorThesaurusProperties errors, TenantEntityManager tenantEntityManager ) { this.tenantScope = tenantScope; this.errors = errors; + this.tenantEntityManager = tenantEntityManager; } @PrePersist public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant()) { - if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(this.tenantScope.getTenant()) != 0)) { + if (this.tenantEntityManager.isTenantFiltersDisabled()) return; + if (tenantScope.isMultitenant()) { + if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) { logger.error("somebody tried to set not login tenant"); throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage()); } - if (!this.tenantScope.isDefaultTenant()) { - final UUID tenantId = this.tenantScope.getTenant(); + if (!tenantScope.isDefaultTenant()) { + final UUID tenantId = tenantScope.getTenant(); entity.setTenantId(tenantId); } } else { @@ -49,18 +53,19 @@ public class TenantListener { @PreUpdate @PreRemove public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { - if (this.tenantScope.isMultitenant()) { - if (!this.tenantScope.isDefaultTenant()) { + if (this.tenantEntityManager.isTenantFiltersDisabled()) return; + if (tenantScope.isMultitenant()) { + 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(this.tenantScope.getTenant()) != 0) { + 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 = this.tenantScope.getTenant(); + final UUID tenantId = tenantScope.getTenant(); entity.setTenantId(tenantId); } else { if (entity.getTenantId() != null) { @@ -69,7 +74,7 @@ public class TenantListener { } } } else { - if (entity.getTenantId() != null && (!this.tenantScope.isDefaultTenant() ||entity.getTenantId().compareTo(this.tenantScope.getTenant()) != 0)) { + 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()); } @@ -77,3 +82,4 @@ public class TenantListener { } } +