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

This commit is contained in:
Sofia Papacharalampous 2024-04-03 12:59:12 +03:00
commit 5e5bd8aab0
18 changed files with 301 additions and 78 deletions

View File

@ -3,12 +3,14 @@ package eu.eudat.data;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.scope.tenant.TenantScoped; 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.authz.service.AuthorizationService;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import jakarta.persistence.*; import jakarta.persistence.*;
import org.hibernate.Session;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope; import org.springframework.web.context.annotation.RequestScope;
@ -80,5 +82,28 @@ public class TenantEntityManager {
public void clear() { public void clear() {
this.entityManager.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);
}
} }

View File

@ -1,7 +1,6 @@
package eu.eudat.elastic.data; package eu.eudat.elastic.data;
import eu.eudat.commons.enums.DescriptionStatus; import eu.eudat.commons.enums.DescriptionStatus;
import eu.eudat.commons.enums.DescriptionTemplateVersionStatus;
import eu.eudat.elastic.data.nested.*; import eu.eudat.elastic.data.nested.*;
import gr.cite.tools.elastic.ElasticConstants; import gr.cite.tools.elastic.ElasticConstants;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
@ -19,6 +18,10 @@ public class DescriptionElasticEntity {
private UUID id; private UUID id;
public final static String _id = "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 = { @MultiField(mainField = @Field(value = DescriptionElasticEntity._label, type = FieldType.Text), otherFields = {
@InnerField(suffix = ElasticConstants.SubFields.keyword, type = FieldType.Keyword) @InnerField(suffix = ElasticConstants.SubFields.keyword, type = FieldType.Keyword)
}) })
@ -65,6 +68,14 @@ public class DescriptionElasticEntity {
this.id = id; this.id = id;
} }
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
public String getLabel() { public String getLabel() {
return label; return label;
} }

View File

@ -19,6 +19,10 @@ public class DmpElasticEntity {
private UUID id; private UUID id;
public final static String _id = "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 = { @MultiField(mainField = @Field(value = DmpElasticEntity._label, type = FieldType.Text), otherFields = {
@InnerField(suffix = ElasticConstants.SubFields.keyword, type = FieldType.Keyword) @InnerField(suffix = ElasticConstants.SubFields.keyword, type = FieldType.Keyword)
}) })
@ -84,6 +88,14 @@ public class DmpElasticEntity {
this.id = id; this.id = id;
} }
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
public String getLabel() { public String getLabel() {
return label; return label;
} }

View File

@ -67,6 +67,9 @@ public class DescriptionElasticBuilder extends BaseElasticBuilder<DescriptionEla
if (d.getFinalizedAt() != null) { if (d.getFinalizedAt() != null) {
m.setFinalizedAt(Date.from(d.getFinalizedAt())); m.setFinalizedAt(Date.from(d.getFinalizedAt()));
} }
if (d.getTenantId() != null) {
m.setTenantId(d.getTenantId());
}
if (referenceElasticEntityMap != null) m.setReferences(referenceElasticEntityMap.getOrDefault(d.getId(), null)); if (referenceElasticEntityMap != null) m.setReferences(referenceElasticEntityMap.getOrDefault(d.getId(), null));
if (tagElasticEntityMap != null) m.setTags(tagElasticEntityMap.getOrDefault(d.getId(), null)); if (tagElasticEntityMap != null) m.setTags(tagElasticEntityMap.getOrDefault(d.getId(), null));
if (dmpElasticEntityMap != null) m.setDmp(dmpElasticEntityMap.getOrDefault(d.getDmpId(), null)); if (dmpElasticEntityMap != null) m.setDmp(dmpElasticEntityMap.getOrDefault(d.getDmpId(), null));

View File

