From f47c049b8c672e5ac97e2930842b82e219e9da79 Mon Sep 17 00:00:00 2001 From: sgiannopoulos Date: Wed, 3 Apr 2024 12:22:43 +0300 Subject: [PATCH] elastic apply tenant logic --- .../eu/eudat/data/TenantEntityManager.java | 25 ++++++ .../data/DescriptionElasticEntity.java | 13 ++- .../eudat/elastic/data/DmpElasticEntity.java | 12 +++ .../DescriptionElasticBuilder.java | 3 + .../elasticbuilder/DmpElasticBuilder.java | 3 + .../query/DescriptionElasticQuery.java | 37 +++++++-- .../eudat/elastic/query/DmpElasticQuery.java | 39 ++++++++- .../eudat/service/elastic/ElasticService.java | 4 +- .../service/elastic/ElasticServiceImpl.java | 79 +++++++++++-------- 9 files changed, 171 insertions(+), 44 deletions(-) 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 efbaccdb2..ba8a2b77b 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 @@ -3,12 +3,14 @@ package eu.eudat.data; import eu.eudat.authorization.Permission; import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.commons.scope.tenant.TenantScoped; +import eu.eudat.data.tenant.TenantScopedBaseEntity; 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 jakarta.persistence.*; +import org.hibernate.Session; import org.springframework.stereotype.Service; import org.springframework.web.context.annotation.RequestScope; @@ -80,5 +82,28 @@ public class TenantEntityManager { 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); + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DescriptionElasticEntity.java b/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DescriptionElasticEntity.java index df810740b..3a5078cb9 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DescriptionElasticEntity.java +++ b/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DescriptionElasticEntity.java @@ -1,7 +1,6 @@ package eu.eudat.elastic.data; import eu.eudat.commons.enums.DescriptionStatus; -import eu.eudat.commons.enums.DescriptionTemplateVersionStatus; import eu.eudat.elastic.data.nested.*; import gr.cite.tools.elastic.ElasticConstants; import org.springframework.data.annotation.Id; @@ -19,6 +18,10 @@ public class DescriptionElasticEntity { private UUID id; public final static String _id = "id"; + @Field(value = DescriptionElasticEntity._tenantId, type = FieldType.Keyword) + private UUID tenantId; + public final static String _tenantId = "tenantId"; + @MultiField(mainField = @Field(value = DescriptionElasticEntity._label, type = FieldType.Text), otherFields = { @InnerField(suffix = ElasticConstants.SubFields.keyword, type = FieldType.Keyword) }) @@ -65,6 +68,14 @@ public class DescriptionElasticEntity { this.id = id; } + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + public String getLabel() { return label; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DmpElasticEntity.java b/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DmpElasticEntity.java index 1bc1eaa28..6adfc844e 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DmpElasticEntity.java +++ b/dmp-backend/core/src/main/java/eu/eudat/elastic/data/DmpElasticEntity.java @@ -19,6 +19,10 @@ public class DmpElasticEntity { private UUID id; public final static String _id = "id"; + @Field(value = DmpElasticEntity._tenantId, type = FieldType.Keyword) + private UUID tenantId; + public final static String _tenantId = "tenantId"; + @MultiField(mainField = @Field(value = DmpElasticEntity._label, type = FieldType.Text), otherFields = { @InnerField(suffix = ElasticConstants.SubFields.keyword, type = FieldType.Keyword) }) @@ -84,6 +88,14 @@ public class DmpElasticEntity { this.id = id; } + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + public String getLabel() { return label; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/elastic/elasticbuilder/DescriptionElasticBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/elastic/elasticbuilder/DescriptionElasticBuilder.java index cf5c332e7..62921c448 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/elastic/elasticbuilder/DescriptionElasticBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/elastic/elasticbuilder/DescriptionElasticBuilder.java @@ -67,6 +67,9 @@ public class DescriptionElasticBuilder extends BaseElasticBuilder predicates){ + if (this.tenantScope.isSet()){ + Query tenantQuery; + if (this.tenantScope.isDefaultTenant()){ + tenantQuery = this.fieldNotExists(this.elasticFieldOf(DescriptionElasticEntity._tenantId))._toQuery(); + } + else { + try { + tenantQuery = this.or(this.fieldNotExists(this.elasticFieldOf(DescriptionElasticEntity._tenantId))._toQuery(), + this.equals(this.elasticFieldOf(DescriptionElasticEntity._tenantId), this.tenantScope.getTenant()))._toQuery(); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } + if (predicates == null) return tenantQuery; + else return this.and(tenantQuery, this.or(predicates)._toQuery()); + } else { + if (predicates != null) return this.or(predicates)._toQuery(); + } + return null; + } + @Override protected Query applyAuthZ() { - if (this.authorize.contains(AuthorizationFlags.None)) return null; - //if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseDescription)) return null; + if (this.authorize.contains(AuthorizationFlags.None)) return this.applyTenant(null); + if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseDescription)) return this.applyTenant(null); UUID userId = null; boolean usePublic = this.authorize.contains(AuthorizationFlags.Public); if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe(); @@ -175,9 +202,9 @@ public class DescriptionElasticQuery extends ElasticQuery { private final AppElasticProperties appElasticProperties; private final ElasticService elasticService; private final UserScope userScope; + private final TenantScope tenantScope; private final AuthorizationService authService; @Autowired() - public DmpElasticQuery(ElasticsearchTemplate elasticsearchTemplate, ElasticProperties elasticProperties, QueryFactory queryFactory, AppElasticProperties appElasticProperties, ElasticService elasticService, UserScope userScope, AuthorizationService authService) { + public DmpElasticQuery(ElasticsearchTemplate elasticsearchTemplate, ElasticProperties elasticProperties, QueryFactory queryFactory, AppElasticProperties appElasticProperties, ElasticService elasticService, UserScope userScope, TenantScope tenantScope, AuthorizationService authService) { super(elasticsearchTemplate, elasticProperties); this.queryFactory = queryFactory; this.appElasticProperties = appElasticProperties; this.elasticService = elasticService; this.userScope = userScope; + this.tenantScope = tenantScope; this.authService = authService; } @@ -183,10 +188,32 @@ public class DmpElasticQuery extends ElasticQuery { return DmpElasticEntity.class; } + private Query applyTenant(List predicates){ + if (this.tenantScope.isSet()){ + Query tenantQuery; + if (this.tenantScope.isDefaultTenant()){ + tenantQuery = this.fieldNotExists(this.elasticFieldOf(DmpElasticEntity._tenantId))._toQuery(); + } + else { + try { + tenantQuery = this.or(this.fieldNotExists(this.elasticFieldOf(DmpElasticEntity._tenantId))._toQuery(), + this.equals(this.elasticFieldOf(DmpElasticEntity._tenantId), this.tenantScope.getTenant()))._toQuery(); + } catch (InvalidApplicationException e) { + throw new RuntimeException(e); + } + } + if (predicates == null) return tenantQuery; + else return this.and(tenantQuery, this.or(predicates)._toQuery()); + } else { + if (predicates != null) return this.or(predicates)._toQuery(); + } + return null; + } @Override protected Query applyAuthZ() { - if (this.authorize.contains(AuthorizationFlags.None)) return null; - if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseDmp)) return null; + + if (this.authorize.contains(AuthorizationFlags.None)) return this.applyTenant(null); + if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseDmp)) return this.applyTenant(null); UUID userId = null; boolean usePublic = this.authorize.contains(AuthorizationFlags.Public); if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe(); @@ -203,7 +230,11 @@ public class DmpElasticQuery extends ElasticQuery { query.ids(userId); predicates.add(this.nestedQuery(query).build()._toQuery()); } - return this.or(predicates)._toQuery(); + if (!predicates.isEmpty()) { + return this.applyTenant(predicates); + } else { + return this.equals(this.elasticFieldOf(DescriptionElasticEntity._id), UUID.randomUUID()); + } } @Override diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticService.java b/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticService.java index f58965132..2d1c1648d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticService.java @@ -30,7 +30,7 @@ public interface ElasticService { void deleteDescriptionIndex() throws IOException; - void resetDmpIndex() throws IOException; + void resetDmpIndex() throws IOException, InvalidApplicationException; - void resetDescriptionIndex() throws IOException; + void resetDescriptionIndex() throws IOException, InvalidApplicationException; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticServiceImpl.java index 667ccd73e..3936ee638 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/elastic/ElasticServiceImpl.java @@ -9,6 +9,7 @@ import eu.eudat.commons.enums.IsActive; import eu.eudat.data.DescriptionEntity; import eu.eudat.data.DmpEntity; import eu.eudat.data.TenantEntityManager; +import eu.eudat.data.tenant.TenantScopedBaseEntity; import eu.eudat.elastic.data.DescriptionElasticEntity; import eu.eudat.elastic.data.DmpElasticEntity; import eu.eudat.elastic.data.nested.*; @@ -29,6 +30,7 @@ import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.MapLogEntry; +import org.hibernate.Session; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; @@ -133,6 +135,7 @@ public class ElasticServiceImpl implements ElasticService { private Map createDescriptionTemplatePropertyMap(){ Map propertyMap = new HashMap<>(); propertyMap.put(DescriptionElasticEntity._id, this.createElastic(FieldType.Keyword, false)); + propertyMap.put(DescriptionElasticEntity._tenantId, this.createElastic(FieldType.Keyword, false)); propertyMap.put(DescriptionElasticEntity._label, this.createElastic(FieldType.Keyword, false)); propertyMap.put(DescriptionElasticEntity._description, this.createElastic(FieldType.Text, true)); propertyMap.put(DescriptionElasticEntity._status, this.createElastic(FieldType.Short, false)); @@ -149,6 +152,7 @@ public class ElasticServiceImpl implements ElasticService { private Map createDmpTemplatePropertyMap(){ Map propertyMap = new HashMap<>(); propertyMap.put(DmpElasticEntity._id, this.createElastic(FieldType.Keyword, false)); + propertyMap.put(DmpElasticEntity._tenantId, this.createElastic(FieldType.Keyword, false)); propertyMap.put(DmpElasticEntity._label, this.createElastic(FieldType.Text, true)); propertyMap.put(DmpElasticEntity._description, this.createElastic(FieldType.Text, false)); propertyMap.put(DmpElasticEntity._status, this.createElastic(FieldType.Short, false)); @@ -356,7 +360,7 @@ public class ElasticServiceImpl implements ElasticService { } @Override - public void resetDmpIndex() throws IOException { + public void resetDmpIndex() throws IOException, InvalidApplicationException { logger.debug(new MapLogEntry("reset dmp index")); this.authorizationService.authorizeForce(Permission.ManageElastic); @@ -364,47 +368,58 @@ public class ElasticServiceImpl implements ElasticService { this.deleteDmpIndex(); this.ensureDmpIndex(); - int page = 0; - int pageSize = this.appElasticProperties.getResetBatchSize(); - List items; - do { - DmpQuery query = this.queryFactory.query(DmpQuery.class); - query.setOrder(new Ordering().addAscending(Dmp._createdAt)); - query.setPage(new Paging(page * pageSize, pageSize)); - - items = query.collect(); - if (items != null && !items.isEmpty()) { - List elasticEntities = this.builderFactory.builder(DmpElasticBuilder.class).build(items); - elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDmpIndexName())); - page++; - } - } while (items != null && !items.isEmpty()); + try { + this.entityManager.disableTenantFilters(); + int page = 0; + int pageSize = this.appElasticProperties.getResetBatchSize(); + List items; + do { + DmpQuery query = this.queryFactory.query(DmpQuery.class); + query.setOrder(new Ordering().addAscending(Dmp._createdAt)); + query.setPage(new Paging(page * pageSize, pageSize)); + + items = query.collect(); + if (items != null && !items.isEmpty()) { + List elasticEntities = this.builderFactory.builder(DmpElasticBuilder.class).build(items); + elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDmpIndexName())); + page++; + } + } while (items != null && !items.isEmpty()); + }finally { + this.entityManager.enableTenantFilters(); + } } @Override - public void resetDescriptionIndex() throws IOException { + public void resetDescriptionIndex() throws IOException, InvalidApplicationException { logger.debug(new MapLogEntry("reset description index")); this.authorizationService.authorizeForce(Permission.ManageElastic); - + if (!this.enabled()) return; this.deleteDescriptionIndex(); this.ensureDescriptionIndex(); - int page = 0; - int pageSize = this.appElasticProperties.getResetBatchSize(); - List items; - do { - DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class); - query.setOrder(new Ordering().addAscending(Description._createdAt)); - query.setPage(new Paging(page * pageSize, pageSize)); + try { + this.entityManager.disableTenantFilters(); - items = query.collect(); - if (items != null && !items.isEmpty()) { - List elasticEntities = this.builderFactory.builder(DescriptionElasticBuilder.class).build(items); - elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDescriptionIndexName())); - page++; - } - } while (items != null && !items.isEmpty()); + int page = 0; + int pageSize = this.appElasticProperties.getResetBatchSize(); + List items; + do { + DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class); + query.setOrder(new Ordering().addAscending(Description._createdAt)); + query.setPage(new Paging(page * pageSize, pageSize)); + + items = query.collect(); + if (items != null && !items.isEmpty()) { + List elasticEntities = this.builderFactory.builder(DescriptionElasticBuilder.class).build(items); + elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDescriptionIndexName())); + page++; + } + } while (items != null && !items.isEmpty()); + }finally { + this.entityManager.enableTenantFilters(); + } } //endregion