multitenant integration changes

This commit is contained in:
Efstratios Giannopoulos 2024-04-03 18:35:37 +03:00
parent 424d18526b
commit 671f8f75ba
88 changed files with 2319 additions and 1135 deletions

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.web.controllers; package gr.cite.annotation.web.controllers;
import gr.cite.annotation.authorization.ClaimNames;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
@ -80,7 +81,7 @@ public class PrincipalController {
logger.debug("my-tenants"); logger.debug("my-tenants");
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();
List<String> tenants = this.claimExtractor.asStrings(principal, TenantScope.TenantCodesClaimName); List<String> tenants = this.claimExtractor.asStrings(principal, ClaimNames.TenantCodesClaimName);
this.auditService.track(AuditableAction.Tenants_Lookup); this.auditService.track(AuditableAction.Tenants_Lookup);
//auditService.trackIdentity(AuditableAction.IdentityTracking_Action); //auditService.trackIdentity(AuditableAction.IdentityTracking_Action);

View File

@ -13,64 +13,66 @@ import java.util.UUID;
@Service @Service
public class TenantByCodeCacheService extends CacheService<TenantByCodeCacheService.TenantByCodeCacheValue> { public class TenantByCodeCacheService extends CacheService<TenantByCodeCacheService.TenantByCodeCacheValue> {
public static class TenantByCodeCacheValue { public static class TenantByCodeCacheValue {
public TenantByCodeCacheValue() { public TenantByCodeCacheValue() {
} }
public TenantByCodeCacheValue(String tenantCode, UUID tenantId) { public TenantByCodeCacheValue(String tenantCode, UUID tenantId) {
this.tenantCode = tenantCode; this.tenantCode = tenantCode;
this.tenantId = tenantId; this.tenantId = tenantId;
} }
private String tenantCode; private String tenantCode;
public String getTenantCode() { public String getTenantCode() {
return tenantCode; return tenantCode;
} }
public void setTenantCode(String tenantCode) { public void setTenantCode(String tenantCode) {
this.tenantCode = tenantCode; this.tenantCode = tenantCode;
} }
private UUID tenantId; private UUID tenantId;
public UUID getTenantId() { public UUID getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(UUID tenantId) { public void setTenantId(UUID tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
} }
private final ConventionService conventionService; private final ConventionService conventionService;
@Autowired @Autowired
public TenantByCodeCacheService(TenantByCodeCacheOptions options, ConventionService conventionService) { public TenantByCodeCacheService(TenantByCodeCacheOptions options, ConventionService conventionService) {
super(options); super(options);
this.conventionService = conventionService; this.conventionService = conventionService;
} }
@EventListener @EventListener
public void handleTenantTouchedEvent(TenantTouchedEvent event) { public void handleTenantTouchedEvent(TenantTouchedEvent event) {
if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantCode())); if (!this.conventionService.isNullOrEmpty(event.getTenantCode()))
if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode())) this.evict(this.buildKey(event.getPreviousTenantCode())); this.evict(this.buildKey(event.getTenantCode()));
} if (!this.conventionService.isNullOrEmpty(event.getPreviousTenantCode()))
this.evict(this.buildKey(event.getPreviousTenantCode()));
}
@Override @Override
protected Class<TenantByCodeCacheValue> valueClass() { protected Class<TenantByCodeCacheValue> valueClass() {
return TenantByCodeCacheValue.class; return TenantByCodeCacheValue.class;
} }
@Override @Override
public String keyOf(TenantByCodeCacheValue value) { public String keyOf(TenantByCodeCacheValue value) {
return this.buildKey(value.getTenantCode()); return this.buildKey(value.getTenantCode());
} }
public String buildKey(String code) { public String buildKey(String code) {
return this.generateKey(new HashMap<>() {{ HashMap<String, String> keyParts = new HashMap<>();
put("$code$", code); keyParts.put("$code$", code);
}}); return this.generateKey(keyParts);
} }
} }

View File

@ -14,63 +14,64 @@ import java.util.UUID;
@Service @Service
public class TenantByIdCacheService extends CacheService<TenantByIdCacheService.TenantByIdCacheValue> { public class TenantByIdCacheService extends CacheService<TenantByIdCacheService.TenantByIdCacheValue> {
public static class TenantByIdCacheValue { public static class TenantByIdCacheValue {
public TenantByIdCacheValue() { public TenantByIdCacheValue() {
} }
public TenantByIdCacheValue(String tenantCode, UUID tenantId) { public TenantByIdCacheValue(String tenantCode, UUID tenantId) {
this.tenantCode = tenantCode; this.tenantCode = tenantCode;
this.tenantId = tenantId; this.tenantId = tenantId;
} }
private String tenantCode; private String tenantCode;
public String getTenantCode() { public String getTenantCode() {
return tenantCode; return tenantCode;
} }
public void setTenantCode(String tenantCode) { public void setTenantCode(String tenantCode) {
this.tenantCode = tenantCode; this.tenantCode = tenantCode;
} }
private UUID tenantId; private UUID tenantId;
public UUID getTenantId() { public UUID getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(UUID tenantId) { public void setTenantId(UUID tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
} }
private final ConventionService conventionService; private final ConventionService conventionService;
@Autowired @Autowired
public TenantByIdCacheService(TenantByIdCacheOptions options, ConventionService conventionService) { public TenantByIdCacheService(TenantByIdCacheOptions options, ConventionService conventionService) {
super(options); super(options);
this.conventionService = conventionService; this.conventionService = conventionService;
} }
@EventListener @EventListener
public void handleTenantTouchedEvent(TenantTouchedEvent event) { public void handleTenantTouchedEvent(TenantTouchedEvent event) {
if (!this.conventionService.isNullOrEmpty(event.getTenantCode())) this.evict(this.buildKey(event.getTenantId())); if (!this.conventionService.isNullOrEmpty(event.getTenantCode()))
} this.evict(this.buildKey(event.getTenantId()));
}
@Override @Override
protected Class<TenantByIdCacheValue> valueClass() { protected Class<TenantByIdCacheValue> valueClass() {
return TenantByIdCacheValue.class; return TenantByIdCacheValue.class;
} }
@Override @Override
public String keyOf(TenantByIdCacheValue value) { public String keyOf(TenantByIdCacheValue value) {
return this.buildKey(value.getTenantId()); return this.buildKey(value.getTenantId());
} }
public String buildKey(UUID id) { public String buildKey(UUID id) {
return this.generateKey(new HashMap<>() {{ HashMap<String, String> keyParts = new HashMap<>();
put("$tenantId$", id.toString().toLowerCase(Locale.ROOT)); keyParts.put("$tenantId$", id.toString().toLowerCase(Locale.ROOT));
}}); return this.generateKey(keyParts);
} }
} }

View File

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

View File

@ -1,19 +1,20 @@
package gr.cite.annotation.web.scope.tenant; package gr.cite.annotation.web.scope.tenant;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.annotation.authorization.ClaimNames;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.TenantEntity; import gr.cite.annotation.data.TenantEntity;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Root;
@ -32,165 +33,152 @@ import java.util.UUID;
@Component @Component
public class TenantScopeClaimInterceptor implements WebRequestInterceptor { public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScopeClaimInterceptor.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScopeClaimInterceptor.class));
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final ConventionService conventionService; private final ConventionService conventionService;
private final TenantScopeProperties tenantScopeProperties; private final TenantScopeProperties tenantScopeProperties;
private final ErrorThesaurusProperties errorThesaurusProperties; private final ErrorThesaurusProperties errorThesaurusProperties;
private final ClaimExtractor claimExtractor; private final ClaimExtractor claimExtractor;
private final CurrentPrincipalResolver currentPrincipalResolver; private final CurrentPrincipalResolver currentPrincipalResolver;
private final String clientTenantClaimName; private final String clientTenantClaimName;
private final ClaimExtractorContext claimExtractorContext; private final ClaimExtractorContext claimExtractorContext;
private final TenantByCodeCacheService tenantByCodeCacheService; private final TenantByCodeCacheService tenantByCodeCacheService;
private final TenantByIdCacheService tenantByIdCacheService; private final TenantByIdCacheService tenantByIdCacheService;
@PersistenceContext @PersistenceContext
public EntityManager entityManager; public EntityManager entityManager;
@Autowired @Autowired
public TenantScopeClaimInterceptor( public TenantScopeClaimInterceptor(
TenantScope tenantScope, TenantScope tenantScope,
ConventionService conventionService, ConventionService conventionService,
ClaimExtractor claimExtractor, ClaimExtractor claimExtractor,
CurrentPrincipalResolver currentPrincipalResolver, CurrentPrincipalResolver currentPrincipalResolver,
ErrorThesaurusProperties errorThesaurusProperties, ErrorThesaurusProperties errorThesaurusProperties,
TenantScopeProperties tenantScopeProperties, TenantScopeProperties tenantScopeProperties,
ClaimExtractorContext claimExtractorContext, ClaimExtractorContext claimExtractorContext,
TenantByCodeCacheService tenantByCodeCacheService, TenantByCodeCacheService tenantByCodeCacheService,
TenantByIdCacheService tenantByIdCacheService TenantByIdCacheService tenantByIdCacheService
) { ) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.conventionService = conventionService; this.conventionService = conventionService;
this.currentPrincipalResolver = currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor; this.claimExtractor = claimExtractor;
this.errorThesaurusProperties = errorThesaurusProperties; this.errorThesaurusProperties = errorThesaurusProperties;
this.tenantScopeProperties = tenantScopeProperties; this.tenantScopeProperties = tenantScopeProperties;
this.claimExtractorContext = claimExtractorContext; this.claimExtractorContext = claimExtractorContext;
this.tenantByCodeCacheService = tenantByCodeCacheService; this.tenantByCodeCacheService = tenantByCodeCacheService;
this.tenantByIdCacheService = tenantByIdCacheService; this.tenantByIdCacheService = tenantByIdCacheService;
this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + TenantScope.TenantClaimName; this.clientTenantClaimName = this.tenantScopeProperties.getClientClaimsPrefix() + ClaimNames.TenantClaimName;
} }
@Override @Override
public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException { public void preHandle(@NotNull WebRequest request) throws InvalidApplicationException {
if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return;
if (!this.tenantScope.isMultitenant()) return; if (!this.tenantScope.isMultitenant()) return;
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal(); MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();
if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) { if (principal != null && principal.isAuthenticated() /* principal.Claims.Any() */) {
Boolean scoped = this.scopeByPrincipal(this.tenantScope, principal); boolean scoped = this.scopeByPrincipal(principal);
if (!scoped) scoped = this.scopeByClient(this.tenantScope, principal); if (!scoped) scoped = this.scopeByClient(principal);
if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant()) throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant())
} throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage());
} }
}
private Boolean scopeByPrincipal(TenantScope scope, MyPrincipal principal) { private boolean scopeByPrincipal(MyPrincipal principal) {
String tenantCode = this.claimExtractor.tenantString(principal); String tenantCode = this.claimExtractor.tenantString(principal);
if (tenantCode == null || tenantCode.isBlank()) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); if (this.conventionService.isNullOrEmpty(tenantCode)) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName);
if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return false;
UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){
if (tenantId == null && tenantCode == null) return false; logger.debug("parsed tenant header and set tenant to default tenant");
if (tenantId == null) { this.tenantScope.setTenant(null, tenantCode);
TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
if (cacheValue != null) { return true;
tenantId = cacheValue.getTenantId(); }
} else {
tenantId = this.getTenantIdFromDatabase(tenantCode); UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode);
this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); if (tenantId == null) {
this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode));
} if (cacheValue != null) {
} else { tenantId = cacheValue.getTenantId();
logger.debug("tenant claim was set to {}", tenantId); } else {
TenantByIdCacheService.TenantByIdCacheValue cacheValue = this.tenantByIdCacheService.lookup(this.tenantByIdCacheService.buildKey(tenantId)); tenantId = this.getTenantIdFromDatabase(tenantCode);
this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId));
this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId));
}
} else {
logger.debug("tenant claim was set to {}", tenantId);
TenantByIdCacheService.TenantByIdCacheValue cacheValue = this.tenantByIdCacheService.lookup(this.tenantByIdCacheService.buildKey(tenantId));
if (cacheValue != null) { if (cacheValue != null) {
tenantCode = cacheValue.getTenantCode(); tenantCode = cacheValue.getTenantCode();
} else { } else {
tenantCode = this.getTenantCodeFromDatabase(tenantId); tenantCode = this.getTenantCodeFromDatabase(tenantId);
this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId)); this.tenantByCodeCacheService.put(new TenantByCodeCacheService.TenantByCodeCacheValue(tenantCode, tenantId));
this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId)); this.tenantByIdCacheService.put(new TenantByIdCacheService.TenantByIdCacheValue(tenantCode, tenantId));
} }
} }
if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { if (tenantId != null) {
logger.debug("parsed tenant header and set tenant id to {}", tenantId); logger.debug("parsed tenant header and set tenant id to {}", tenantId);
this.tenantScope.setTenant(tenantId, tenantCode); this.tenantScope.setTenant(tenantId, tenantCode);
this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
} return true;
return tenantId != null; }
} return false;
}
private Boolean scopeByClient(TenantScope scope, MyPrincipal principal) throws InvalidApplicationException { private boolean scopeByClient(MyPrincipal principal) throws InvalidApplicationException {
String client = this.claimExtractor.client(principal); String client = this.claimExtractor.client(principal);
Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client); Boolean isWhiteListed = this.tenantScopeProperties.getWhiteListedClients() != null && !this.conventionService.isNullOrEmpty(client) && this.tenantScopeProperties.getWhiteListedClients().contains(client);
logger.debug("client is whitelisted : {}, scope is set: {}, with value {}", isWhiteListed, scope.isSet(), (scope.isSet() ? scope.getTenant() : null)); logger.debug("client is whitelisted : {}, scope is set: {}, with value {}", isWhiteListed, this.tenantScope.isSet(), (this.tenantScope.isSet() ? this.tenantScope.getTenant() : null));
return isWhiteListed && scope.isSet(); return isWhiteListed && this.tenantScope.isSet();
} }
private UUID getTenantIdFromDatabase(String tenantCode) { private UUID getTenantIdFromDatabase(String tenantCode) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
criteriaBuilder.equal(root.get(TenantEntity._code), tenantCode), criteriaBuilder.equal(root.get(TenantEntity._code), tenantCode),
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getId();
try { }
o = results.get(0).get(TenantEntity._id); return null;
} catch (IllegalArgumentException e) { }
return null;
}
if (o == null) return null;
try {
return (UUID) o;
} catch (ClassCastException e) {
return null;
}
}
return null;
}
private String getTenantCodeFromDatabase(UUID tenantId) { private String getTenantCodeFromDatabase(UUID tenantId) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
criteriaBuilder.equal(root.get(TenantEntity._id), tenantId), criteriaBuilder.equal(root.get(TenantEntity._id), tenantId),
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getCode();
try { }
o = results.get(0).get(TenantEntity._code); return null;
} catch (IllegalArgumentException e) { }
return null;
}
if (o == null) return null;
try {
return (String) o;
} catch (ClassCastException e) {
return null;
}
}
return null;
}
@Override @Override
public void postHandle(@NonNull WebRequest request, ModelMap model) { public void postHandle(@NonNull WebRequest request, ModelMap model) {
this.tenantScope.setTenant(null, null); this.tenantScope.setTenant(null, null);
this.claimExtractorContext.removeReplaceParameter(TenantScope.TenantReplaceParameter); this.claimExtractorContext.removeReplaceParameter(TenantScope.TenantReplaceParameter);
} }
@Override @Override
public void afterCompletion(@NonNull WebRequest request, Exception ex) { public void afterCompletion(@NonNull WebRequest request, Exception ex) {
} }
} }

View File