@ -62,6 +62,9 @@ public class DmpElasticBuilder extends BaseElasticBuilder<DmpElasticEntity, DmpE
if (d.getFinalizedAt() != null) { if (d.getFinalizedAt() != null) {
m.setFinalizedAt(Date.from(d.getFinalizedAt())); m.setFinalizedAt(Date.from(d.getFinalizedAt()));
} }
if (d.getTenantId() != null) {
m.setTenantId(d.getTenantId());
}
if (referenceElasticEntityMap != null) m.setReferences(referenceElasticEntityMap.getOrDefault(d.getId(), null)); if (referenceElasticEntityMap != null) m.setReferences(referenceElasticEntityMap.getOrDefault(d.getId(), null));
if (descriptionElasticEntityMap != null) m.setDescriptions(descriptionElasticEntityMap.getOrDefault(d.getId(), null)); if (descriptionElasticEntityMap != null) m.setDescriptions(descriptionElasticEntityMap.getOrDefault(d.getId(), null));
if (collaboratorElasticEntityMap != null) m.setCollaborators(collaboratorElasticEntityMap.getOrDefault(d.getId(), null)); if (collaboratorElasticEntityMap != null) m.setCollaborators(collaboratorElasticEntityMap.getOrDefault(d.getId(), null));

View File

@ -2,9 +2,11 @@ package eu.eudat.elastic.query;
import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.DescriptionStatus; import eu.eudat.commons.enums.DescriptionStatus;
import eu.eudat.commons.enums.DmpAccessType; import eu.eudat.commons.enums.DmpAccessType;
import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.enums.DmpStatus;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.service.elastic.AppElasticProperties; import eu.eudat.service.elastic.AppElasticProperties;
import eu.eudat.elastic.data.DescriptionElasticEntity; import eu.eudat.elastic.data.DescriptionElasticEntity;
@ -26,6 +28,7 @@ import org.springframework.context.annotation.Scope;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
@ -132,14 +135,16 @@ public class DescriptionElasticQuery extends ElasticQuery<DescriptionElasticEnti
private final AppElasticProperties appElasticProperties; private final AppElasticProperties appElasticProperties;
private final ElasticService elasticService; private final ElasticService elasticService;
private final UserScope userScope; private final UserScope userScope;
private final TenantScope tenantScope;
private final AuthorizationService authService; private final AuthorizationService authService;
@Autowired() @Autowired()
public DescriptionElasticQuery(ElasticsearchTemplate elasticsearchTemplate, ElasticProperties elasticProperties, QueryFactory queryFactory, AppElasticProperties appElasticProperties, ElasticService elasticService, UserScope userScope, AuthorizationService authService) { public DescriptionElasticQuery(ElasticsearchTemplate elasticsearchTemplate, ElasticProperties elasticProperties, QueryFactory queryFactory, AppElasticProperties appElasticProperties, ElasticService elasticService, UserScope userScope, TenantScope tenantScope, AuthorizationService authService) {
super(elasticsearchTemplate, elasticProperties); super(elasticsearchTemplate, elasticProperties);
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.appElasticProperties = appElasticProperties; this.appElasticProperties = appElasticProperties;
this.elasticService = elasticService; this.elasticService = elasticService;
this.userScope = userScope; this.userScope = userScope;
this.tenantScope = tenantScope;
this.authService = authService; this.authService = authService;
} }
@ -155,10 +160,32 @@ public class DescriptionElasticQuery extends ElasticQuery<DescriptionElasticEnti
return DescriptionElasticEntity.class; return DescriptionElasticEntity.class;
} }
private Query applyTenant(List<Query> 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 @Override
protected Query applyAuthZ() { protected Query applyAuthZ() {
if (this.authorize.contains(AuthorizationFlags.None)) return null; if (this.authorize.contains(AuthorizationFlags.None)) return this.applyTenant(null);
//if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseDescription)) return null; if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseDescription)) return this.applyTenant(null);
UUID userId = null; UUID userId = null;
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public); boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe(); if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe();
@ -175,9 +202,9 @@ public class DescriptionElasticQuery extends ElasticQuery<DescriptionElasticEnti
query.ids(userId); query.ids(userId);
predicates.add(this.nestedQuery(query).build()._toQuery()); predicates.add(this.nestedQuery(query).build()._toQuery());
} }
if (!predicates.isEmpty()) { if (!predicates.isEmpty()) {
return this.or(predicates)._toQuery(); return this.applyTenant(predicates);
} else { } else {
return this.equals(this.elasticFieldOf(DescriptionElasticEntity._id), UUID.randomUUID()); return this.equals(this.elasticFieldOf(DescriptionElasticEntity._id), UUID.randomUUID());
} }