@ -1,18 +1,20 @@
package gr.cite.annotation.web.scope.tenant; package gr.cite.annotation.web.scope.tenant;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext; import gr.cite.annotation.authorization.ClaimNames;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.TenantEntity; import gr.cite.annotation.data.TenantEntity;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorContext;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Root;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
@ -21,7 +23,6 @@ import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.context.request.WebRequestInterceptor;
import javax.management.InvalidApplicationException;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -55,16 +56,22 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
} }
@Override @Override
public void preHandle(WebRequest request) throws InvalidApplicationException { public void preHandle(@NotNull WebRequest request) {
if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return; if (!this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) return;
if (!this.tenantScope.isMultitenant()) return; if (!this.tenantScope.isMultitenant()) return;
String tenantCode = request.getHeader(TenantScope.TenantClaimName); String tenantCode = request.getHeader(ClaimNames.TenantClaimName);
logger.debug("retrieved request tenant header is: {header}", tenantCode); logger.debug("retrieved request tenant header is: {}", tenantCode);
if (this.conventionService.isNullOrEmpty(tenantCode)) return; if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return;
if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){
logger.debug("parsed tenant header and set tenant to default tenant");
this.tenantScope.setTenant(null, tenantCode);
this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
return;
}
UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode);
if (tenantId == null && tenantCode == null) return;
if (tenantId == null) { if (tenantId == null) {
TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode)); TenantByCodeCacheService.TenantByCodeCacheValue cacheValue = this.tenantByCodeCacheService.lookup(this.tenantByCodeCacheService.buildKey(tenantCode));
if (cacheValue != null) { if (cacheValue != null) {
@ -85,8 +92,8 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
} }
} }
if (tenantId != null && tenantCode != null && !tenantCode.isBlank()) { if (tenantId != null) {
logger.debug("parsed tenant header and set tenant id to {tenant}", tenantId); logger.debug("parsed tenant header and set tenant id to {}", tenantId);
this.tenantScope.setTenant(tenantId, tenantCode); this.tenantScope.setTenant(tenantId, tenantCode);
this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode); this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
} }
@ -94,7 +101,7 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
private UUID getTenantIdFromDatabase(String tenantCode) { private UUID getTenantIdFromDatabase(String tenantCode) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -102,27 +109,16 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getId();
try {
o = results.get(0).get(TenantEntity._id);
} catch (IllegalArgumentException e) {
return null;
}
if (o == null) return null;
try {
return UUID.class.cast(o);
} catch (ClassCastException e) {
return null;
}
} }
return null; return null;
} }
private String getTenantCodeFromDatabase(UUID tenantId) { private String getTenantCodeFromDatabase(UUID tenantId) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -130,20 +126,9 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code)); ).multiselect(root.get(TenantEntity._code).alias(TenantEntity._code));
List<Tuple> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
Object o; return results.getFirst().getCode();
try {
o = results.get(0).get(TenantEntity._code);
} catch (IllegalArgumentException e) {
return null;
}
if (o == null) return null;
try {
return String.class.cast(o);
} catch (ClassCastException e) {
return null;
}
} }
return null; return null;
} }

View File

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

View File

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

View File

@ -14,77 +14,78 @@ import java.util.UUID;
@Service @Service
public class UserAllowedTenantCacheService extends CacheService<UserAllowedTenantCacheService.UserAllowedTenantCacheValue> { public class UserAllowedTenantCacheService extends CacheService<UserAllowedTenantCacheService.UserAllowedTenantCacheValue> {
public static class UserAllowedTenantCacheValue { public static class UserAllowedTenantCacheValue {
public UserAllowedTenantCacheValue() { public UserAllowedTenantCacheValue() {
} }
public UserAllowedTenantCacheValue(UUID userId, UUID tenantId, boolean isAllowed) { public UserAllowedTenantCacheValue(UUID userId, UUID tenantId, boolean isAllowed) {
this.userId = userId; this.userId = userId;
this.tenantId = tenantId; this.tenantId = tenantId;
this.isAllowed = isAllowed; this.isAllowed = isAllowed;
} }
private UUID userId; private UUID userId;
public UUID getUserId() { public UUID getUserId() {
return userId; return userId;
} }
public void setUserId(UUID userId) { public void setUserId(UUID userId) {
this.userId = userId; this.userId = userId;
} }
private UUID tenantId; private UUID tenantId;
public UUID getTenantId() { public UUID getTenantId() {
return tenantId; return tenantId;
} }
public void setTenantId(UUID tenantId) { public void setTenantId(UUID tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
private boolean isAllowed; private boolean isAllowed;
public boolean isAllowed() { public boolean isAllowed() {
return isAllowed; return isAllowed;
} }
public void setAllowed(boolean allowed) { public void setAllowed(boolean allowed) {
isAllowed = allowed; isAllowed = allowed;
} }
} }
@Autowired @Autowired
public UserAllowedTenantCacheService(UserAllowedTenantCacheOptions options) { public UserAllowedTenantCacheService(UserAllowedTenantCacheOptions options) {
super(options); super(options);
} }
@EventListener @EventListener
public void handleUserRemovedFromTenantEvent(UserRemovedFromTenantEvent event) { public void handleUserRemovedFromTenantEvent(UserRemovedFromTenantEvent event) {
this.evict(this.buildKey(event.getUserId(), event.getTenantId())); this.evict(this.buildKey(event.getUserId(), event.getTenantId()));
} }
@EventListener @EventListener
public void handleUserAddedToTenantEvent(UserAddedToTenantEvent event) { public void handleUserAddedToTenantEvent(UserAddedToTenantEvent event) {
this.evict(this.buildKey(event.getUserId(), event.getTenantId())); this.evict(this.buildKey(event.getUserId(), event.getTenantId()));
} }
@Override @Override
protected Class<UserAllowedTenantCacheValue> valueClass() { protected Class<UserAllowedTenantCacheValue> valueClass() {
return UserAllowedTenantCacheValue.class; return UserAllowedTenantCacheValue.class;
} }
@Override @Override
public String keyOf(UserAllowedTenantCacheValue value) { public String keyOf(UserAllowedTenantCacheValue value) {
return this.buildKey(value.getUserId(), value.getTenantId()); return this.buildKey(value.getUserId(), value.getTenantId());
} }
public String buildKey(UUID userId, UUID tenantId) { public String buildKey(UUID userId, UUID tenantId) {
return this.generateKey(new HashMap<>() {{ HashMap<String, String> keyParts = new HashMap<>();
put("$user_id$", userId.toString().toLowerCase(Locale.ROOT)); keyParts.put("$user_id$", userId.toString().toLowerCase(Locale.ROOT));
put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT)); keyParts.put("$tenant_id$", tenantId.toString().toLowerCase(Locale.ROOT));
}}); return this.generateKey(keyParts);
} }
} }

View File

@ -1,154 +1,81 @@
//package gr.cite.notification.web.scope.user; package gr.cite.annotation.web.scope.user;
//
//
//import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.annotation.common.scope.user.UserScope;
//import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; import gr.cite.annotation.data.UserCredentialEntity;
//import gr.cite.notification.common.enums.IsActive; import gr.cite.annotation.model.UserCredential;
//import gr.cite.notification.common.scope.user.UserScope; import gr.cite.annotation.query.UserCredentialQuery;
//import gr.cite.notification.data.UserEntity; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
//import gr.cite.notification.locale.LocaleService; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
//import gr.cite.tools.logging.LoggerService; import gr.cite.tools.data.query.QueryFactory;
//import org.slf4j.LoggerFactory; import gr.cite.tools.exception.MyForbiddenException;
//import org.springframework.beans.factory.annotation.Autowired; import gr.cite.tools.fieldset.BaseFieldSet;
//import org.springframework.lang.NonNull; import org.jetbrains.annotations.NotNull;
//import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.transaction.PlatformTransactionManager; import org.springframework.lang.NonNull;
//import org.springframework.transaction.TransactionDefinition; import org.springframework.stereotype.Component;
//import org.springframework.transaction.TransactionStatus; import org.springframework.ui.ModelMap;
//import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.web.context.request.WebRequest;
//import org.springframework.ui.ModelMap; import org.springframework.web.context.request.WebRequestInterceptor;
//import org.springframework.web.context.request.WebRequest;
//import org.springframework.web.context.request.WebRequestInterceptor; import java.util.UUID;
//
//import javax.management.InvalidApplicationException; @Component
//import jakarta.persistence.EntityManager; public class UserInterceptor implements WebRequestInterceptor {
//import jakarta.persistence.PersistenceContext; private final UserScope userScope;
//import jakarta.persistence.Tuple; private final ClaimExtractor claimExtractor;
//import jakarta.persistence.criteria.CriteriaBuilder; private final CurrentPrincipalResolver currentPrincipalResolver;
//import jakarta.persistence.criteria.CriteriaQuery; private final UserInterceptorCacheService userInterceptorCacheService;
//import jakarta.persistence.criteria.Root; private final QueryFactory queryFactory;
//import java.time.Instant;
//import java.util.List; @Autowired
//import java.util.UUID; public UserInterceptor(
// UserScope userScope,
//@Component ClaimExtractor claimExtractor,
//public class UserInterceptor implements WebRequestInterceptor { CurrentPrincipalResolver currentPrincipalResolver,
// private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class)); UserInterceptorCacheService userInterceptorCacheService,
// private final UserScope userScope; QueryFactory queryFactory) {
// private final ClaimExtractor claimExtractor; this.userScope = userScope;
// private final CurrentPrincipalResolver currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
// private final LocaleService localeService; this.claimExtractor = claimExtractor;
// private final PlatformTransactionManager transactionManager; this.userInterceptorCacheService = userInterceptorCacheService;
// private final UserInterceptorCacheService userInterceptorCacheService; this.queryFactory = queryFactory;
// @PersistenceContext }
// public EntityManager entityManager;
// @Override
// @Autowired public void preHandle(@NotNull WebRequest request) {
// public UserInterceptor( UUID userId = null;
// UserScope userScope, if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) {
// LocaleService localeService, String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());
// ClaimExtractor claimExtractor, if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed");
// CurrentPrincipalResolver currentPrincipalResolver,
// PlatformTransactionManager transactionManager, UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId));
// UserInterceptorCacheService userInterceptorCacheService if (cacheValue != null) {
// ) { userId = cacheValue.getUserId();
// this.userScope = userScope; } else {
// this.localeService = localeService; userId = this.findExistingUserFromDb(subjectId);
// this.currentPrincipalResolver = currentPrincipalResolver; if (userId != null) {
// this.claimExtractor = claimExtractor; cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId);
// this.transactionManager = transactionManager; this.userInterceptorCacheService.put(cacheValue);
// this.userInterceptorCacheService = userInterceptorCacheService; }
// } }
// }
// @Override this.userScope.setUserId(userId);
// public void preHandle(WebRequest request) throws InvalidApplicationException { }
// UUID userId = null; private UUID findExistingUserFromDb(String subjectId) {
// if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user));
// String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); if (userCredential != null) {
// return userCredential.getUserId();
// UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId)); }
// if (cacheValue != null) { return null;
// userId = cacheValue.getUserId(); }
// } else { @Override
// userId = this.getUserIdFromDatabase(subjectId); public void postHandle(@NonNull WebRequest request, ModelMap model) {
// if (userId == null) userId = this.createUser(subjectId); this.userScope.setUserId(null);
// }
// this.userInterceptorCacheService.put(new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId));
// } @Override
// } public void afterCompletion(@NonNull WebRequest request, Exception ex) {
// this.userScope.setUserId(userId); }
// } }
//
// private UUID getUserIdFromDatabase(String subjectId) throws InvalidApplicationException {
// CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
// CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class);
// Root<UserEntity> root = query.from(UserEntity.class);
// query.where(
// criteriaBuilder.and(
//// criteriaBuilder.equal(root.get(UserEntity._subjectId), subjectId),
// criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active)
// ));
//
// query.multiselect(root.get(UserEntity._id).alias(UserEntity._id));
//
// List<Tuple> results = this.entityManager.createQuery(query).getResultList();
// if (results.size() == 1) {
// Object o;
// try {
// o = results.get(0).get(UserEntity._id);
// } catch (IllegalArgumentException e) {
// return null;
// }
// if (o == null) return null;
// try {
// return UUID.class.cast(o);
// } catch (ClassCastException e) {
// return null;
// }
// }
// return null;
// }
//
// private UUID createUser(String subjectId) {
// String name = this.claimExtractor.name(this.currentPrincipalResolver.currentPrincipal());
// String familyName = this.claimExtractor.familyName(this.currentPrincipalResolver.currentPrincipal());
// if (name == null) name = subjectId;
// UserEntity user = new UserEntity();
// user.setId(UUID.randomUUID());
// user.setCreatedAt(Instant.now());
// user.setUpdatedAt(Instant.now());
// user.setName(name);
//// user.setLastName(familyName == null ? name : familyName);
// user.setIsActive(IsActive.Active);
//// user.setSubjectId(subjectId);
//// user.setCulture(this.localeService.cultureName());
//// user.setTimezone(this.localeService.timezoneName());
//// user.setLanguage(this.localeService.language());
//
// DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// definition.setName(UUID.randomUUID().toString());
// definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
// definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// TransactionStatus status = null;
// try {
// status = transactionManager.getTransaction(definition);
// this.entityManager.persist(user);
//
// this.entityManager.flush();
// transactionManager.commit(status);
// } catch (Exception ex) {
// if (status != null) transactionManager.rollback(status);
// throw ex;
// }
// return user.getId();
// }
//
// @Override
// public void postHandle(@NonNull WebRequest request, ModelMap model) {
// this.userScope.setUserId(null);
// }
//
// @Override
// public void afterCompletion(@NonNull WebRequest request, Exception ex) {
// }
//}

View File

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

View File

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

View File

@ -10,7 +10,7 @@ spring:
dialect: org.hibernate.dialect.PostgreSQLDialect dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate: hibernate:
naming: naming:
physical-strategy: gr.cite.notification.config.db.PrefixPhysicalNamingStrategy physical-strategy: gr.cite.annotation.data.namingstrategy.PrefixPhysicalNamingStrategy
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
datasource: datasource:
url: ${DB_CONNECTION_STRING} url: ${DB_CONNECTION_STRING}

View File

@ -41,3 +41,9 @@ error-thesaurus:
overlapping-tenant-configuration-notifier-list: overlapping-tenant-configuration-notifier-list:
code: 119 code: 119
message: Overlapping Tenant Configuration Notifier List message: Overlapping Tenant Configuration Notifier List
tenant-not-allowed:
code: 113
message: tenant not allowed
tenant-tampering:
code: 123
message: Tenant tampering

View File

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

View File

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

View File

@ -2,7 +2,6 @@ tenant:
multitenancy: multitenancy:
is-multitenant: false is-multitenant: false
interceptor: interceptor:
client-claims-prefix: client_
white-listed-clients: [ ] white-listed-clients: [ ]
enforce-trusted-tenant: false enforce-trusted-tenant: false
white-listed-endpoints: [ '/api/principal/my-tenants', '/api/principal/me','/api/user/user-settings', '/error', '/api/tenant-request' ] white-listed-endpoints: [ '/api/principal/me' ]

View File

@ -0,0 +1,9 @@
package gr.cite.annotation.authorization;
public class ClaimNames {
public static final String ExternalProviderName = "ExternalProviderName";
public static final String TenantCodesClaimName = "TenantCodes";
public static final String TenantClaimName = "x-tenant";
public static final String GlobalRolesClaimName = "GlobalRoles";
public static final String TenantRolesClaimName = "TenantRoles";
}

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.conventers.AnnotationProtectionTypeConverter; import gr.cite.annotation.data.conventers.AnnotationProtectionTypeConverter;
import gr.cite.annotation.data.conventers.IsActiveConverter; import gr.cite.annotation.data.conventers.IsActiveConverter;
import gr.cite.annotation.data.tenant.TenantScopedBaseEntity;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.time.Instant; import java.time.Instant;
@ -11,7 +12,7 @@ import java.util.UUID;
@Entity @Entity
@Table(name = "\"Annotation\"") @Table(name = "\"Annotation\"")
public class AnnotationEntity { public class AnnotationEntity extends TenantScopedBaseEntity {
@Id @Id
@Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false)

View File

@ -2,6 +2,7 @@ package gr.cite.annotation.data;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.conventers.IsActiveConverter; import gr.cite.annotation.data.conventers.IsActiveConverter;
import gr.cite.annotation.data.tenant.TenantScopedBaseEntity;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.time.Instant; import java.time.Instant;
@ -9,7 +10,7 @@ import java.util.UUID;
@Entity @Entity
@Table(name = "\"EntityUser\"") @Table(name = "\"EntityUser\"")
public class EntityUserEntity { public class EntityUserEntity extends TenantScopedBaseEntity {
@Id @Id
@Column(name = "\"id\"", columnDefinition = "uuid", updatable = false, nullable = false) @Column(name = "\"id\"", columnDefinition = "uuid", updatable = false, nullable = false)

View File

@ -0,0 +1,115 @@
package gr.cite.annotation.data;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.common.scope.tenant.TenantScoped;
import gr.cite.annotation.data.tenant.TenantScopedBaseEntity;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.exception.MyForbiddenException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.PersistenceContext;
import org.hibernate.Session;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
import javax.management.InvalidApplicationException;
@Service
@RequestScope
public class TenantEntityManager {
@PersistenceContext
private EntityManager entityManager;
private final TenantScope tenantScope;
private final ErrorThesaurusProperties errors;
public TenantEntityManager(TenantScope tenantScope, ErrorThesaurusProperties errors) {
this.tenantScope = tenantScope;
this.errors = errors;
}
public void persist(Object entity) {
this.entityManager.persist(entity);
}
public <T> T merge(T entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) {
if (!tenantScope.isDefaultTenant()) {
if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
} else if (tenantScopedEntity.getTenantId() != null) {
throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
}
}
return this.entityManager.merge(entity);
}
public void remove(Object entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) {
if (!tenantScope.isDefaultTenant()) {
if (tenantScopedEntity.getTenantId() == null || !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
} else if (tenantScopedEntity.getTenantId() != null) {
throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
}
}
this.entityManager.remove(entity);
}
public <T> T find(Class<T> entityClass, Object primaryKey) throws InvalidApplicationException {
T entity = this.entityManager.find(entityClass, primaryKey);
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped tenantScopedEntity)) {
if (tenantScopedEntity.getTenantId() != null && !tenantScopedEntity.getTenantId().equals(tenantScope.getTenant())) return null;
}
return entity;
}
public void flush() {
this.entityManager.flush();
}
public void setFlushMode(FlushModeType flushMode) {
this.entityManager.setFlushMode(flushMode);
}
public FlushModeType getFlushMode() {
return this.entityManager.getFlushMode();
}
public void clear() {
this.entityManager.clear();
}
public void enableTenantFilters() throws InvalidApplicationException {
if(!tenantScope.isDefaultTenant()) {
this.entityManager
.unwrap(Session.class)
.enableFilter(TenantScopedBaseEntity.TENANT_FILTER)
.setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString());
} else {
this.entityManager
.unwrap(Session.class)
.enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER);
}
}
public void disableTenantFilters(){
this.entityManager
.unwrap(Session.class)
.disableFilter(TenantScopedBaseEntity.TENANT_FILTER);
this.entityManager
.unwrap(Session.class)
.disableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER);
}
public EntityManager getEntityManager() {
return entityManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
}

View File

@ -1,97 +0,0 @@
package gr.cite.annotation.data;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.common.scope.tenant.TenantScoped;
import gr.cite.tools.exception.MyForbiddenException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import org.hibernate.Session;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.util.UUID;
@Service
@Scope
public class TenantScopedEntityManager {
@PersistenceContext
private EntityManager entityManager;
private final AuthorizationService authorizationService;
private final TenantScope tenantScope;
public TenantScopedEntityManager(AuthorizationService authorizationService, TenantScope tenantScope) {
this.authorizationService = authorizationService;
this.tenantScope = tenantScope;
}
public int getBulkSize() {
Session session = this.entityManager.unwrap(Session.class);
return session.getJdbcBatchSize();
}
public void setBulkSize(int size) {
Session session = this.entityManager.unwrap(Session.class);
session.setJdbcBatchSize(size);
}
public Query createQuery(String query){
return this.entityManager.createQuery(query);
}
public void persist(Object entity) {
this.entityManager.persist(entity);
}
public <T> T merge(T entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) {
boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant);
final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null;
if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering");
}
return this.entityManager.merge(entity);
}
public void remove(Object entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) {
final UUID tenantId = tenantScope.getTenant();
if (!tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering");
}
this.entityManager.remove(entity);
}
public <T> T find(Class<T> entityClass, Object primaryKey) throws InvalidApplicationException {
T entity = this.entityManager.find(entityClass, primaryKey);
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) {
boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant);
final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null;
if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) return null;
}
return entity;
}
public void flush() {
this.entityManager.flush();
}
public void setFlushMode(FlushModeType flushMode) {
this.entityManager.setFlushMode(flushMode);
}
public FlushModeType getFlushMode() {
return this.entityManager.getFlushMode();
}
public void clear() {
this.entityManager.clear();
}
}

View File

@ -12,7 +12,7 @@ import java.util.UUID;
@Entity @Entity
@Table(name = "\"UserContactInfo\"") @Table(name = "\"UserContactInfo\"")
public class UserContactInfoEntity extends TenantScopedBaseEntity { public class UserContactInfoEntity {
@Id @Id
@Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false)

View File

@ -10,7 +10,7 @@ import java.util.UUID;
@Entity @Entity
@Table(name = "\"UserCredential\"") @Table(name = "\"UserCredential\"")
public class UserCredentialEntity extends TenantScopedBaseEntity { public class UserCredentialEntity {
@Id @Id
@Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false) @Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false)

View File

@ -1,4 +1,4 @@
package gr.cite.annotation.config.db; package gr.cite.annotation.data.namingstrategy;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding; import org.springframework.boot.context.properties.bind.ConstructorBinding;

View File

@ -1,4 +1,4 @@
package gr.cite.annotation.config.db; package gr.cite.annotation.data.namingstrategy;
import gr.cite.annotation.common.StringUtils; import gr.cite.annotation.common.StringUtils;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;

View File

@ -1,7 +1,5 @@
package gr.cite.annotation.data.tenant; package gr.cite.annotation.data.tenant;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.common.scope.tenant.TenantScope;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
@ -9,7 +7,6 @@ import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.hibernate.Session; import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
@ -19,30 +16,28 @@ import javax.management.InvalidApplicationException;
public class TenantFilterAspect { public class TenantFilterAspect {
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final ClaimExtractor claimExtractor;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final ApplicationContext applicationContext;
@Autowired @Autowired
public TenantFilterAspect( public TenantFilterAspect(
TenantScope tenantScope, TenantScope tenantScope
ClaimExtractor claimExtractor,
CurrentPrincipalResolver currentPrincipalResolver,
ApplicationContext applicationContext
) { ) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor;
this.applicationContext = applicationContext;
} }
@AfterReturning( @AfterReturning(
pointcut="bean(entityManagerFactory) && execution(* createEntityManager(..))", pointcut = "bean(entityManagerFactory) && execution(* createEntityManager(..))",
returning="retVal") returning = "retVal")
public void getSessionAfter(JoinPoint joinPoint, Object retVal) throws InvalidApplicationException { public void getSessionAfter(JoinPoint joinPoint, Object retVal) throws InvalidApplicationException {
if (retVal != null && EntityManager.class.isInstance(retVal) && tenantScope.isSet() && tenantScope.isMultitenant()) { if (retVal instanceof EntityManager && tenantScope.isSet()) {
Session session = ((EntityManager) retVal).unwrap(Session.class); Session session = ((EntityManager) retVal).unwrap(Session.class);
session.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); if(!tenantScope.isDefaultTenant()) {
session
.enableFilter(TenantScopedBaseEntity.TENANT_FILTER)
.setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString());
} else {
session
.enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER);
}
} }
} }

View File

@ -2,6 +2,7 @@ package gr.cite.annotation.data.tenant;
import gr.cite.annotation.common.scope.tenant.TenantScope; import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.common.scope.tenant.TenantScoped; import gr.cite.annotation.common.scope.tenant.TenantScoped;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.PrePersist; import jakarta.persistence.PrePersist;
@ -17,18 +18,28 @@ public class TenantListener {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantListener.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantListener.class));
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final ErrorThesaurusProperties errors;
@Autowired @Autowired
public TenantListener( public TenantListener(
TenantScope tenantScope TenantScope tenantScope, ErrorThesaurusProperties errors
) { ) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.errors = errors;
} }
@PrePersist @PrePersist
public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant()) { if (tenantScope.isMultitenant()) {
final UUID tenantId = tenantScope.getTenant(); if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) {
entity.setTenantId(tenantId); logger.error("somebody tried to set not login tenant");
throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
}
if (!tenantScope.isDefaultTenant()) {
final UUID tenantId = tenantScope.getTenant();
entity.setTenantId(tenantId);
}
} else { } else {
entity.setTenantId(null); entity.setTenantId(null);
} }
@ -38,22 +49,30 @@ public class TenantListener {
@PreRemove @PreRemove
public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant()) { if (tenantScope.isMultitenant()) {
if (entity.getTenantId() == null) { if (!tenantScope.isDefaultTenant()) {
logger.error("somebody tried to set null tenant"); if (entity.getTenantId() == null) {
throw new MyForbiddenException("tenant tampering"); logger.error("somebody tried to set null tenant");
} throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { }
logger.error("somebody tried to change an entries tenant"); if (entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) {
throw new MyForbiddenException("tenant tampering"); logger.error("somebody tried to change an entries tenant");
} throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
}
final UUID tenantId = tenantScope.getTenant(); final UUID tenantId = tenantScope.getTenant();
entity.setTenantId(tenantId); entity.setTenantId(tenantId);
} else {
if (entity.getTenantId() != null) {
logger.error("somebody tried to set null tenant");
throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
}
}
} else { } else {
if (entity.getTenantId() != null) { if (entity.getTenantId() != null && (!this.tenantScope.isDefaultTenant() ||entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) {
logger.error("somebody tried to set non null tenant"); logger.error("somebody tried to change an entries tenant");
throw new MyForbiddenException("tenant tampering"); throw new MyForbiddenException(this.errors.getTenantTampering().getCode(), this.errors.getTenantTampering().getMessage());
} }
} }
} }
} }

View File

@ -16,23 +16,20 @@ import java.util.UUID;
//@Getter //@Getter
//@Setter //@Setter
//@NoArgsConstructor //@NoArgsConstructor
@FilterDef(name = TenantScopedBaseEntity.tenantFilter, parameters = {@ParamDef(name = TenantScopedBaseEntity.tenantFilterTenantParam, type = String.class)}) @FilterDef(name = TenantScopedBaseEntity.TENANT_FILTER, parameters = {@ParamDef(name = TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, type = String.class)})
@Filter(name = "tenantFilter", condition = "tenant = (cast(:tenantId as uuid))") @FilterDef(name = TenantScopedBaseEntity.DEFAULT_TENANT_FILTER)
@Filter(name = TenantScopedBaseEntity.DEFAULT_TENANT_FILTER, condition = "(tenant = tenant is null)")
@Filter(name = TenantScopedBaseEntity.TENANT_FILTER, condition = "(tenant = (cast(:tenantId as uuid)) or tenant is null)")
@EntityListeners(TenantListener.class) @EntityListeners(TenantListener.class)
public abstract class TenantScopedBaseEntity implements TenantScoped, Serializable { public abstract class TenantScopedBaseEntity implements TenantScoped, Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final String TENANT_FILTER = "tenantFilter";
public static final String DEFAULT_TENANT_FILTER = "defaultTenantFilter";
public static final String TENANT_FILTER_TENANT_PARAM = "tenantId";
public static final String tenantFilter = "tenantFilter"; @Column(name = "tenant", columnDefinition = "uuid", nullable = true)
public static final String tenantFilterTenantParam = "tenantId";
@Column(name = "tenant", columnDefinition = "uuid")
private UUID tenantId; private UUID tenantId;
public static final String _tenantId = "tenantId"; public static final String _tenantId = "tenantId";
public UUID getTenantId() { public UUID getTenantId() {
return tenantId; return tenantId;
} }
@ -41,5 +38,5 @@ public abstract class TenantScopedBaseEntity implements TenantScoped, Serializab
public void setTenantId(UUID tenantId) { public void setTenantId(UUID tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
} }

View File

@ -15,6 +15,8 @@ public class ErrorThesaurusProperties {
private ErrorDescription singleTenantConfigurationPerTypeSupported; private ErrorDescription singleTenantConfigurationPerTypeSupported;
private ErrorDescription incompatibleTenantConfigurationTypes; private ErrorDescription incompatibleTenantConfigurationTypes;
private ErrorDescription overlappingTenantConfigurationNotifierList; private ErrorDescription overlappingTenantConfigurationNotifierList;
private ErrorDescription tenantNotAllowed;
private ErrorDescription tenantTampering;
public ErrorDescription getHashConflict() { public ErrorDescription getHashConflict() {
return hashConflict; return hashConflict;
@ -89,4 +91,19 @@ public class ErrorThesaurusProperties {
this.overlappingTenantConfigurationNotifierList = overlappingTenantConfigurationNotifierList; this.overlappingTenantConfigurationNotifierList = overlappingTenantConfigurationNotifierList;
} }
public ErrorDescription getTenantNotAllowed() {
return tenantNotAllowed;
}
public void setTenantNotAllowed(ErrorDescription tenantNotAllowed) {
this.tenantNotAllowed = tenantNotAllowed;
}
public ErrorDescription getTenantTampering() {
return tenantTampering;
}
public void setTenantTampering(ErrorDescription tenantTampering) {
this.tenantTampering = tenantTampering;
}
} }

View File

@ -17,6 +17,7 @@ import gr.cite.queueinbox.entity.QueueInboxStatus;
import gr.cite.queueinbox.repository.CandidateInfo; import gr.cite.queueinbox.repository.CandidateInfo;
import gr.cite.queueinbox.repository.InboxRepository; import gr.cite.queueinbox.repository.InboxRepository;
import gr.cite.queueinbox.task.MessageOptions; import gr.cite.queueinbox.task.MessageOptions;
import gr.cite.rabbitmq.IntegrationEventMessageConstants;
import gr.cite.rabbitmq.consumer.InboxCreatorParams; import gr.cite.rabbitmq.consumer.InboxCreatorParams;
import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -242,7 +243,8 @@ public class InboxRepositoryImpl implements InboxRepository {
QueueInboxEntity queueMessage = new QueueInboxEntity(); QueueInboxEntity queueMessage = new QueueInboxEntity();
queueMessage.setId(UUID.randomUUID()); queueMessage.setId(UUID.randomUUID());
queueMessage.setTenantId(null); Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null;
if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId);
queueMessage.setExchange(this.inboxProperties.getExchange()); queueMessage.setExchange(this.inboxProperties.getExchange());
queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey());
queueMessage.setQueue(inboxCreatorParams.getQueueName()); queueMessage.setQueue(inboxCreatorParams.getQueueName());
@ -279,7 +281,7 @@ public class InboxRepositoryImpl implements InboxRepository {
logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId()); logger.warn("Could not lookup queue inbox {} to process. Continuing...", candidateInfo.getId());
} else { } else {
EventProcessingStatus status = this.processMessage(queueInboxMessage.getRoute(), queueInboxMessage.getMessageId().toString(), queueInboxMessage.getApplicationId(), queueInboxMessage.getMessage()); EventProcessingStatus status = this.processMessage(queueInboxMessage);
switch (status) { switch (status) {
case Success: { case Success: {
queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL); queueInboxMessage.setStatus(QueueInboxStatus.SUCCESSFUL);
@ -322,23 +324,23 @@ public class InboxRepositoryImpl implements InboxRepository {
return success; return success;
} }
private EventProcessingStatus processMessage(String routingKey, String messageId, String appId, String message) { private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) {
IntegrationEventHandler handler; IntegrationEventHandler handler;
logger.debug("Processing message with routing key '{}'", routingKey); logger.debug("Processing message with routing key '{}'", queueInboxMessage.getRoute());
if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantRemovalTopic())) if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantRemovalTopic()))
handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class); handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getTenantTouchTopic())) else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantTouchTopic()))
handler = this.applicationContext.getBean(TenantTouchedIntegrationEventHandler.class); handler = this.applicationContext.getBean(TenantTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserRemovalTopic())) else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getUserRemovalTopic()))
handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchTopic())) else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getUserTouchTopic()))
handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesTouchTopic())) else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getAnnotationEntitiesTouchTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesTouchedIntegrationEventHandler.class); handler = this.applicationContext.getBean(AnnotationEntitiesTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesRemovalTopic())) else if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getAnnotationEntitiesRemovalTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesRemovalIntegrationEventHandler.class); handler = this.applicationContext.getBean(AnnotationEntitiesRemovalIntegrationEventHandler.class);
else { else {
logger.error("No handler found for message routing key '{}'. Discarding.", routingKey); logger.error("No handler found for message routing key '{}'. Discarding.", queueInboxMessage.getRoute());
handler = null; handler = null;
} }
@ -346,16 +348,17 @@ public class InboxRepositoryImpl implements InboxRepository {
return EventProcessingStatus.Discard; return EventProcessingStatus.Discard;
IntegrationEventProperties properties = new IntegrationEventProperties(); IntegrationEventProperties properties = new IntegrationEventProperties();
properties.setAppId(appId); properties.setAppId(queueInboxMessage.getApplicationId());
properties.setMessageId(messageId); properties.setMessageId(queueInboxMessage.getMessageId().toString());
properties.setTenantId(queueInboxMessage.getTenantId());
TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, message); TrackedEvent event = this.jsonHandlingService.fromJsonSafe(TrackedEvent.class, queueInboxMessage.getMessage());
// using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag)) // using (LogContext.PushProperty(this._logTrackingConfig.LogTrackingContextName, @event.TrackingContextTag))
// { // {
try { try {
return handler.handle(properties, message); return handler.handle(properties, queueInboxMessage.getMessage());
} catch (Exception ex) { } catch (Exception ex) {
logger.error("problem handling event from routing key " + routingKey + ". Setting nack and continuing...", ex); logger.error("problem handling event from routing key " + queueInboxMessage.getRoute() + ". Setting nack and continuing...", ex);
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
// } // }

View File

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

View File

@ -4,15 +4,21 @@ import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope; import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.data.EntityUserEntity; import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.data.TenantEntity;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.query.EntityUserQuery; import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.annotation.query.TenantQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -62,6 +68,20 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
EntityTransaction transaction = null; EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
TenantScope scope = this.applicationContext.getBean(TenantScope.class);
if (scope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) {
logger.error("missing tenant from event message");
return EventProcessingStatus.Error;
}
scope.setTenant(properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) {
// logger.error("missing tenant from event message");
// return EventProcessingStatus.Error;
scope.setTenant(null, scope.getDefaultTenantCode());
}
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties)); currentPrincipalResolver.push(InboxPrincipal.build(properties));
@ -75,6 +95,9 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
try { try {
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
tenantEntityManager.disableTenantFilters();
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> items = entityUserQuery List<EntityUserEntity> items = entityUserQuery
.entityIds(event.getEntityIds()) .entityIds(event.getEntityIds())

View File

@ -4,15 +4,21 @@ import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope; import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.data.EntityUserEntity; import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.data.TenantEntity;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus; import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal; import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.query.EntityUserQuery; import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.annotation.query.TenantQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -64,6 +70,20 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
EntityTransaction transaction = null; EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
TenantScope scope = this.applicationContext.getBean(TenantScope.class);
if (scope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) {
logger.error("missing tenant from event message");
return EventProcessingStatus.Error;
}
scope.setTenant(properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) {
// logger.error("missing tenant from event message");
// return EventProcessingStatus.Error;
scope.setTenant(null, scope.getDefaultTenantCode());
}
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties)); currentPrincipalResolver.push(InboxPrincipal.build(properties));
@ -76,6 +96,9 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
transaction.begin(); transaction.begin();
try { try {
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
tenantEntityManager.disableTenantFilters();
for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) { for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) {
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.integrationevent.inbox.tenantremoval; package gr.cite.annotation.integrationevent.inbox.tenantremoval;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
@ -35,15 +36,10 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final ErrorThesaurusProperties errors;
private final MessageSource messageSource; public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext) {
public TenantRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ApplicationContext applicationContext, ErrorThesaurusProperties errors, MessageSource messageSource) {
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.errors = errors;
this.messageSource = messageSource;
} }
@Override @Override
@ -70,6 +66,9 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
transaction.begin(); transaction.begin();
try { try {
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
tenantEntityManager.disableTenantFilters();
TenantService tenantService = this.applicationContext.getBean(TenantService.class); TenantService tenantService = this.applicationContext.getBean(TenantService.class);
tenantService.deleteAndSave(event.getId()); tenantService.deleteAndSave(event.getId());

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.integrationevent.inbox.tenanttouch; package gr.cite.annotation.integrationevent.inbox.tenanttouch;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
@ -67,6 +68,9 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn
transaction.begin(); transaction.begin();
try { try {
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
tenantEntityManager.disableTenantFilters();
TenantService tenantService = this.applicationContext.getBean(TenantService.class); TenantService tenantService = this.applicationContext.getBean(TenantService.class);
tenantService.persist(model, null); tenantService.persist(model, null);

View File

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

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.integrationevent.inbox.userremoval; package gr.cite.annotation.integrationevent.inbox.userremoval;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
@ -75,16 +76,16 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
TenantScope scope = this.applicationContext.getBean(TenantScope.class); TenantScope scope = this.applicationContext.getBean(TenantScope.class);
if (scope.isMultitenant() && event.getTenant() != null) { if (scope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(event.getTenant(), tenant.getCode()); scope.setTenant(properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (scope.isMultitenant()) {
logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
return EventProcessingStatus.Error; scope.setTenant(null, scope.getDefaultTenantCode());
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
@ -101,6 +102,8 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
transaction.begin(); transaction.begin();
try { try {
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
UserService userService = this.applicationContext.getBean(UserService.class); UserService userService = this.applicationContext.getBean(UserService.class);
userService.deleteAndSave(event.getUserId()); userService.deleteAndSave(event.getUserId());

View File

@ -6,6 +6,7 @@ import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent; import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.annotation.model.persist.UserTouchedIntegrationEventPersist; import gr.cite.annotation.model.persist.UserTouchedIntegrationEventPersist;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification; import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -23,22 +24,24 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
public static final String _id = "id"; public static final String _id = "id";
private UUID tenant;
private String name; private String name;
public static final String _name = "name"; public static final String _name = "name";
public static final int _nameLength = 200; public static final int _nameLength = 200;
private String subjectId;
public static final String _subjectId = "subjectId";
private UserProfile profile; private UserProfile profile;
private List<UserContactInfo> userContactInfo; private List<UserContactInfo> userContactInfo;
public static final String _userContactInfo = "userContactInfo";
private List<TenantUser> tenantUsers;
public static final String _tenantUsers = "tenantUsers";
private List<UserCredential> credentials;
public static final String _credentials = "credentials";
public UUID getId() { public UUID getId() {
return id; return id;
} }
@ -47,14 +50,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.id = id; this.id = id;
} }
public UUID getTenant() {
return tenant;
}
public void setTenant(UUID tenant) {
this.tenant = tenant;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -63,12 +58,20 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.name = name; this.name = name;
} }
public String getSubjectId() { public List<TenantUser> getTenantUsers() {
return subjectId; return tenantUsers;
} }
public void setSubjectId(String subjectId) { public void setTenantUsers(List<TenantUser> tenantUsers) {
this.subjectId = subjectId; this.tenantUsers = tenantUsers;
}
public List<UserCredential> getCredentials() {
return credentials;
}
public void setCredentials(List<UserCredential> credentials) {
this.credentials = credentials;
} }
public UserProfile getProfile() { public UserProfile getProfile() {
@ -124,9 +127,15 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private ContactInfoType type; private ContactInfoType type;
public static final String _type = "type";
private String value; private String value;
private int ordinal; public static final String _value = "value";
private Integer ordinal;
public static final String _ordinal = "ordinal";
public ContactInfoType getType() { public ContactInfoType getType() {
return type; return type;
@ -144,13 +153,132 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.value = value; this.value = value;
} }
public int getOrdinal() { public Integer getOrdinal() {
return ordinal; return ordinal;
} }
public void setOrdinal(int ordinal) { public void setOrdinal(Integer ordinal) {
this.ordinal = ordinal; this.ordinal = ordinal;
} }
@Component(UserTouchedIntegrationEvent.UserContactInfo.UserTouchedIntegrationUserContactInfoEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UserTouchedIntegrationUserContactInfoEventValidator extends BaseValidator<UserContactInfo> {
public static final String ValidatorName = "UserTouchedIntegrationUserContactInfoEventValidator";
private final MessageSource messageSource;
protected UserTouchedIntegrationUserContactInfoEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<UserContactInfo> modelClass() {
return UserContactInfo.class;
}
@Override
protected List<Specification> specifications(UserContactInfo item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isEmpty(item.getValue()))
.failOn(UserContactInfo._value).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._value}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getType()))
.failOn(UserContactInfo._type).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._type}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getOrdinal()))
.failOn(UserContactInfo._ordinal).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserContactInfo._ordinal}, LocaleContextHolder.getLocale()))
);
}
}
}
public static class UserCredential {
private String subjectId;
public static final String _subjectId = "subjectId";
public String getSubjectId() {
return subjectId;
}
public void setSubjectId(String subjectId) {
this.subjectId = subjectId;
}
@Component(UserCredential.UserTouchedIntegrationUserCredentialEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UserTouchedIntegrationUserCredentialEventValidator extends BaseValidator<UserCredential> {
public static final String ValidatorName = "UserTouchedIntegrationUserCredentialEventValidator";
private final MessageSource messageSource;
protected UserTouchedIntegrationUserCredentialEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<UserCredential> modelClass() {
return UserCredential.class;
}
@Override
protected List<Specification> specifications(UserCredential item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isEmpty(item.getSubjectId()))
.failOn(UserCredential._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserCredential._subjectId}, LocaleContextHolder.getLocale()))
);
}
}
}
public static class TenantUser {
private UUID tenant;
public static final String _tenant = "tenant";
public UUID getTenant() {
return tenant;
}
public void setTenant(UUID tenant) {
this.tenant = tenant;
}
@Component(TenantUser.UserTouchedIntegrationTenantUserEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class UserTouchedIntegrationTenantUserEventValidator extends BaseValidator<TenantUser> {
public static final String ValidatorName = "UserTouchedIntegrationTenantUserEventValidator";
private final MessageSource messageSource;
protected UserTouchedIntegrationTenantUserEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<TenantUser> modelClass() {
return TenantUser.class;
}
@Override
protected List<Specification> specifications(TenantUser item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isNull(item.getTenant()))
.failOn(TenantUser._tenant).failWith(messageSource.getMessage("Validation_Required", new Object[]{TenantUser._tenant}, LocaleContextHolder.getLocale()))
);
}
}
} }
@Component(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.ValidatorName) @Component(UserTouchedIntegrationEvent.UserTouchedIntegrationEventValidator.ValidatorName)
@ -161,9 +289,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private final MessageSource messageSource; private final MessageSource messageSource;
protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) { private final ValidatorFactory validatorFactory;
protected UserTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors); super(conventionService, errors);
this.messageSource = messageSource; this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
} }
@Override @Override
@ -185,10 +316,12 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
.iff(() -> !this.isEmpty(item.getName())) .iff(() -> !this.isEmpty(item.getName()))
.must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEventPersist._nameLength)) .must(() -> this.lessEqualLength(item.getName(), UserTouchedIntegrationEventPersist._nameLength))
.failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())), .failOn(UserTouchedIntegrationEvent._name).failWith(messageSource.getMessage("Validation_MaxLength", new Object[]{UserTouchedIntegrationEvent._name}, LocaleContextHolder.getLocale())),
this.spec() this.navSpec()
.must(() -> !this.isEmpty(item.getSubjectId())) .iff(() -> !this.isListNullOrEmpty(item.getUserContactInfo()))
.failOn(UserTouchedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{UserTouchedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale())) .on(UserTouchedIntegrationEvent._userContactInfo)
); .over(item.getUserContactInfo())
.using((i) -> this.validatorFactory.validator(UserContactInfo.UserTouchedIntegrationUserContactInfoEventValidator.class))
);
} }
} }
} }

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.integrationevent.inbox.usertouch; package gr.cite.annotation.integrationevent.inbox.usertouch;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.annotation.audit.AuditableAction; import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
@ -67,20 +68,18 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
TenantScope scope = this.applicationContext.getBean(TenantScope.class); TenantScope scope = this.applicationContext.getBean(TenantScope.class);
if (scope.isMultitenant() && event.getTenant() != null) { if (scope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code)); TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) { if (tenant == null) {
logger.error("missing tenant from event message"); logger.error("missing tenant from event message");
return EventProcessingStatus.Error; return EventProcessingStatus.Error;
} }
scope.setTenant(event.getTenant(), tenant.getCode()); scope.setTenant(properties.getTenantId(), tenant.getCode());
} else if (scope.isMultitenant()) { } else if (scope.isMultitenant()) {
logger.error("missing tenant from event message"); // logger.error("missing tenant from event message");
return EventProcessingStatus.Error; // return EventProcessingStatus.Error;
scope.setTenant(null, scope.getDefaultTenantCode());
} }
//
// ValidationService validator = this.applicationContext.getBean(ValidationService.class);
// validator.validateForce(model);
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties)); currentPrincipalResolver.push(InboxPrincipal.build(properties));
@ -92,6 +91,8 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
transaction.begin(); transaction.begin();
try { try {
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
UserService userService = this.applicationContext.getBean(UserService.class); UserService userService = this.applicationContext.getBean(UserService.class);
userService.persist(event, null); userService.persist(event, null);

View File

@ -7,52 +7,13 @@ public class OutboxProperties {
private final String exchange; private final String exchange;
private final String tenantTouchTopic;
private final String tenantRemovalTopic; public OutboxProperties(String exchange
private final String userTouchTopic;
private final String userRemovalTopic;
private final String notifyTopic;
public OutboxProperties(String exchange,
String tenantTouchTopic,
String tenantRemovalTopic,
String userTouchTopic,
String userRemovalTopic,
String notifyTopic
) { ) {
this.exchange = exchange; this.exchange = exchange;
this.tenantTouchTopic = tenantTouchTopic;
this.tenantRemovalTopic = tenantRemovalTopic;
this.userTouchTopic = userTouchTopic;
this.userRemovalTopic = userRemovalTopic;
this.notifyTopic = notifyTopic;
} }
public String getExchange() { public String getExchange() {
return exchange; return exchange;
} }
public String getTenantTouchTopic() {
return tenantTouchTopic;
}
public String getTenantRemovalTopic() {
return tenantRemovalTopic;
}
public String getUserTouchTopic() {
return userTouchTopic;
}
public String getUserRemovalTopic() {
return userRemovalTopic;
}
public String getNotifyTopic() {
return notifyTopic;
}
} }

View File

@ -23,7 +23,6 @@ import org.springframework.context.ApplicationContext;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -31,9 +30,11 @@ import java.util.stream.Collectors;
public class OutboxRepositoryImpl implements OutboxRepository { public class OutboxRepositoryImpl implements OutboxRepository {
protected final ApplicationContext applicationContext; protected final ApplicationContext applicationContext;
private final Random random = new Random();
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxRepositoryImpl.class));
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final OutboxProperties outboxProperties; private final OutboxProperties outboxProperties;
public OutboxRepositoryImpl( public OutboxRepositoryImpl(
@ -87,14 +88,17 @@ public class OutboxRepositoryImpl implements OutboxRepository {
} catch (OptimisticLockException ex) { } catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working // we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working
logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage()); logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage());
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
candidate = null; candidate = null;
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
candidate = null; candidate = null;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex); logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
@ -136,10 +140,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
success = false; success = false;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -181,10 +187,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
success = false; success = false;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -236,10 +244,12 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
success = false; success = false;
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -265,7 +275,7 @@ public class OutboxRepositoryImpl implements OutboxRepository {
List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect();
if (queueOutboxMessages == null) { if (queueOutboxMessages == null) {
logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); logger.warn("Could not lookup messages {} to process. Continuing...", confirmedMessages.stream().map(UUID::toString).collect(Collectors.joining(",")));
} else { } else {
for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) {
@ -280,9 +290,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -307,7 +319,7 @@ public class OutboxRepositoryImpl implements OutboxRepository {
List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect();
if (queueOutboxMessages == null) { if (queueOutboxMessages == null) {
logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); logger.warn("Could not lookup messages {} to process. Continuing...", nackedMessages.stream().map(UUID::toString).collect(Collectors.joining(",")));
} else { } else {
for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) {
@ -322,9 +334,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
transaction.rollback();
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -335,7 +349,6 @@ public class OutboxRepositoryImpl implements OutboxRepository {
public QueueOutbox create(IntegrationEvent item) { public QueueOutbox create(IntegrationEvent item) {
EntityTransaction transaction = null; EntityTransaction transaction = null;
EntityManager entityManager = null; EntityManager entityManager = null;
boolean success = false;
QueueOutboxEntity queueMessage = null; QueueOutboxEntity queueMessage = null;
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
@ -353,10 +366,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
transaction.commit(); transaction.commit();
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) transaction.rollback(); if (transaction != null)
success = false; transaction.rollback();
} finally { } finally {
if (entityManager != null) entityManager.close(); if (entityManager != null)
entityManager.close();
} }
} catch (Exception ex) { } catch (Exception ex) {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
@ -374,13 +388,14 @@ public class OutboxRepositoryImpl implements OutboxRepository {
} }
// UUID correlationId = UUID.randomUUID(); // UUID correlationId = UUID.randomUUID();
// if (event.getEvent() != null) event.getEvent().setTrackingContextTag(correlationId.toString()); // if (event.getEvent() != null)
// event.getEvent().setTrackingContextTag(correlationId.toString());
// event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent())); // event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent()));
// //this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}"); // //this._logTrackingService.Trace(correlationId.ToString(), $"Correlating current tracking context with new correlationId {correlationId}");
// //
// QueueOutboxEntity queueMessage = new QueueOutboxEntity(); // QueueOutboxEntity queueMessage = new QueueOutboxEntity();
// queueMessage.setId(UUID.randomUUID()); // queueMessage.setId(UUID.randomUUID());
// queueMessage.setTenantId(null); // queueMessage.setTenantId(event.getTenantId());
// queueMessage.setExchange(this.outboxProperties.getExchange()); // queueMessage.setExchange(this.outboxProperties.getExchange());
// queueMessage.setRoute(routingKey); // queueMessage.setRoute(routingKey);
// queueMessage.setMessageId(event.getMessageId()); // queueMessage.setMessageId(event.getMessageId());

View File

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

View File

@ -0,0 +1,34 @@
package gr.cite.annotation.integrationevent.outbox;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class OutboxServiceImpl implements OutboxService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxServiceImpl.class));
private final ApplicationEventPublisher eventPublisher;
public OutboxServiceImpl(
ApplicationEventPublisher eventPublisher
) {
this.eventPublisher = eventPublisher;
}
@Override
public void publish(OutboxIntegrationEvent event) {
try {
eventPublisher.publishEvent(event);
} catch (Exception ex) {
logger.error(new MapLogEntry(String.format("Could not save message ", event.getMessage())).And("message", event.getMessage()).And("ex", ex));
//Still want to skip it from processing
}
}
}

View File

@ -75,6 +75,10 @@ public class Annotation {
private List<String> authorizationFlags; private List<String> authorizationFlags;
public static final String _authorizationFlags = "authorizationFlags"; public static final String _authorizationFlags = "authorizationFlags";
private Boolean belongsToCurrentTenant;
public static final String _belongsToCurrentTenant = "belongsToCurrentTenant";
public UUID getId() { public UUID getId() {
return id; return id;
} }
@ -202,4 +206,5 @@ public class Annotation {
public void setAuthorizationFlags(List<String> authorizationFlags) { public void setAuthorizationFlags(List<String> authorizationFlags) {
this.authorizationFlags = authorizationFlags; this.authorizationFlags = authorizationFlags;
} }
} }

View File

@ -0,0 +1,98 @@
package gr.cite.annotation.model;
import gr.cite.annotation.common.enums.IsActive;
import java.time.Instant;
import java.util.UUID;
public class TenantUser {
private UUID id;
public final static String _id = "id";
private User user;
public final static String _user = "user";
private Tenant tenant;
public final static String _tenant = "tenant";
private IsActive isActive;
public final static String _isActive = "isActive";
private Instant createdAt;
public final static String _createdAt = "createdAt";
private Instant updatedAt;
public final static String _updatedAt = "updatedAt";
private String hash;
public final static String _hash = "hash";
private Boolean belongsToCurrentTenant;
public static final String _belongsToCurrentTenant = "belongsToCurrentTenant";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
public IsActive getIsActive() {
return isActive;
}
public void setIsActive(IsActive isActive) {
this.isActive = isActive;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public Boolean getBelongsToCurrentTenant() {
return belongsToCurrentTenant;
}
public void setBelongsToCurrentTenant(Boolean belongsToCurrentTenant) {
this.belongsToCurrentTenant = belongsToCurrentTenant;
}
}

View File

@ -0,0 +1,76 @@
package gr.cite.annotation.model;
import gr.cite.annotation.common.enums.ContactInfoType;
import java.time.Instant;
import java.util.UUID;
public class UserContactInfo {
private UUID id;
public static final String _id = "id";
private String value;
public static final String _value = "value";
private ContactInfoType type;
public static final String _type = "type";
private int ordinal;
public static final String _ordinal = "ordinal";
private User user;
public static final String _user = "user";
private Instant createdAt;
public static final String _createdAt = "createdAt";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public ContactInfoType getType() {
return type;
}
public void setType(ContactInfoType type) {
this.type = type;
}
public int getOrdinal() {
return ordinal;
}
public void setOrdinal(int ordinal) {
this.ordinal = ordinal;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
}

View File

@ -2,10 +2,8 @@ package gr.cite.annotation.model.deleter;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.AnnotationEntity; import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.TenantScopedEntityManager; import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.data.UserCredentialEntity;
import gr.cite.annotation.query.AnnotationQuery; import gr.cite.annotation.query.AnnotationQuery;
import gr.cite.annotation.query.UserCredentialQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
@ -28,13 +26,13 @@ public class AnnotationDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
@Autowired @Autowired
public AnnotationDeleter( public AnnotationDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory QueryFactory queryFactory
) { ) {
this.entityManager = entityManager; this.entityManager = entityManager;

View File

@ -2,7 +2,7 @@ package gr.cite.EntityUser.model.deleter;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.EntityUserEntity; import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.data.TenantScopedEntityManager; import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.query.EntityUserQuery; import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -26,13 +26,13 @@ public class EntityUserDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EntityUserDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EntityUserDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
@Autowired @Autowired
public EntityUserDeleter( public EntityUserDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory QueryFactory queryFactory
) { ) {
this.entityManager = entityManager; this.entityManager = entityManager;

View File

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

View File

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

View File

@ -0,0 +1,74 @@
package gr.cite.annotation.model.deleter;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.data.TenantUserEntity;
import gr.cite.annotation.query.TenantUserQuery;
import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TenantUserDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantUserDeleter.class));
private final TenantEntityManager entityManager;
private final QueryFactory queryFactory;
private final DeleterFactory deleterFactory;
@Autowired
public TenantUserDeleter(
TenantEntityManager entityManager,
QueryFactory queryFactory,
DeleterFactory deleterFactory
) {
this.entityManager = entityManager;
this.queryFactory = queryFactory;
this.deleterFactory = deleterFactory;
}
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {
logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(e -> e.size()).orElse(0)).And("ids", ids));
List<TenantUserEntity> datas = this.queryFactory.query(TenantUserQuery.class).ids(ids).collect();
logger.trace("retrieved {} items", Optional.ofNullable(datas).map(e -> e.size()).orElse(0));
this.deleteAndSave(datas);
}
public void deleteAndSave(List<TenantUserEntity> datas) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(datas).map(e -> e.size()).orElse(0));
this.delete(datas);
logger.trace("saving changes");
this.entityManager.flush();
logger.trace("changes saved");
}
public void delete(List<TenantUserEntity> datas) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(datas).map(x -> x.size()).orElse(0));
if (datas == null || datas.isEmpty()) return;
Instant now = Instant.now();
for (TenantUserEntity item : datas) {
logger.trace("deleting item {}", item.getId());
item.setIsActive(IsActive.Inactive);
item.setUpdatedAt(now);
logger.trace("updating item");
this.entityManager.merge(item);
logger.trace("updated item");
}
}
}

View File

@ -0,0 +1,68 @@
package gr.cite.annotation.model.deleter;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.data.UserContactInfoEntity;
import gr.cite.annotation.query.UserContactInfoQuery;
import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserContactInfoDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserContactInfoDeleter.class));
private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory;
@Autowired
public UserContactInfoDeleter(
TenantEntityManager entityManager,
QueryFactory queryFactory
) {
this.entityManager = entityManager;
this.queryFactory = queryFactory;
}
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {
logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids));
List<UserContactInfoEntity> data = this.queryFactory.query(UserContactInfoQuery.class).ids(ids).collect();
logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.deleteAndSave(data);
}
public void deleteAndSave(List<UserContactInfoEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.delete(data);
logger.trace("saving changes");
this.entityManager.flush();
logger.trace("changes saved");
}
public void delete(List<UserContactInfoEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
if (data == null || data.isEmpty())
return;
for (UserContactInfoEntity item : data) {
logger.trace("deleting item {}", item.getId());
logger.trace("deleting item");
this.entityManager.remove(item);
logger.trace("deleted item");
}
}
}

View File

@ -1,7 +1,7 @@
package gr.cite.annotation.model.deleter; package gr.cite.annotation.model.deleter;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.TenantScopedEntityManager; import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.data.UserCredentialEntity; import gr.cite.annotation.data.UserCredentialEntity;
import gr.cite.annotation.query.UserCredentialQuery; import gr.cite.annotation.query.UserCredentialQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
@ -26,13 +26,13 @@ public class UserCredentialDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserCredentialDeleter.class));
private final TenantScopedEntityManager entityManager; private final TenantEntityManager entityManager;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
@Autowired @Autowired
public UserCredentialDeleter( public UserCredentialDeleter(
TenantScopedEntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory QueryFactory queryFactory
) { ) {
this.entityManager = entityManager; this.entityManager = entityManager;

View File

@ -1,8 +1,9 @@
package gr.cite.annotation.model.deleter; package gr.cite.annotation.model.deleter;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.UserCredentialEntity; import gr.cite.annotation.data.*;
import gr.cite.annotation.data.UserEntity; import gr.cite.annotation.query.TenantUserQuery;
import gr.cite.annotation.query.UserContactInfoQuery;
import gr.cite.annotation.query.UserCredentialQuery; import gr.cite.annotation.query.UserCredentialQuery;
import gr.cite.annotation.query.UserQuery; import gr.cite.annotation.query.UserQuery;
import gr.cite.tools.data.deleter.Deleter; import gr.cite.tools.data.deleter.Deleter;
@ -10,7 +11,6 @@ import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
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 jakarta.persistence.EntityManager;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@ -30,7 +30,7 @@ public class UserDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserDeleter.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserDeleter.class));
private final EntityManager entityManager; private final TenantEntityManager entityManager;
protected final QueryFactory queryFactory; protected final QueryFactory queryFactory;
@ -38,7 +38,7 @@ public class UserDeleter implements Deleter {
@Autowired @Autowired
public UserDeleter( public UserDeleter(
EntityManager entityManager, TenantEntityManager entityManager,
QueryFactory queryFactory, QueryFactory queryFactory,
DeleterFactory deleterFactory DeleterFactory deleterFactory
) { ) {
@ -76,6 +76,18 @@ public class UserDeleter implements Deleter {
deleter.delete(items); deleter.delete(items);
} }
{
logger.debug("checking related - {}", UserContactInfoEntity.class.getSimpleName());
List<UserContactInfoEntity> items = this.queryFactory.query(UserContactInfoQuery.class).userIds(ids).collect();
UserContactInfoDeleter deleter = this.deleterFactory.deleter(UserContactInfoDeleter.class);
deleter.delete(items);
}
{
logger.debug("checking related - {}", TenantUserEntity.class.getSimpleName());
List<TenantUserEntity> items = this.queryFactory.query(TenantUserQuery.class).userIds(ids).collect();
TenantUserDeleter deleter = this.deleterFactory.deleter(TenantUserDeleter.class);
deleter.delete(items);
}
Instant now = Instant.now(); Instant now = Instant.now();
for (UserEntity item : data) { for (UserEntity item : data) {

View File

@ -0,0 +1,209 @@
package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.TenantUserEntity;
import gr.cite.annotation.data.UserEntity;
import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.model.TenantUser;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TenantUserQuery extends QueryBase<TenantUserEntity> {
private Collection<UUID> ids;
private Collection<UUID> userIds;
private Collection<UUID> tenantIds;
private Collection<IsActive> isActives;
private UserQuery userQuery;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final UserScope userScope;
private final AuthorizationService authService;
public TenantUserQuery(
UserScope userScope,
AuthorizationService authService
) {
this.userScope = userScope;
this.authService = authService;
}
public TenantUserQuery ids(UUID value) {
this.ids = List.of(value);
return this;
}
public TenantUserQuery ids(UUID... value) {
this.ids = Arrays.asList(value);
return this;
}
public TenantUserQuery ids(Collection<UUID> values) {
this.ids = values;
return this;
}
public TenantUserQuery userIds(UUID value) {
this.userIds = List.of(value);
return this;
}
public TenantUserQuery userIds(UUID... value) {
this.userIds = Arrays.asList(value);
return this;
}
public TenantUserQuery userIds(Collection<UUID> values) {
this.userIds = values;
return this;
}
public TenantUserQuery tenantIds(UUID value) {
this.tenantIds = List.of(value);
return this;
}
public TenantUserQuery tenantIds(UUID... value) {
this.tenantIds = Arrays.asList(value);
return this;
}
public TenantUserQuery tenantIds(Collection<UUID> values) {
this.tenantIds = values;
return this;
}
public TenantUserQuery isActive(IsActive value) {
this.isActives = List.of(value);
return this;
}
public TenantUserQuery isActive(IsActive... value) {
this.isActives = Arrays.asList(value);
return this;
}
public TenantUserQuery isActive(Collection<IsActive> values) {
this.isActives = values;
return this;
}
public TenantUserQuery userSubQuery(UserQuery subQuery) {
this.userQuery = subQuery;
return this;
}
public TenantUserQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
protected Class<TenantUserEntity> entityClass() {
return TenantUserEntity.class;
}
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.userIds) || this.isEmpty(this.tenantIds) || this.isEmpty(this.isActives) || this.isFalseQuery(this.userQuery);
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseTenant)) return null;
UUID ownerId = null;
if (this.authorize.contains(AuthorizationFlags.Owner)) ownerId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (ownerId != null) {
predicates.add(queryContext.CriteriaBuilder.equal(queryContext.Root.get(TenantUserEntity._userId), ownerId));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>();
if (this.ids != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._id));
for (UUID item : this.ids) inClause.value(item);
predicates.add(inClause);
}
if (this.userIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._userId));
for (UUID item : this.userIds) inClause.value(item);
predicates.add(inClause);
}
if (this.tenantIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._tenantId));
for (UUID item : this.tenantIds) inClause.value(item);
predicates.add(inClause);
}
if (this.isActives != null) {
CriteriaBuilder.In<IsActive> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._isActive));
for (IsActive item : this.isActives) inClause.value(item);
predicates.add(inClause);
}
if (this.userQuery != null) {
QueryContext<UserEntity, UUID> subQuery = this.applySubQuery(this.userQuery, queryContext, UUID.class, root -> root.get(TenantUserEntity._userId));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(TenantUserEntity._userId)).value(subQuery.Query));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return null;
}
}
@Override
protected TenantUserEntity convert(Tuple tuple, Set<String> columns) {
TenantUserEntity item = new TenantUserEntity();
item.setId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._id, UUID.class));
item.setTenantId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._tenantId, UUID.class));
item.setUserId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._userId, UUID.class));
item.setTenantId(QueryBase.convertSafe(tuple, columns, TenantUserEntity._tenantId, UUID.class));
item.setCreatedAt(QueryBase.convertSafe(tuple, columns, TenantUserEntity._createdAt, Instant.class));
item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, TenantUserEntity._updatedAt, Instant.class));
item.setIsActive(QueryBase.convertSafe(tuple, columns, TenantUserEntity._isActive, IsActive.class));
return item;
}
@Override
protected String fieldNameOf(FieldResolver item) {
if (item.match(TenantUser._id)) return TenantUserEntity._id;
else if (item.match(TenantUser._tenant, Tenant._id)) return TenantUserEntity._tenantId;
else if (item.prefix(TenantUser._tenant)) return TenantUserEntity._tenantId;
else if (item.match(TenantUser._isActive)) return TenantUserEntity._isActive;
else if (item.match(TenantUser._createdAt)) return TenantUserEntity._createdAt;
else if (item.match(TenantUser._updatedAt)) return TenantUserEntity._updatedAt;
else if (item.match(TenantUser._hash)) return TenantUserEntity._updatedAt;
else if (item.match(TenantUser._user, UserEntity._id)) return TenantUserEntity._userId;
else if (item.prefix(TenantUser._user)) return TenantUserEntity._userId;
else if (item.match(TenantUser._belongsToCurrentTenant)) return TenantUserEntity._tenantId;
else return null;
}
}

View File

@ -0,0 +1,236 @@
package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.enums.ContactInfoType;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.UserContactInfoEntity;
import gr.cite.annotation.model.UserContactInfo;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserContactInfoQuery extends QueryBase<UserContactInfoEntity> {
private Collection<UUID> ids;
private Collection<UUID> excludedIds;
private Collection<UUID> excludedUserIds;
private Collection<UUID> userIds;
private Collection<String> values;
private Collection<ContactInfoType> types;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final UserScope userScope;
private final AuthorizationService authService;
public UserContactInfoQuery(UserScope userScope, AuthorizationService authService) {
this.userScope = userScope;
this.authService = authService;
}
public UserContactInfoQuery ids(UUID value) {
this.ids = List.of(value);
return this;
}
public UserContactInfoQuery ids(UUID... value) {
this.ids = Arrays.asList(value);
return this;
}
public UserContactInfoQuery ids(Collection<UUID> values) {
this.ids = values;
return this;
}
public UserContactInfoQuery excludedIds(Collection<UUID> values) {
this.excludedIds = values;
return this;
}
public UserContactInfoQuery excludedIds(UUID value) {
this.excludedIds = List.of(value);
return this;
}
public UserContactInfoQuery excludedIds(UUID... value) {
this.excludedIds = Arrays.asList(value);
return this;
}
public UserContactInfoQuery excludedUserIds(Collection<UUID> values) {
this.excludedUserIds = values;
return this;
}
public UserContactInfoQuery excludedUserIds(UUID value) {
this.excludedUserIds = List.of(value);
return this;
}
public UserContactInfoQuery excludedUserIds(UUID... value) {
this.excludedUserIds = Arrays.asList(value);
return this;
}
public UserContactInfoQuery userIds(UUID value) {
this.userIds = List.of(value);
return this;
}
public UserContactInfoQuery userIds(UUID... value) {
this.userIds = Arrays.asList(value);
return this;
}
public UserContactInfoQuery userIds(Collection<UUID> values) {
this.userIds = values;
return this;
}
public UserContactInfoQuery values(String value) {
this.values = List.of(value);
return this;
}
public UserContactInfoQuery values(String... value) {
this.values = Arrays.asList(value);
return this;
}
public UserContactInfoQuery values(Collection<String> values) {
this.values = values;
return this;
}
public UserContactInfoQuery types(ContactInfoType value) {
this.types = List.of(value);
return this;
}
public UserContactInfoQuery types(ContactInfoType... value) {
this.types = Arrays.asList(value);
return this;
}
public UserContactInfoQuery types(Collection<ContactInfoType> values) {
this.types = values;
return this;
}
public UserContactInfoQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
protected Boolean isFalseQuery() {
return
this.isEmpty(this.ids) ||
this.isEmpty(this.userIds) ||
this.isEmpty(this.excludedIds) ||
this.isEmpty(this.values) ||
this.isEmpty(this.excludedIds);
}
@Override
protected Class<UserContactInfoEntity> entityClass() {
return UserContactInfoEntity.class;
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUser)) return null;
UUID userId;
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
else userId = null;
List<Predicate> predicates = new ArrayList<>();
if (userId != null) {
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._userId)).value(userId));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>();
if (this.ids != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._id));
for (UUID item : this.ids)
inClause.value(item);
predicates.add(inClause);
}
if (this.userIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._userId));
for (UUID item : this.userIds)
inClause.value(item);
predicates.add(inClause);
}
if (this.excludedIds != null) {
CriteriaBuilder.In<UUID> notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._id));
for (UUID item : this.excludedIds)
notInClause.value(item);
predicates.add(notInClause.not());
}
if (this.excludedUserIds != null) {
CriteriaBuilder.In<UUID> notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._userId));
for (UUID item : this.excludedUserIds)
notInClause.value(item);
predicates.add(notInClause.not());
}
if (this.values != null) {
CriteriaBuilder.In<String> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserContactInfoEntity._value));
for (String item : this.values)
inClause.value(item);
predicates.add(inClause);
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return null;
}
}
@Override
protected String fieldNameOf(FieldResolver item) {
if (item.match(UserContactInfo._id)) return UserContactInfoEntity._id;
else if (item.match(UserContactInfo._value)) return UserContactInfoEntity._value;
else if (item.match(UserContactInfo._ordinal)) return UserContactInfoEntity._ordinal;
else if (item.prefix(UserContactInfo._user)) return UserContactInfoEntity._userId;
else if (item.match(UserContactInfo._user)) return UserContactInfoEntity._userId;
else if (item.match(UserContactInfo._type)) return UserContactInfoEntity._type;
else if (item.match(UserContactInfo._createdAt) ) return UserContactInfoEntity._createdAt;
else return null;
}
@Override
protected UserContactInfoEntity convert(Tuple tuple, Set<String> columns) {
UserContactInfoEntity item = new UserContactInfoEntity();
item.setId(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._id, UUID.class));
item.setValue(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._value, String.class));
item.setType(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._type, ContactInfoType.class));
item.setOrdinal(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._ordinal, Integer.class));
item.setUserId(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._userId, UUID.class));
item.setCreatedAt(QueryBase.convertSafe(tuple, columns, UserContactInfoEntity._createdAt, Instant.class));
return item;
}
}

View File

@ -7,6 +7,7 @@ import gr.cite.annotation.authorization.authorizationcontentresolver.Authorizati
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope; import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity; import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.model.Annotation; import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.builder.AnnotationBuilder; import gr.cite.annotation.model.builder.AnnotationBuilder;
import gr.cite.annotation.model.deleter.AnnotationDeleter; import gr.cite.annotation.model.deleter.AnnotationDeleter;
@ -22,7 +23,6 @@ import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
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 jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -44,7 +44,7 @@ public class AnnotationServiceImpl implements AnnotationService {
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
private final EntityManager entityManager; private final TenantEntityManager entityManager;
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
@ -57,7 +57,7 @@ public class AnnotationServiceImpl implements AnnotationService {
public AnnotationServiceImpl( public AnnotationServiceImpl(
AuthorizationService authorizationService, AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
EntityManager entityManager, TenantEntityManager entityManager,
BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) { BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;

View File

@ -1,6 +1,7 @@
package gr.cite.annotation.service.tenant; package gr.cite.annotation.service.tenant;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission; import gr.cite.annotation.authorization.Permission;
@ -21,7 +22,6 @@ import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
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 jakarta.persistence.EntityManager;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -42,14 +42,14 @@ public class TenantServiceImpl implements TenantService {
private final ConventionService conventionService; private final ConventionService conventionService;
private final EntityManager entityManager; private final TenantEntityManager entityManager;
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
public TenantServiceImpl(AuthorizationService authorizationService, public TenantServiceImpl(AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
ConventionService conventionService, ConventionService conventionService,
EntityManager entityManager, TenantEntityManager entityManager,
BuilderFactory builderFactory) { BuilderFactory builderFactory) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;

View File

@ -10,7 +10,7 @@ import gr.cite.annotation.common.types.tenantconfiguration.DefaultUserLocaleConf
import gr.cite.annotation.common.types.tenantconfiguration.EmailClientConfigurationDataContainer; import gr.cite.annotation.common.types.tenantconfiguration.EmailClientConfigurationDataContainer;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.TenantConfigurationEntity; import gr.cite.annotation.data.TenantConfigurationEntity;
import gr.cite.annotation.data.TenantScopedEntityManager; import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.model.TenantConfiguration; import gr.cite.annotation.model.TenantConfiguration;
import gr.cite.annotation.model.builder.TenantConfigurationBuilder; import gr.cite.annotation.model.builder.TenantConfigurationBuilder;
@ -51,11 +51,11 @@ public class TenantConfigurationServiceImpl implements TenantConfigurationServic
private final MessageSource messageSource; private final MessageSource messageSource;
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final TenantScopedEntityManager dbContext; private final TenantEntityManager dbContext;
private final DeleterFactory deleterFactory; private final DeleterFactory deleterFactory;
@Autowired @Autowired
public TenantConfigurationServiceImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, AuthorizationService authorizationService, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, BuilderFactory builderFactory, TenantScopedEntityManager dbContext, DeleterFactory deleterFactory) { public TenantConfigurationServiceImpl(ApplicationContext applicationContext, JsonHandlingService jsonHandlingService, AuthorizationService authorizationService, ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, BuilderFactory builderFactory, TenantEntityManager dbContext, DeleterFactory deleterFactory) {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;

View File

@ -1,19 +1,23 @@
package gr.cite.annotation.service.user; package gr.cite.annotation.service.user;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission; import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.UserContactInfoEntity; import gr.cite.annotation.data.*;
import gr.cite.annotation.data.UserCredentialEntity;
import gr.cite.annotation.data.UserEntity;
import gr.cite.annotation.integrationevent.inbox.usertouch.UserTouchedIntegrationEvent; import gr.cite.annotation.integrationevent.inbox.usertouch.UserTouchedIntegrationEvent;
import gr.cite.annotation.model.User; import gr.cite.annotation.model.User;
import gr.cite.annotation.model.builder.UserBuilder; import gr.cite.annotation.model.builder.UserBuilder;
import gr.cite.annotation.model.deleter.TenantUserDeleter;
import gr.cite.annotation.model.deleter.UserContactInfoDeleter;
import gr.cite.annotation.model.deleter.UserCredentialDeleter;
import gr.cite.annotation.model.deleter.UserDeleter; import gr.cite.annotation.model.deleter.UserDeleter;
import gr.cite.annotation.query.TenantUserQuery;
import gr.cite.annotation.query.UserContactInfoQuery;
import gr.cite.annotation.query.UserCredentialQuery;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -25,16 +29,17 @@ import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
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 jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
@Service @Service
public class UserServiceImpl implements UserService { public class UserServiceImpl implements UserService {
@ -47,28 +52,28 @@ public class UserServiceImpl implements UserService {
private final ConventionService conventionService; private final ConventionService conventionService;
private final EntityManager entityManager; private final TenantEntityManager entityManager;
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
public UserServiceImpl(AuthorizationService authorizationService, public UserServiceImpl(AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
ConventionService conventionService, ConventionService conventionService,
EntityManager entityManager, TenantEntityManager entityManager,
BuilderFactory builderFactory, BuilderFactory builderFactory, QueryFactory queryFactory,
QueryFactory queryFactory,
JsonHandlingService jsonHandlingService) { JsonHandlingService jsonHandlingService) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
this.conventionService = conventionService; this.conventionService = conventionService;
this.entityManager = entityManager; this.entityManager = entityManager;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
} }
@Override @Override
@ -94,11 +99,6 @@ public class UserServiceImpl implements UserService {
this.entityManager.persist(data); this.entityManager.persist(data);
for (UserTouchedIntegrationEvent.UserContactInfo eventC : model.getUserContactInfo()) {
this.entityManager.persist(this.buildContactInfoEntityFromEventData(eventC, model.getId()));
}
this.entityManager.persist(this.buildUserCredentialEntityFromEventData(model));
} else { } else {
data.setName(model.getName()); data.setName(model.getName());
data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile())); data.setAdditionalInfo(this.jsonHandlingService.toJson(model.getProfile()));
@ -114,6 +114,12 @@ public class UserServiceImpl implements UserService {
this.entityManager.flush(); this.entityManager.flush();
this.persistContactInfo(model.getUserContactInfo(), data.getId());
this.persistUserCredential(model.getCredentials(), data.getId());
this.persistTenantUser(model.getTenantUsers(), data.getId());
this.entityManager.flush();
return this.builderFactory.builder(UserBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, User._id), data); return this.builderFactory.builder(UserBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, User._id), data);
} }
@ -126,30 +132,92 @@ public class UserServiceImpl implements UserService {
this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id)); this.deleterFactory.deleter(UserDeleter.class).deleteAndSaveByIds(List.of(id));
} }
private UserContactInfoEntity buildContactInfoEntityFromEventData(UserTouchedIntegrationEvent.UserContactInfo eventC, UUID userId) { private void persistContactInfo(List<UserTouchedIntegrationEvent.UserContactInfo> models, UUID userId) throws InvalidApplicationException {
UserContactInfoEntity contactInfo = new UserContactInfoEntity(); List<UserContactInfoEntity> items = this.queryFactory.query(UserContactInfoQuery.class)
contactInfo.setId(UUID.randomUUID()); .userIds(userId)
contactInfo.setUserId(userId); .collect();
contactInfo.setType(eventC.getType()); List<UUID> updatedCreatedIds = new ArrayList<>();
contactInfo.setValue(eventC.getValue()); if (models != null) {
contactInfo.setOrdinal(eventC.getOrdinal()); for (UserTouchedIntegrationEvent.UserContactInfo model : models) {
contactInfo.setCreatedAt(Instant.now()); UserContactInfoEntity data = items.stream().filter(x -> x.getType().equals(model.getType()) && x.getValue().equals(model.getValue())).findFirst().orElse(null);
contactInfo.setUpdatedAt(Instant.now()); if (data == null) {
contactInfo.setIsActive(IsActive.Active); data = new UserContactInfoEntity();
data.setId(UUID.randomUUID());
data.setUserId(userId);
data.setType(model.getType());
data.setValue(model.getValue());
data.setOrdinal(model.getOrdinal());
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
entityManager.persist(data);
} else {
data.setOrdinal(model.getOrdinal());
entityManager.merge(data);
}
updatedCreatedIds.add(data.getId());
}
}
List<UserContactInfoEntity> toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList());
deleterFactory.deleter(UserContactInfoDeleter.class).delete(toDelete);
return contactInfo; entityManager.flush();
} }
private UserCredentialEntity buildUserCredentialEntityFromEventData(UserTouchedIntegrationEvent event) { private void persistUserCredential(List<UserTouchedIntegrationEvent.UserCredential> models, UUID userId) throws InvalidApplicationException {
UserCredentialEntity credentialEntity = new UserCredentialEntity(); List<UserCredentialEntity> items = this.queryFactory.query(UserCredentialQuery.class)
credentialEntity.setId(UUID.randomUUID()); .userIds(userId)
credentialEntity.setUserId(event.getId()); .isActive(IsActive.Active)
credentialEntity.setExternalId(event.getSubjectId()); .collect();
credentialEntity.setCreatedAt(Instant.now()); List<UUID> updatedCreatedIds = new ArrayList<>();
credentialEntity.setUpdatedAt(Instant.now()); if (models != null) {
credentialEntity.setIsActive(IsActive.Active); for (UserTouchedIntegrationEvent.UserCredential model : models) {
UserCredentialEntity data = items.stream().filter(x -> x.getExternalId().equals(model.getSubjectId())).findFirst().orElse(null);
if (data == null) {
data = new UserCredentialEntity();
data.setId(UUID.randomUUID());
data.setUserId(userId);
data.setExternalId(model.getSubjectId());
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
entityManager.persist(data);
}
updatedCreatedIds.add(data.getId());
}
}
List<UserCredentialEntity> toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList());
deleterFactory.deleter(UserCredentialDeleter.class).delete(toDelete);
return credentialEntity; entityManager.flush();
}
private void persistTenantUser(List<UserTouchedIntegrationEvent.TenantUser> models, UUID userId) throws InvalidApplicationException {
List<TenantUserEntity> items = this.queryFactory.query(TenantUserQuery.class)
.userIds(userId)
.isActive(IsActive.Active)
.collect();
List<UUID> updatedCreatedIds = new ArrayList<>();
if (models != null) {
for (UserTouchedIntegrationEvent.TenantUser model : models) {
TenantUserEntity data = items.stream().filter(x -> x.getTenantId().equals(model.getTenant())).findFirst().orElse(null);
if (data == null) {
data = new TenantUserEntity();
data.setId(UUID.randomUUID());
data.setUserId(userId);
data.setTenantId(model.getTenant());
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
entityManager.persist(data);
}
updatedCreatedIds.add(data.getId());
}
}
List<TenantUserEntity> toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList());
deleterFactory.deleter(TenantUserDeleter.class).delete(toDelete);
entityManager.flush();
} }
} }

View File

@ -108,5 +108,13 @@ public class TenantEntityManager {
.unwrap(Session.class) .unwrap(Session.class)
.disableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER); .disableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER);
} }
public EntityManager getEntityManager() {
return entityManager;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
} }

View File

@ -21,11 +21,9 @@ public class AppRabbitConfigurer extends RabbitConfigurer {
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
private InboxProperties inboxProperties;
public AppRabbitConfigurer(ApplicationContext applicationContext, InboxProperties inboxProperties) { public AppRabbitConfigurer(ApplicationContext applicationContext) {
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.inboxProperties = inboxProperties;
} }
// @Bean // @Bean

View File

@ -11,6 +11,7 @@ import gr.cite.queueinbox.entity.QueueInboxStatus;
import gr.cite.queueinbox.repository.CandidateInfo; import gr.cite.queueinbox.repository.CandidateInfo;
import gr.cite.queueinbox.repository.InboxRepository; import gr.cite.queueinbox.repository.InboxRepository;
import gr.cite.queueinbox.task.MessageOptions; import gr.cite.queueinbox.task.MessageOptions;
import gr.cite.rabbitmq.IntegrationEventMessageConstants;
import gr.cite.rabbitmq.consumer.InboxCreatorParams; import gr.cite.rabbitmq.consumer.InboxCreatorParams;
import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -233,10 +234,10 @@ public class InboxRepositoryImpl implements InboxRepository {
} }
private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) { private QueueInboxEntity createQueueInboxEntity(InboxCreatorParams inboxCreatorParams) {
QueueInboxEntity queueMessage = new QueueInboxEntity(); QueueInboxEntity queueMessage = new QueueInboxEntity();
queueMessage.setId(UUID.randomUUID()); queueMessage.setId(UUID.randomUUID());
queueMessage.setTenantId(null); Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null;
if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId);
queueMessage.setExchange(this.inboxProperties.getExchange()); queueMessage.setExchange(this.inboxProperties.getExchange());
queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey());
queueMessage.setQueue(inboxCreatorParams.getQueueName()); queueMessage.setQueue(inboxCreatorParams.getQueueName());

View File

@ -275,7 +275,7 @@ public class OutboxRepositoryImpl implements OutboxRepository {
List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect(); List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(confirmedMessages).collect();
if (queueOutboxMessages == null) { if (queueOutboxMessages == null) {
logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", confirmedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); logger.warn("Could not lookup messages {} to process. Continuing...", confirmedMessages.stream().map(UUID::toString).collect(Collectors.joining(",")));
} else { } else {
for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) {
@ -319,7 +319,7 @@ public class OutboxRepositoryImpl implements OutboxRepository {
List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect(); List<QueueOutboxEntity> queueOutboxMessages = queryFactory.query(QueueOutboxQuery.class).ids(nackedMessages).collect();
if (queueOutboxMessages == null) { if (queueOutboxMessages == null) {
logger.warn("Could not lookup messages {} to process. Continuing...", String.join(",", nackedMessages.stream().map(x -> x.toString()).collect(Collectors.toList()))); logger.warn("Could not lookup messages {} to process. Continuing...", nackedMessages.stream().map(UUID::toString).collect(Collectors.joining(",")));
} else { } else {
for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) { for (QueueOutboxEntity queueOutboxMessage : queueOutboxMessages) {
@ -349,7 +349,6 @@ public class OutboxRepositoryImpl implements OutboxRepository {
public QueueOutbox create(IntegrationEvent item) { public QueueOutbox create(IntegrationEvent item) {
EntityTransaction transaction = null; EntityTransaction transaction = null;
EntityManager entityManager = null; EntityManager entityManager = null;
boolean success = false;
QueueOutboxEntity queueMessage = null; QueueOutboxEntity queueMessage = null;
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
@ -369,7 +368,6 @@ public class OutboxRepositoryImpl implements OutboxRepository {
logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex); logger.error("Problem executing purge. rolling back any db changes and marking error. Continuing...", ex);
if (transaction != null) if (transaction != null)
transaction.rollback(); transaction.rollback();
success = false;
} finally { } finally {
if (entityManager != null) if (entityManager != null)
entityManager.close(); entityManager.close();
@ -453,7 +451,7 @@ public class OutboxRepositoryImpl implements OutboxRepository {
QueueOutboxEntity queueMessage = new QueueOutboxEntity(); QueueOutboxEntity queueMessage = new QueueOutboxEntity();
queueMessage.setId(UUID.randomUUID()); queueMessage.setId(UUID.randomUUID());
queueMessage.setTenantId(null); queueMessage.setTenantId(event.getTenantId());
queueMessage.setExchange(this.outboxProperties.getExchange()); queueMessage.setExchange(this.outboxProperties.getExchange());
queueMessage.setRoute(routingKey); queueMessage.setRoute(routingKey);
queueMessage.setMessageId(event.getMessageId()); queueMessage.setMessageId(event.getMessageId());

View File

@ -14,17 +14,11 @@ import org.springframework.web.context.annotation.RequestScope;
public class OutboxServiceImpl implements OutboxService { public class OutboxServiceImpl implements OutboxService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxServiceImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OutboxServiceImpl.class));
private final OutboxProperties config;
private final JsonHandlingService jsonHandlingService;
private final ApplicationEventPublisher eventPublisher; private final ApplicationEventPublisher eventPublisher;
public OutboxServiceImpl( public OutboxServiceImpl(
OutboxProperties config,
JsonHandlingService jsonHandlingService,
ApplicationEventPublisher eventPublisher ApplicationEventPublisher eventPublisher
) { ) {
this.config = config;
this.jsonHandlingService = jsonHandlingService;
this.eventPublisher = eventPublisher; this.eventPublisher = eventPublisher;
} }

View File

@ -1,9 +1,10 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval; package eu.eudat.integrationevent.outbox.annotationentityremoval;
import javax.management.InvalidApplicationException;
import java.util.UUID; import java.util.UUID;
public interface AnnotationEntityRemovalIntegrationEventHandler { public interface AnnotationEntityRemovalIntegrationEventHandler {
void handleDescription(UUID descriptionId); void handleDescription(UUID descriptionId) throws InvalidApplicationException;
void handleDmp(UUID dmpId); void handleDmp(UUID dmpId) throws InvalidApplicationException;
} }

View File

@ -1,6 +1,7 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval; package eu.eudat.integrationevent.outbox.annotationentityremoval;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.data.DescriptionEntity; import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpUserEntity; import eu.eudat.data.DmpUserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
@ -17,6 +18,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -30,22 +32,25 @@ public class AnnotationEntityRemovalIntegrationEventHandlerImpl implements Annot
private final OutboxService outboxService; private final OutboxService outboxService;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final TenantScope tenantScope;
public AnnotationEntityRemovalIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) { public AnnotationEntityRemovalIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory, TenantScope tenantScope) {
this.outboxService = outboxService; this.outboxService = outboxService;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.tenantScope = tenantScope;
} }
private void handle(AnnotationEntitiesRemovalIntegrationEvent event) { private void handle(AnnotationEntitiesRemovalIntegrationEvent event) throws InvalidApplicationException {
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE); message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE);
message.setEvent(event); message.setEvent(event);
if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant());
this.outboxService.publish(message); this.outboxService.publish(message);
} }
@Override @Override
public void handleDescription(UUID descriptionId) { public void handleDescription(UUID descriptionId) throws InvalidApplicationException {
AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent(); AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent();
event.setEntityIds(List.of(descriptionId)); event.setEntityIds(List.of(descriptionId));
@ -53,7 +58,7 @@ public class AnnotationEntityRemovalIntegrationEventHandlerImpl implements Annot
} }
@Override @Override
public void handleDmp(UUID dmpId) { public void handleDmp(UUID dmpId) throws InvalidApplicationException {
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id)); List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id));
AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent(); AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent();

View File

@ -36,29 +36,31 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
} }
private void handle(AnnotationEntitiesTouchedIntegrationEvent event) { private void handle(AnnotationEntitiesTouchedIntegrationEvent event, UUID tenantId) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH); message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH);
message.setEvent(event); message.setEvent(event);
message.setTenantId(tenantId);
this.outboxService.publish(message); this.outboxService.publish(message);
} }
@Override @Override
public void handleDescription(UUID descriptionId) { public void handleDescription(UUID descriptionId) {
DescriptionEntity entity = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).firstAs(new BaseFieldSet().ensure(Description._dmp)); DescriptionEntity entity = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).firstAs(new BaseFieldSet().ensure(Description._dmp).ensure(DescriptionEntity._tenantId));
if (entity == null) return; if (entity == null) return;
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(entity.getDmpId()).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user)); List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(entity.getDmpId()).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user));
AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent(); AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent();
event.setEvents(List.of(this.buildEventItem(descriptionId, dmpUsers))); event.setEvents(List.of(this.buildEventItem(descriptionId, dmpUsers)));
this.handle(event); this.handle(event, entity.getTenantId());
} }
@Override @Override
public void handleDmp(UUID dmpId) { public void handleDmp(UUID dmpId) {
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id)); List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id));
if (descriptionEntities == null || descriptionEntities.isEmpty()) return;
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user)); List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user));
AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent(); AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent();
@ -67,7 +69,7 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
for (DescriptionEntity description : descriptionEntities) event.getEvents().add(this.buildEventItem(description.getId(), dmpUsers)); for (DescriptionEntity description : descriptionEntities) event.getEvents().add(this.buildEventItem(description.getId(), dmpUsers));
this.handle(event); this.handle(event, descriptionEntities.getFirst().getTenantId());
} }
private AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent buildEventItem(UUID entityId, List<DmpUserEntity> dmpUsers){ private AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent buildEventItem(UUID entityId, List<DmpUserEntity> dmpUsers){

View File

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

View File

@ -1,5 +1,6 @@
package eu.eudat.integrationevent.outbox.notification; package eu.eudat.integrationevent.outbox.notification;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; import eu.eudat.integrationevent.outbox.OutboxService;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
@ -8,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope; import org.springframework.web.context.annotation.RequestScope;
import javax.management.InvalidApplicationException;
import java.util.UUID; import java.util.UUID;
@Component @Component
@ -17,19 +19,22 @@ public class NotifyIntegrationEventHandlerImpl implements NotifyIntegrationEvent
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotifyIntegrationEventHandlerImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(NotifyIntegrationEventHandlerImpl.class));
private final OutboxService outboxService; private final OutboxService outboxService;
private final TenantScope tenantScope;
@Autowired @Autowired
public NotifyIntegrationEventHandlerImpl( public NotifyIntegrationEventHandlerImpl(
OutboxService outboxService) { OutboxService outboxService, TenantScope tenantScope) {
this.outboxService = outboxService; this.outboxService = outboxService;
this.tenantScope = tenantScope;
} }
@Override @Override
public void handle(NotifyIntegrationEvent event) { public void handle(NotifyIntegrationEvent event) throws InvalidApplicationException {
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.NOTIFY); message.setType(OutboxIntegrationEvent.NOTIFY);
message.setEvent(event); message.setEvent(event);
if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant());
this.outboxService.publish(message); this.outboxService.publish(message);
} }
} }

View File

@ -40,6 +40,7 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.TENANT_REMOVE); message.setType(OutboxIntegrationEvent.TENANT_REMOVE);
message.setEvent(event); message.setEvent(event);
message.setTenantId(event.getId());
this.outboxService.publish(message); this.outboxService.publish(message);
} }
@ -55,6 +56,7 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
TenantRemovalIntegrationEvent event = new TenantRemovalIntegrationEvent(); TenantRemovalIntegrationEvent event = new TenantRemovalIntegrationEvent();
event.setId(tenantId); event.setId(tenantId);
message.setEvent(event); message.setEvent(event);
message.setTenantId(event.getId());
this.outboxService.publish(message); this.outboxService.publish(message);
} }

View File

@ -1,5 +1,7 @@
package eu.eudat.integrationevent.outbox.tenanttouched; package eu.eudat.integrationevent.outbox.tenanttouched;
import javax.management.InvalidApplicationException;
public interface TenantTouchedIntegrationEventHandler { public interface TenantTouchedIntegrationEventHandler {
void handle(TenantTouchedIntegrationEvent event); void handle(TenantTouchedIntegrationEvent event);

View File

@ -1,10 +1,26 @@
package eu.eudat.integrationevent.outbox.tenanttouched; package eu.eudat.integrationevent.outbox.tenanttouched;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import java.util.UUID;
public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler { public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler {
private final OutboxService outboxService;
@Override public TenantTouchedIntegrationEventHandlerImpl(OutboxService outboxService) {
this.outboxService = outboxService;
}
@Override
public void handle(TenantTouchedIntegrationEvent event) { public void handle(TenantTouchedIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.TENANT_TOUCH);
message.setTenantId(event.getId());
message.setEvent(event);
this.outboxService.publish(message);
} }
} }

View File

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

View File

@ -1,9 +1,10 @@
package eu.eudat.integrationevent.outbox.userremoval; package eu.eudat.integrationevent.outbox.userremoval;
import javax.management.InvalidApplicationException;
import java.util.UUID; import java.util.UUID;
public interface UserRemovalIntegrationEventHandler { public interface UserRemovalIntegrationEventHandler {
void handle(UUID userId); void handle(UUID userId) throws InvalidApplicationException;
} }

View File

@ -1,5 +1,6 @@
package eu.eudat.integrationevent.outbox.userremoval; package eu.eudat.integrationevent.outbox.userremoval;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.data.UserEntity; import eu.eudat.data.UserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; import eu.eudat.integrationevent.outbox.OutboxService;
@ -14,6 +15,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.UUID; import java.util.UUID;
@Component("outboxuserremovalintegrationeventhandler") @Component("outboxuserremovalintegrationeventhandler")
@ -25,14 +27,16 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
private final OutboxService outboxService; private final OutboxService outboxService;
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final TenantScope tenantScope;
public UserRemovalIntegrationEventHandlerImpl(OutboxService outboxService, ApplicationContext applicationContext) { public UserRemovalIntegrationEventHandlerImpl(OutboxService outboxService, ApplicationContext applicationContext, TenantScope tenantScope) {
this.outboxService = outboxService; this.outboxService = outboxService;
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.tenantScope = tenantScope;
} }
@Override @Override
public void handle(UUID userId) { public void handle(UUID userId) throws InvalidApplicationException {
UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class); UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class);
if (!userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(userId))) if (!userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(userId)))
return; return;
@ -40,9 +44,9 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_REMOVE); message.setType(OutboxIntegrationEvent.USER_REMOVE);
if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant());
UserRemovalIntegrationEvent event = new UserRemovalIntegrationEvent(); UserRemovalIntegrationEvent event = new UserRemovalIntegrationEvent();
event.setUserId(userId); event.setUserId(userId);
event.setTenant(null); //TODO
message.setEvent(event); message.setEvent(event);
this.outboxService.publish(message); this.outboxService.publish(message);

View File

@ -2,6 +2,7 @@ package eu.eudat.integrationevent.outbox.usertouched;
import eu.eudat.commons.enums.ContactInfoType; import eu.eudat.commons.enums.ContactInfoType;
import eu.eudat.integrationevent.TrackedEvent; import eu.eudat.integrationevent.TrackedEvent;
import eu.eudat.model.UserContactInfo;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -10,15 +11,13 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
private UUID id; private UUID id;
private UUID tenant;
private String name; private String name;
private String subjectId;
private UserProfile profile; private UserProfile profile;
private List<UserContactInfo> userContactInfo; private List<UserContactInfo> userContactInfo;
private List<TenantUser> tenantUsers;
private List<UserCredential> credentials;
public UUID getId() { public UUID getId() {
return id; return id;
@ -28,14 +27,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.id = id; this.id = id;
} }
public UUID getTenant() {
return tenant;
}
public void setTenant(UUID tenant) {
this.tenant = tenant;
}
public String getName() { public String getName() {
return name; return name;
} }
@ -44,13 +35,6 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.name = name; this.name = name;
} }
public String getSubjectId() {
return subjectId;
}
public void setSubjectId(String subjectId) {
this.subjectId = subjectId;
}
public UserProfile getProfile() { public UserProfile getProfile() {
return profile; return profile;
@ -68,6 +52,22 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
this.userContactInfo = userContactInfo; this.userContactInfo = userContactInfo;
} }
public List<TenantUser> getTenantUsers() {
return tenantUsers;
}
public void setTenantUsers(List<TenantUser> tenantUsers) {
this.tenantUsers = tenantUsers;
}
public List<UserCredential> getCredentials() {
return credentials;
}
public void setCredentials(List<UserCredential> credentials) {
this.credentials = credentials;
}
public static class UserProfile { public static class UserProfile {
private String timezone; private String timezone;
@ -133,4 +133,32 @@ public class UserTouchedIntegrationEvent extends TrackedEvent {
} }
} }
public static class UserCredential {
private String subjectId;
public String getSubjectId() {
return subjectId;
}
public void setSubjectId(String subjectId) {
this.subjectId = subjectId;
}
}
public static class TenantUser {
private UUID tenant;
public UUID getTenant() {
return tenant;
}
public void setTenant(UUID tenant) {
this.tenant = tenant;
}
}
} }

View File

@ -1,9 +1,10 @@
package eu.eudat.integrationevent.outbox.usertouched; package eu.eudat.integrationevent.outbox.usertouched;
import javax.management.InvalidApplicationException;
import java.util.UUID; import java.util.UUID;
public interface UserTouchedIntegrationEventHandler { public interface UserTouchedIntegrationEventHandler {
void handle(UUID userId); void handle(UUID userId) throws InvalidApplicationException;
} }

View File

@ -1,20 +1,18 @@
package eu.eudat.integrationevent.outbox.usertouched; package eu.eudat.integrationevent.outbox.usertouched;
import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.types.user.AdditionalInfoEntity; import eu.eudat.commons.types.user.AdditionalInfoEntity;
import eu.eudat.data.UserContactInfoEntity; import eu.eudat.data.*;
import eu.eudat.data.UserCredentialEntity; import eu.eudat.elastic.data.DmpElasticEntity;
import eu.eudat.data.UserEntity; import eu.eudat.elastic.elasticbuilder.DmpElasticBuilder;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.model.Reference; import eu.eudat.model.*;
import eu.eudat.model.User;
import eu.eudat.model.UserContactInfo;
import eu.eudat.model.UserCredential;
import eu.eudat.model.builder.UserAdditionalInfoBuilder; import eu.eudat.model.builder.UserAdditionalInfoBuilder;
import eu.eudat.query.UserContactInfoQuery; import eu.eudat.query.*;
import eu.eudat.query.UserCredentialQuery; import gr.cite.tools.data.query.Ordering;
import eu.eudat.query.UserQuery; import gr.cite.tools.data.query.Paging;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
@ -24,8 +22,10 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -39,58 +39,89 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
private final OutboxService outboxService; private final OutboxService outboxService;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final MessageSource messageSource; private final MessageSource messageSource;
private final TenantScope tenantScope;
private final TenantEntityManager entityManager;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
public UserTouchedIntegrationEventHandlerImpl( public UserTouchedIntegrationEventHandlerImpl(
OutboxService outboxService, JsonHandlingService jsonHandlingService, MessageSource messageSource, QueryFactory queryFactory) { OutboxService outboxService, JsonHandlingService jsonHandlingService, MessageSource messageSource, TenantScope tenantScope, TenantEntityManager entityManager, QueryFactory queryFactory) {
this.outboxService = outboxService; this.outboxService = outboxService;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.messageSource = messageSource; this.messageSource = messageSource;
this.tenantScope = tenantScope;
this.entityManager = entityManager;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
} }
@Override @Override
public void handle(UUID userId) { public void handle(UUID userId) throws InvalidApplicationException {
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.USER_TOUCH); message.setType(OutboxIntegrationEvent.USER_TOUCH);
if (this.tenantScope.isSet()) message.setTenantId(tenantScope.getTenant());
UserEntity user = this.queryFactory.query(UserQuery.class).ids(userId) try {
.firstAs(new BaseFieldSet().ensure(User._name).ensure(User._additionalInfo)); this.entityManager.disableTenantFilters();
if (user == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<UserContactInfoEntity> userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId) UserEntity user = this.queryFactory.query(UserQuery.class).ids(userId)
.collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value)); .firstAs(new BaseFieldSet().ensure(User._name).ensure(User._additionalInfo));
if (user == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<UserCredentialEntity> userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(userId) List<UserContactInfoEntity> userContactInfoEntities = this.queryFactory.query(UserContactInfoQuery.class).userIds(userId)
.collectAs(new BaseFieldSet().ensure(UserCredential._id, UserCredential._externalId)); .collectAs(new BaseFieldSet().ensure(UserContactInfo._type).ensure(UserContactInfo._ordinal).ensure(UserContactInfo._value));
UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent(); List<UserCredentialEntity> userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(userId)
event.setId(userId); .collectAs(new BaseFieldSet().ensure(UserCredential._id, UserCredential._externalId));
event.setTenant(null); //TODO
event.setName(user.getName()); List<TenantUserEntity> tenantUserEntities = this.queryFactory.query(TenantUserQuery.class).userIds(userId)
if (userCredentialEntities != null && !userCredentialEntities.isEmpty()) .collectAs(new BaseFieldSet().ensure(TenantUser._id, TenantUser._tenant));
event.setSubjectId(userCredentialEntities.getFirst().getExternalId());
event.setProfile(new UserTouchedIntegrationEvent.UserProfile()); UserTouchedIntegrationEvent event = new UserTouchedIntegrationEvent();
AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo()); event.setId(userId);
if (definition != null) { event.setName(user.getName());
event.getProfile().setCulture(definition.getCulture()); if (userCredentialEntities != null && !userCredentialEntities.isEmpty()) {
event.getProfile().setLanguage(definition.getLanguage()); event.setCredentials(new ArrayList<>());
event.getProfile().setTimezone(definition.getTimezone()); for (UserCredentialEntity userCredential : userCredentialEntities){
} UserTouchedIntegrationEvent.UserCredential userCredentialEvent = new UserTouchedIntegrationEvent.UserCredential();
userCredentialEvent.setSubjectId(userCredential.getExternalId());
event.getCredentials().add(userCredentialEvent);
}
}
if (tenantUserEntities != null && !tenantUserEntities.isEmpty()) {
event.setTenantUsers(new ArrayList<>());
for (TenantUserEntity tenantUserEntity : tenantUserEntities){
UserTouchedIntegrationEvent.TenantUser tenantUser = new UserTouchedIntegrationEvent.TenantUser();
tenantUser.setTenant(tenantUserEntity.getTenantId());
event.getTenantUsers().add(tenantUser);
}
}
AdditionalInfoEntity definition = this.jsonHandlingService.fromJsonSafe(AdditionalInfoEntity.class, user.getAdditionalInfo());
if (definition != null) {
event.setProfile(new UserTouchedIntegrationEvent.UserProfile());
event.getProfile().setCulture(definition.getCulture());
event.getProfile().setLanguage(definition.getLanguage());
event.getProfile().setTimezone(definition.getTimezone());
}
if (userContactInfoEntities != null&& !userContactInfoEntities.isEmpty()){
event.setUserContactInfo(new ArrayList<>());
for (UserContactInfoEntity contactInfoEntity : userContactInfoEntities){
UserTouchedIntegrationEvent.UserContactInfo contactInfo = new UserTouchedIntegrationEvent.UserContactInfo();
contactInfo.setType(contactInfoEntity.getType());
contactInfo.setValue(contactInfoEntity.getValue());
contactInfo.setOrdinal(contactInfoEntity.getOrdinal());
event.getUserContactInfo().add(contactInfo);
}
}
message.setEvent(event);
}finally {
this.entityManager.enableTenantFilters();
}
event.setUserContactInfo(new ArrayList<>());
if (userContactInfoEntities != null){
for (UserContactInfoEntity contactInfoEntity : userContactInfoEntities){
UserTouchedIntegrationEvent.UserContactInfo contactInfo = new UserTouchedIntegrationEvent.UserContactInfo();
contactInfo.setType(contactInfoEntity.getType());
contactInfo.setValue(contactInfoEntity.getValue());
contactInfo.setOrdinal(contactInfoEntity.getOrdinal());
event.getUserContactInfo().add(contactInfo);
}
}
message.setEvent(event);
this.outboxService.publish(message); this.outboxService.publish(message);
} }
} }

View File

@ -316,6 +316,7 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
else if (item.prefix(Description._dmp)) return DescriptionEntity._dmpId; else if (item.prefix(Description._dmp)) return DescriptionEntity._dmpId;
else if (item.match(Description._dmp)) return DescriptionEntity._dmpId; else if (item.match(Description._dmp)) return DescriptionEntity._dmpId;
else if (item.match(Description._belongsToCurrentTenant)) return DescriptionEntity._tenantId; else if (item.match(Description._belongsToCurrentTenant)) return DescriptionEntity._tenantId;
else if (item.match(DescriptionEntity._tenantId)) return DescriptionEntity._tenantId;
else return null; else return null;
} }

View File

@ -164,16 +164,25 @@ public class StorageFileQuery extends QueryBase<StorageFileEntity> {
predicates.add(queryContext.CriteriaBuilder.greaterThan(queryContext.Root.get(StorageFileEntity._createdAt), this.createdAfter)); predicates.add(queryContext.CriteriaBuilder.greaterThan(queryContext.Root.get(StorageFileEntity._createdAt), this.createdAfter));
} }
if (this.canPurge != null) { if (this.canPurge != null) {
predicates.add( if (this.canPurge) {
queryContext.CriteriaBuilder.and( predicates.add(
queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgeAt)).not(), queryContext.CriteriaBuilder.and(
queryContext.CriteriaBuilder.lessThan(queryContext.Root.get(StorageFileEntity._purgeAt), Instant.now()) queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(StorageFileEntity._purgeAt)),
) queryContext.CriteriaBuilder.lessThan(queryContext.Root.get(StorageFileEntity._purgeAt), Instant.now())
); )
);
} else {
predicates.add(
queryContext.CriteriaBuilder.or(
queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgeAt)),
queryContext.CriteriaBuilder.greaterThan(queryContext.Root.get(StorageFileEntity._purgeAt), Instant.now())
)
);
}
} }
if (this.isPurged != null) { if (this.isPurged != null) {
if (!this.isPurged){ if (this.isPurged){
predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgedAt)).not()); predicates.add(queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(StorageFileEntity._purgedAt)));
} else { } else {
predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgedAt))); predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(StorageFileEntity._purgedAt)));
} }

View File

@ -260,7 +260,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate, NotificationContactType type) throws InvalidApplicationException { private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate, NotificationContactType type) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent(); NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setTenantId(tenantScope.getTenant());
event.setUserId(userScope.getUserIdSafe()); event.setUserId(userScope.getUserIdSafe());
UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId()); UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId());

View File

@ -3,8 +3,11 @@ package eu.eudat.service.storage;
import eu.eudat.commons.fake.FakeRequestScope; import eu.eudat.commons.fake.FakeRequestScope;
import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.data.StorageFileEntity; import eu.eudat.data.StorageFileEntity;
import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.model.StorageFile; import eu.eudat.model.StorageFile;
import eu.eudat.query.StorageFileQuery; import eu.eudat.query.StorageFileQuery;
import eu.eudat.query.TenantQuery;
import gr.cite.tools.data.query.Ordering; import gr.cite.tools.data.query.Ordering;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
@ -19,6 +22,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
@ -115,10 +119,8 @@ public class StorageFileCleanupTask implements Closeable, ApplicationListener<A
try { try {
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class); QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
StorageFileService storageFileService = this.applicationContext.getBean(StorageFileService.class);
entityManager = entityManagerFactory.createEntityManager();
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction(); transaction = entityManager.getTransaction();
transaction.begin(); transaction.begin();
@ -127,7 +129,22 @@ public class StorageFileCleanupTask implements Closeable, ApplicationListener<A
success = true; success = true;
if (item != null) { if (item != null) {
storageFileService.purgeSafe(fileId); TenantScope tenantScope = this.applicationContext.getBean(TenantScope.class);
TenantEntityManager tenantEntityManager = this.applicationContext.getBean(TenantEntityManager.class);
tenantEntityManager.setEntityManager(entityManager);
StorageFileService storageFileService = this.applicationContext.getBean(StorageFileService.class);
try {
if (item.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(item.getTenantId()).first();
tenantScope.setTempTenant(entityManager, tenant.getId(), tenant.getCode());
} else {
tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode());
}
storageFileService.purgeSafe(fileId);
} finally {
tenantScope.removeTempTenant(entityManager);
}
} }
transaction.commit(); transaction.commit();
@ -150,6 +167,11 @@ public class StorageFileCleanupTask implements Closeable, ApplicationListener<A
return success; return success;
} }
@Transactional
private void purgeSafe(StorageFileService storageFileService, UUID fileId){
storageFileService.purgeSafe(fileId);
}
private CandidateInfo candidate(Instant lastCandidateCreationTimestamp) { private CandidateInfo candidate(Instant lastCandidateCreationTimestamp) {
EntityTransaction transaction = null; EntityTransaction transaction = null;
EntityManager entityManager = null; EntityManager entityManager = null;

View File

@ -142,7 +142,7 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
private UUID getTenantIdFromDatabase(String tenantCode) { private UUID getTenantIdFromDatabase(String tenantCode) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<UserEntity> query = criteriaBuilder.createQuery(UserEntity.class); CriteriaQuery<TenantEntity> query = criteriaBuilder.createQuery(TenantEntity.class);
Root<TenantEntity> root = query.from(TenantEntity.class); Root<TenantEntity> root = query.from(TenantEntity.class);
query = query.where( query = query.where(
criteriaBuilder.and( criteriaBuilder.and(
@ -150,7 +150,7 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active) criteriaBuilder.equal(root.get(TenantEntity._isActive), IsActive.Active)
) )
).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id)); ).multiselect(root.get(TenantEntity._id).alias(TenantEntity._id));
List<UserEntity> results = this.entityManager.createQuery(query).getResultList(); List<TenantEntity> results = this.entityManager.createQuery(query).getResultList();
if (results.size() == 1) { if (results.size() == 1) {
return results.getFirst().getId(); return results.getFirst().getId();
} }

View File

@ -43,6 +43,7 @@ import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.context.request.WebRequestInterceptor;
import javax.management.InvalidApplicationException;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -93,7 +94,7 @@ public class UserInterceptor implements WebRequestInterceptor {
} }
@Override @Override
public void preHandle(WebRequest request) throws InterruptedException { public void preHandle(WebRequest request) throws InterruptedException, InvalidApplicationException {
UUID userId = null; UUID userId = null;
if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) { if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) {
String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal()); String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());