View File

@ -6,7 +6,9 @@ import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.DmpAccessType; import eu.eudat.commons.enums.DmpAccessType;
import eu.eudat.commons.enums.DmpStatus; import eu.eudat.commons.enums.DmpStatus;
import eu.eudat.commons.enums.DmpVersionStatus; import eu.eudat.commons.enums.DmpVersionStatus;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.elastic.data.DescriptionElasticEntity;
import eu.eudat.service.elastic.AppElasticProperties; import eu.eudat.service.elastic.AppElasticProperties;
import eu.eudat.elastic.data.DmpElasticEntity; import eu.eudat.elastic.data.DmpElasticEntity;
import eu.eudat.elastic.data.nested.NestedDescriptionElasticEntity; import eu.eudat.elastic.data.nested.NestedDescriptionElasticEntity;
@ -23,6 +25,7 @@ import org.springframework.context.annotation.Scope;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -162,14 +165,16 @@ public class DmpElasticQuery extends ElasticQuery<DmpElasticEntity, UUID> {
private final AppElasticProperties appElasticProperties; private final AppElasticProperties appElasticProperties;
private final ElasticService elasticService; private final ElasticService elasticService;
private final UserScope userScope; private final UserScope userScope;
private final TenantScope tenantScope;
private final AuthorizationService authService; private final AuthorizationService authService;
@Autowired() @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); super(elasticsearchTemplate, elasticProperties);
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.appElasticProperties = appElasticProperties; this.appElasticProperties = appElasticProperties;
this.elasticService = elasticService; this.elasticService = elasticService;
this.userScope = userScope; this.userScope = userScope;
this.tenantScope = tenantScope;
this.authService = authService; this.authService = authService;
} }
@ -183,10 +188,32 @@ public class DmpElasticQuery extends ElasticQuery<DmpElasticEntity, UUID> {
return DmpElasticEntity.class; return DmpElasticEntity.class;
} }
private Query applyTenant(List<Query> 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 @Override
protected Query applyAuthZ() { 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; UUID userId = null;
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public); boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe(); if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe();
@ -203,7 +230,11 @@ public class DmpElasticQuery extends ElasticQuery<DmpElasticEntity, UUID> {
query.ids(userId); query.ids(userId);
predicates.add(this.nestedQuery(query).build()._toQuery()); 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 @Override

View File

@ -30,7 +30,7 @@ public interface ElasticService {
void deleteDescriptionIndex() throws IOException; void deleteDescriptionIndex() throws IOException;
void resetDmpIndex() throws IOException; void resetDmpIndex() throws IOException, InvalidApplicationException;
void resetDescriptionIndex() throws IOException; void resetDescriptionIndex() throws IOException, InvalidApplicationException;
} }

View File

@ -9,6 +9,7 @@ import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.DescriptionEntity; import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpEntity; import eu.eudat.data.DmpEntity;
import eu.eudat.data.TenantEntityManager; import eu.eudat.data.TenantEntityManager;
import eu.eudat.data.tenant.TenantScopedBaseEntity;
import eu.eudat.elastic.data.DescriptionElasticEntity; import eu.eudat.elastic.data.DescriptionElasticEntity;
import eu.eudat.elastic.data.DmpElasticEntity; import eu.eudat.elastic.data.DmpElasticEntity;
import eu.eudat.elastic.data.nested.*; 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.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import org.hibernate.Session;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
@ -133,6 +135,7 @@ public class ElasticServiceImpl implements ElasticService {
private Map<String, Property> createDescriptionTemplatePropertyMap(){ private Map<String, Property> createDescriptionTemplatePropertyMap(){
Map<String, Property> propertyMap = new HashMap<>(); Map<String, Property> propertyMap = new HashMap<>();
propertyMap.put(DescriptionElasticEntity._id, this.createElastic(FieldType.Keyword, false)); 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._label, this.createElastic(FieldType.Keyword, false));
propertyMap.put(DescriptionElasticEntity._description, this.createElastic(FieldType.Text, true)); propertyMap.put(DescriptionElasticEntity._description, this.createElastic(FieldType.Text, true));
propertyMap.put(DescriptionElasticEntity._status, this.createElastic(FieldType.Short, false)); propertyMap.put(DescriptionElasticEntity._status, this.createElastic(FieldType.Short, false));
@ -149,6 +152,7 @@ public class ElasticServiceImpl implements ElasticService {
private Map<String, Property> createDmpTemplatePropertyMap(){ private Map<String, Property> createDmpTemplatePropertyMap(){
Map<String, Property> propertyMap = new HashMap<>(); Map<String, Property> propertyMap = new HashMap<>();
propertyMap.put(DmpElasticEntity._id, this.createElastic(FieldType.Keyword, false)); 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._label, this.createElastic(FieldType.Text, true));
propertyMap.put(DmpElasticEntity._description, this.createElastic(FieldType.Text, false)); propertyMap.put(DmpElasticEntity._description, this.createElastic(FieldType.Text, false));
propertyMap.put(DmpElasticEntity._status, this.createElastic(FieldType.Short, false)); propertyMap.put(DmpElasticEntity._status, this.createElastic(FieldType.Short, false));
@ -356,7 +360,7 @@ public class ElasticServiceImpl implements ElasticService {
} }
@Override @Override
public void resetDmpIndex() throws IOException { public void resetDmpIndex() throws IOException, InvalidApplicationException {
logger.debug(new MapLogEntry("reset dmp index")); logger.debug(new MapLogEntry("reset dmp index"));
this.authorizationService.authorizeForce(Permission.ManageElastic); this.authorizationService.authorizeForce(Permission.ManageElastic);
@ -364,47 +368,58 @@ public class ElasticServiceImpl implements ElasticService {
this.deleteDmpIndex(); this.deleteDmpIndex();
this.ensureDmpIndex(); this.ensureDmpIndex();
int page = 0; try {
int pageSize = this.appElasticProperties.getResetBatchSize(); this.entityManager.disableTenantFilters();
List<DmpEntity> items; int page = 0;
do { int pageSize = this.appElasticProperties.getResetBatchSize();
DmpQuery query = this.queryFactory.query(DmpQuery.class); List<DmpEntity> items;
query.setOrder(new Ordering().addAscending(Dmp._createdAt)); do {
query.setPage(new Paging(page * pageSize, pageSize)); DmpQuery query = this.queryFactory.query(DmpQuery.class);
query.setOrder(new Ordering().addAscending(Dmp._createdAt));
items = query.collect(); query.setPage(new Paging(page * pageSize, pageSize));
if (items != null && !items.isEmpty()) {
List<DmpElasticEntity> elasticEntities = this.builderFactory.builder(DmpElasticBuilder.class).build(items); items = query.collect();
elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDmpIndexName())); if (items != null && !items.isEmpty()) {
page++; List<DmpElasticEntity> elasticEntities = this.builderFactory.builder(DmpElasticBuilder.class).build(items);
} elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDmpIndexName()));
} while (items != null && !items.isEmpty()); page++;
}
} while (items != null && !items.isEmpty());
}finally {
this.entityManager.enableTenantFilters();
}
} }
@Override @Override
public void resetDescriptionIndex() throws IOException { public void resetDescriptionIndex() throws IOException, InvalidApplicationException {
logger.debug(new MapLogEntry("reset description index")); logger.debug(new MapLogEntry("reset description index"));
this.authorizationService.authorizeForce(Permission.ManageElastic); this.authorizationService.authorizeForce(Permission.ManageElastic);
if (!this.enabled()) return; if (!this.enabled()) return;
this.deleteDescriptionIndex(); this.deleteDescriptionIndex();
this.ensureDescriptionIndex(); this.ensureDescriptionIndex();
int page = 0; try {
int pageSize = this.appElasticProperties.getResetBatchSize(); this.entityManager.disableTenantFilters();
List<DescriptionEntity> 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(); int page = 0;
if (items != null && !items.isEmpty()) { int pageSize = this.appElasticProperties.getResetBatchSize();
List<DescriptionElasticEntity> elasticEntities = this.builderFactory.builder(DescriptionElasticBuilder.class).build(items); List<DescriptionEntity> items;
elasticsearchTemplate.save(elasticEntities, IndexCoordinates.of(this.appElasticProperties.getDescriptionIndexName())); do {
page++; DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class);
} query.setOrder(new Ordering().addAscending(Description._createdAt));
} while (items != null && !items.isEmpty()); query.setPage(new Paging(page * pageSize, pageSize));
items = query.collect();
if (items != null && !items.isEmpty()) {
List<DescriptionElasticEntity> 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 //endregion

View File

@ -69,6 +69,7 @@ public class TenantInterceptor implements WebRequestInterceptor {
private final ConventionService conventionService; private final ConventionService conventionService;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler; private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final AuthorizationProperties authorizationProperties; private final AuthorizationProperties authorizationProperties;
private final UserTenantRolesCacheService userTenantRolesCacheService;
@PersistenceContext @PersistenceContext
public EntityManager entityManager; public EntityManager entityManager;
@ -82,7 +83,7 @@ public class TenantInterceptor implements WebRequestInterceptor {
TenantScopeProperties tenantScopeProperties, TenantScopeProperties tenantScopeProperties,
UserAllowedTenantCacheService userAllowedTenantCacheService, UserAllowedTenantCacheService userAllowedTenantCacheService,
PlatformTransactionManager transactionManager, PlatformTransactionManager transactionManager,
ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService, LockByKeyManager lockByKeyManager, ConventionService conventionService, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, AuthorizationProperties authorizationProperties) { ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService, LockByKeyManager lockByKeyManager, ConventionService conventionService, UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, AuthorizationProperties authorizationProperties, UserTenantRolesCacheService userTenantRolesCacheService) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.userScope = userScope; this.userScope = userScope;
this.currentPrincipalResolver = currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
@ -97,6 +98,7 @@ public class TenantInterceptor implements WebRequestInterceptor {
this.conventionService = conventionService; this.conventionService = conventionService;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler; this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.authorizationProperties = authorizationProperties; this.authorizationProperties = authorizationProperties;
this.userTenantRolesCacheService = userTenantRolesCacheService;
} }
@Override @Override
@ -136,6 +138,10 @@ public class TenantInterceptor implements WebRequestInterceptor {
.unwrap(Session.class) .unwrap(Session.class)
.enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); .enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER);
} }
UserTenantRolesCacheService.UserTenantRolesCacheValue cacheValue = this.userTenantRolesCacheService.lookup(this.userTenantRolesCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.isDefaultTenant() ? UUID.fromString("00000000-0000-0000-0000-000000000000") : this.tenantScope.getTenant()));
if (cacheValue == null || !this.userRolesSynced(cacheValue.getRoles())) {
this.syncUserWithClaims();
}
} else { } else {
if (isAllowedNoTenant || this.isWhiteListedEndpoint(request)) { if (isAllowedNoTenant || this.isWhiteListedEndpoint(request)) {
tenantScope.setTenant(null, null); tenantScope.setTenant(null, null);
@ -144,8 +150,6 @@ public class TenantInterceptor implements WebRequestInterceptor {
throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage()); throw new MyForbiddenException(this.errors.getTenantNotAllowed().getCode(), this.errors.getTenantNotAllowed().getMessage());
} }
} }
this.syncUserWithClaims();
} else { } else {
if (!isAllowedNoTenant) { if (!isAllowedNoTenant) {
if (!this.isWhiteListedEndpoint(request)) { if (!this.isWhiteListedEndpoint(request)) {
@ -244,6 +248,8 @@ public class TenantInterceptor implements WebRequestInterceptor {
} }
private void syncUserWithClaims() throws InvalidApplicationException, InterruptedException { private void syncUserWithClaims() throws InvalidApplicationException, InterruptedException {
boolean usedResource = false; boolean usedResource = false;
String lockId = userScope.getUserId().toString().toLowerCase(Locale.ROOT); String lockId = userScope.getUserId().toString().toLowerCase(Locale.ROOT);
boolean hasChanges = false; boolean hasChanges = false;
@ -258,7 +264,14 @@ public class TenantInterceptor implements WebRequestInterceptor {
try { try {
status = transactionManager.getTransaction(definition); status = transactionManager.getTransaction(definition);
List<String> existingUserRoles = this.collectUserRoles(); UserTenantRolesCacheService.UserTenantRolesCacheValue cacheValue = this.userTenantRolesCacheService.lookup(this.userTenantRolesCacheService.buildKey(this.userScope.getUserId(), this.tenantScope.isDefaultTenant() ? UUID.fromString("00000000-0000-0000-0000-000000000000") : this.tenantScope.getTenant()));
List<String> existingUserRoles;
if (cacheValue != null) {
existingUserRoles = cacheValue.getRoles();
} else {
existingUserRoles = this.collectUserRoles();
this.userTenantRolesCacheService.put(new UserTenantRolesCacheService.UserTenantRolesCacheValue(this.userScope.getUserId(), this.tenantScope.isDefaultTenant() ? UUID.fromString("00000000-0000-0000-0000-000000000000") : this.tenantScope.getTenant(), existingUserRoles));
}
if (!this.userRolesSynced(existingUserRoles)) { if (!this.userRolesSynced(existingUserRoles)) {
this.syncRoles(); this.syncRoles();
hasChanges = true; hasChanges = true;

View File

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

View File

@ -88,3 +88,4 @@ public class UserAllowedTenantCacheService extends CacheService<UserAllowedTenan
return this.generateKey(keyParts); return this.generateKey(keyParts);
} }
} }

View File

@ -0,0 +1,10 @@
package eu.eudat.interceptors.tenant;
import gr.cite.tools.cache.CacheOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.user-tenant-roles")
public class UserTenantRolesCacheOptions extends CacheOptions {
}

View File

@ -0,0 +1,91 @@
package eu.eudat.interceptors.tenant;
import eu.eudat.event.UserAddedToTenantEvent;
import eu.eudat.event.UserRemovedFromTenantEvent;
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.List;
import java.util.Locale;
import java.util.UUID;
@Service
public class UserTenantRolesCacheService extends CacheService<UserTenantRolesCacheService.UserTenantRolesCacheValue> {
public static class UserTenantRolesCacheValue {
public UserTenantRolesCacheValue() {
}
public UserTenantRolesCacheValue(UUID userId, UUID tenantId, List<String> roles) {
this.userId = userId;
this.tenantId = tenantId;
this.roles = roles;
}
private UUID userId;
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
private UUID tenantId;
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
private List<String> roles;
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
@Autowired
public UserTenantRolesCacheService(UserTenantRolesCacheOptions options) {
super(options);
}
@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()));
}
@Override
protected Class<UserTenantRolesCacheValue> valueClass() {
return UserTenantRolesCacheValue.class;
}
@Override
public String keyOf(UserTenantRolesCacheValue value) {
return this.buildKey(value.getUserId(), value.getTenantId());
}
public String buildKey(UUID userId, UUID tenantId) {
HashMap<String, String> 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);
}
}

View File

@ -68,6 +68,12 @@ cache:
maximumSize: 500 maximumSize: 500
enableRecordStats: false enableRecordStats: false
expireAfterWriteSeconds: 60 expireAfterWriteSeconds: 60
- names: [ userTenantRoles ]
allowNullValues: true
initialCapacity: 100
maximumSize: 500
enableRecordStats: false
expireAfterWriteSeconds: 60
mapCaches: mapCaches:
userBySubjectId: userBySubjectId:
name: userBySubjectId name: userBySubjectId
@ -81,6 +87,9 @@ cache:
userAllowedTenant: userAllowedTenant:
name: userAllowedTenant name: userAllowedTenant
keyPattern: user_allowed_tenant$tenant_id$_$user_id$:v0 keyPattern: user_allowed_tenant$tenant_id$_$user_id$:v0
userTenantRoles:
name: userTenantRoles
keyPattern: user_tenant_roles$tenant_id$_$user_id$:v0
supportiveMaterial: supportiveMaterial:
name: supportiveMaterial name: supportiveMaterial
keyPattern: supportive_material_$type$_$lang$:v0 keyPattern: supportive_material_$type$_$lang$:v0

View File

@ -12,14 +12,8 @@ CREATE TABLE IF NOT EXISTS public."ntf_UserCredential"
"created_at" timestamp without time zone NOT NULL, "created_at" timestamp without time zone NOT NULL,
"updated_at" timestamp without time zone NOT NULL, "updated_at" timestamp without time zone NOT NULL,
"is_active" smallint NOT NULL DEFAULT 1, "is_active" smallint NOT NULL DEFAULT 1,
"tenant" uuid,
"data" character varying COLLATE pg_catalog."default", "data" character varying COLLATE pg_catalog."default",
CONSTRAINT "ntf_UserCredential_pkey" PRIMARY KEY (id), CONSTRAINT "ntf_UserCredential_pkey" PRIMARY KEY (id),
CONSTRAINT "ntf_UserCredential_tenant_fkey" FOREIGN KEY ("tenant")
REFERENCES public."ntf_Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
NOT VALID,
CONSTRAINT "ntf_UserCredential_user_fkey" FOREIGN KEY ("user") CONSTRAINT "ntf_UserCredential_user_fkey" FOREIGN KEY ("user")
REFERENCES public."ntf_User" (id) MATCH SIMPLE REFERENCES public."ntf_User" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION

View File

@ -157,24 +157,6 @@ BEGIN
ON DELETE NO ACTION ON DELETE NO ACTION
NOT VALID; NOT VALID;
ALTER TABLE public."UserContactInfo"
ADD COLUMN tenant uuid;
ALTER TABLE public."UserContactInfo"
ADD CONSTRAINT "UserContactInfo_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id)
ON UPDATE NO ACTION
ON DELETE NO ACTION
NOT VALID;
ALTER TABLE public."UserCredential"
ADD COLUMN tenant uuid;
ALTER TABLE public."UserCredential"
ADD CONSTRAINT "UserCredential_tenant_fkey" FOREIGN KEY (tenant)
REFERENCES public."Tenant" (id)
ON UPDATE NO ACTION
ON DELETE NO ACTION
NOT VALID;
ALTER TABLE public."UserDescriptionTemplate" ALTER TABLE public."UserDescriptionTemplate"
ADD COLUMN tenant uuid; ADD COLUMN tenant uuid;
ALTER TABLE public."UserDescriptionTemplate" ALTER TABLE public."UserDescriptionTemplate"

View File

@ -12,14 +12,8 @@ CREATE TABLE IF NOT EXISTS public."ant_UserCredential"
"created_at" timestamp without time zone NOT NULL, "created_at" timestamp without time zone NOT NULL,
"updated_at" timestamp without time zone NOT NULL, "updated_at" timestamp without time zone NOT NULL,
"is_active" smallint NOT NULL DEFAULT 1, "is_active" smallint NOT NULL DEFAULT 1,
"tenant" uuid,
"data" character varying COLLATE pg_catalog."default", "data" character varying COLLATE pg_catalog."default",
CONSTRAINT "ant_UserCredential_pkey" PRIMARY KEY (id), CONSTRAINT "ant_UserCredential_pkey" PRIMARY KEY (id),
CONSTRAINT "ant_UserCredential_tenant_fkey" FOREIGN KEY ("tenant")
REFERENCES public."ant_Tenant" (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
NOT VALID,
CONSTRAINT "ant_UserCredential_user_fkey" FOREIGN KEY ("user") CONSTRAINT "ant_UserCredential_user_fkey" FOREIGN KEY ("user")
REFERENCES public."ant_User" (id) MATCH SIMPLE REFERENCES public."ant_User" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION