support default tenant as null

This commit is contained in:
Efstratios Giannopoulos 2024-04-02 13:07:00 +03:00
parent 77d2dcdc39
commit cc407c202a
12 changed files with 191 additions and 118 deletions

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,12 @@ public class MultitenancyProperties {
public void setIsMultitenant(boolean multitenant) { public void setIsMultitenant(boolean multitenant) {
isMultitenant = multitenant; isMultitenant = multitenant;
} }
public String getDefaultTenantCode() {
return defaultTenantCode;
}
public void setDefaultTenantCode(String defaultTenantCode) {
this.defaultTenantCode = defaultTenantCode;
}
} }

View File

@ -1,10 +1,8 @@
package eu.eudat.commons.scope.tenant; package eu.eudat.commons.scope.tenant;
import eu.eudat.data.tenant.TenantScopedBaseEntity; import eu.eudat.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;
@ -22,7 +20,6 @@ public class TenantScope {
public static final String TenantClaimName = "x-tenant"; public static final String TenantClaimName = "x-tenant";
private final MultitenancyProperties multitenancy; private final MultitenancyProperties multitenancy;
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantScope.class));
private final AtomicReference<UUID> tenant = new AtomicReference<>(); private final AtomicReference<UUID> tenant = new AtomicReference<>();
private final AtomicReference<String> tenantCode = new AtomicReference<>(); private final AtomicReference<String> tenantCode = new AtomicReference<>();
private final AtomicReference<UUID> initialTenant = new AtomicReference<>(); private final AtomicReference<UUID> initialTenant = new AtomicReference<>();
@ -37,45 +34,70 @@ public class TenantScope {
return multitenancy.isMultitenant(); return multitenancy.isMultitenant();
} }
public String getDefaultTenantCode() {
return multitenancy.getDefaultTenantCode();
}
public Boolean isSet() { public Boolean isSet() {
if (!this.isMultitenant()) if (!this.isMultitenant())
return Boolean.TRUE; return Boolean.TRUE;
return this.tenant.get() != null; 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()) if (!this.isMultitenant())
return null; return null;
if (this.tenant.get() == null) if (this.tenant.get() == null && !this.isDefaultTenant())
throw new InvalidApplicationException("tenant not set"); throw new InvalidApplicationException("tenant not set");
return this.tenant.get(); return this.isDefaultTenant() ? null : this.tenant.get();
} }
public String getTenantCode() throws InvalidApplicationException { public String getTenantCode() throws InvalidApplicationException {
if (!this.isMultitenant()) if (!this.isMultitenant())
return null; return null;
if (this.tenant.get() == null) if (this.tenantCode.get() == null)
throw new InvalidApplicationException("tenant not set"); throw new InvalidApplicationException("tenant not set");
return this.tenantCode.get(); return this.tenantCode.get();
} }
public void setTempTenant(EntityManager entityManager, UUID tenant) { public void setTempTenant(EntityManager entityManager, UUID tenant, String tenantCode) {
this.tenant.set(tenant); this.tenant.set(tenant);
this.tenantCode.set(tenantCode);
if (this.tenant.get() != null) { if (this.tenant.get() != null && !this.isDefaultTenant()) {
entityManager if(!this.isDefaultTenant()) {
.unwrap(Session.class) entityManager
.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.tenant.get().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.set(this.initialTenant.get()); this.tenant.set(this.initialTenant.get());
this.tenantCode.set(this.initialTenantCode.get()); this.tenantCode.set(this.initialTenantCode.get());
if (this.initialTenant.get() != null) { if (this.initialTenant.get() != null && !this.isDefaultTenant()) {
entityManager if(!this.isDefaultTenant()) {
.unwrap(Session.class) entityManager
.enableFilter(TenantScopedBaseEntity.tenantFilter).setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, this.initialTenant.get().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);
}
} }
} }

View File

@ -41,7 +41,7 @@ public class TenantEntityManager {
// this.claimExtractor.subjectUUID(this.currentPrincipalResolver.currentPrincipal()); // this.claimExtractor.subjectUUID(this.currentPrincipalResolver.currentPrincipal());
// boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant); // boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant);
boolean isAllowedNoTenant = ((TenantScoped) entity).allowNullTenant(); boolean isAllowedNoTenant = ((TenantScoped) entity).allowNullTenant() || this.tenantScope.isDefaultTenant();
final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null;
if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering"); if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering");
} }
@ -50,7 +50,7 @@ public class TenantEntityManager {
public void remove(Object entity) throws InvalidApplicationException { public void remove(Object entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) { if (tenantScope.isMultitenant() && (entity instanceof TenantScoped)) {
boolean isAllowedNoTenant = ((TenantScoped) entity).allowNullTenant(); boolean isAllowedNoTenant = ((TenantScoped) entity).allowNullTenant() || this.tenantScope.isDefaultTenant();
final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null;
if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering"); if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) throw new MyForbiddenException("tenant tampering");
} }
@ -65,7 +65,7 @@ public class TenantEntityManager {
// this.claimExtractor.subjectUUID(this.currentPrincipalResolver.currentPrincipal()); // this.claimExtractor.subjectUUID(this.currentPrincipalResolver.currentPrincipal());
// boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant); // boolean isAllowedNoTenant = authorizationService.authorize(Permission.AllowNoTenant);
boolean isAllowedNoTenant = ((TenantScoped) entity).allowNullTenant(); boolean isAllowedNoTenant = ((TenantScoped) entity).allowNullTenant() || this.tenantScope.isDefaultTenant();
final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null; final UUID tenantId = !isAllowedNoTenant ? tenantScope.getTenant() : null;
if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) return null; if (!isAllowedNoTenant && !tenantId.equals(((TenantScoped) entity).getTenantId())) return null;
} }

View File

@ -29,11 +29,16 @@ public class TenantFilterAspect {
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 && retVal instanceof EntityManager && tenantScope.isSet()) { if (retVal instanceof EntityManager && tenantScope.isSet()) {
Session session = ((EntityManager) retVal).unwrap(Session.class); Session session = ((EntityManager) retVal).unwrap(Session.class);
session if(!tenantScope.isDefaultTenant()) {
.enableFilter(TenantScopedBaseEntity.tenantFilter) session
.setParameter(TenantScopedBaseEntity.tenantFilterTenantParam, tenantScope.getTenant().toString()); .enableFilter(TenantScopedBaseEntity.TENANT_FILTER)
.setParameter(TenantScopedBaseEntity.TENANT_FILTER_TENANT_PARAM, tenantScope.getTenant().toString());
} else {
session
.enableFilter(TenantScopedBaseEntity.DEFAULT_TENANT_FILTER);
}
} }
} }

View File

@ -29,11 +29,11 @@ public class TenantListener {
@PrePersist @PrePersist
public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException { public void setTenantOnCreate(TenantScoped entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant()) { if (tenantScope.isMultitenant()) {
if (entity.getTenantId() != null && entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { if (entity.getTenantId() != null && (this.tenantScope.isDefaultTenant() || entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) {
logger.error("somebody tried to set not login tenat"); logger.error("somebody tried to set not login tenant");
throw new MyForbiddenException("tenant tampering"); throw new MyForbiddenException("tenant tampering");
} }
if (!entity.allowNullTenant()) { if (!entity.allowNullTenant() && !tenantScope.isDefaultTenant()) {
final UUID tenantId = tenantScope.getTenant(); final UUID tenantId = tenantScope.getTenant();
entity.setTenantId(tenantId); entity.setTenantId(tenantId);
} }
@ -47,19 +47,26 @@ public class TenantListener {
public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException { public void setTenantOnUpdate(TenantScoped entity) throws InvalidApplicationException {
if (tenantScope.isMultitenant()) { if (tenantScope.isMultitenant()) {
if (!entity.allowNullTenant()){ if (!entity.allowNullTenant()){
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("tenant tampering");
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("tenant tampering");
}
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("tenant tampering");
}
}
} else { } else {
if (entity.getTenantId() != null && entity.getTenantId().compareTo(tenantScope.getTenant()) != 0) { if (entity.getTenantId() != null && (!this.tenantScope.isDefaultTenant() ||entity.getTenantId().compareTo(tenantScope.getTenant()) != 0)) {
logger.error("somebody tried to change an entries tenant"); logger.error("somebody tried to change an entries tenant");
throw new MyForbiddenException("tenant tampering"); throw new MyForbiddenException("tenant tampering");
} }

View File

@ -14,13 +14,16 @@ 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)) or tenant is null)") @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 {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final String tenantFilter = "tenantFilter"; public static final String TENANT_FILTER = "tenantFilter";
public static final String tenantFilterTenantParam = "tenantId"; public static final String DEFAULT_TENANT_FILTER = "defaultTenantFilter";
public static final String TENANT_FILTER_TENANT_PARAM = "tenantId";
@Column(name = "tenant", columnDefinition = "uuid", nullable = true) @Column(name = "tenant", columnDefinition = "uuid", nullable = true)
private UUID tenantId; private UUID tenantId;

View File

@ -38,15 +38,6 @@ public class DescriptionTemplateTypePersist {
private DescriptionTemplateTypeStatus status; private DescriptionTemplateTypeStatus status;
private Map<UUID, DescriptionTemplateTypePersist> test; private Map<UUID, DescriptionTemplateTypePersist> test;
public final static String _test = "test";
public Map<UUID, DescriptionTemplateTypePersist> getTest() {
return test;
}
public void setTest(Map<UUID, DescriptionTemplateTypePersist> test) {
this.test = test;
}
public UUID getId() { public UUID getId() {
return id; return id;
@ -86,12 +77,10 @@ public class DescriptionTemplateTypePersist {
public static final String ValidatorName = "DescriptionTemplateTypePersistValidator"; public static final String ValidatorName = "DescriptionTemplateTypePersistValidator";
private final MessageSource messageSource; private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
public DescriptionTemplateTypePersistValidator(MessageSource messageSource, ConventionService conventionService, ErrorThesaurusProperties errors, ValidatorFactory validatorFactory) { public DescriptionTemplateTypePersistValidator(MessageSource messageSource, ConventionService conventionService, ErrorThesaurusProperties errors) {
super(conventionService, errors); super(conventionService, errors);
this.messageSource = messageSource; this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
} }
@Override @Override

View File

@ -3,10 +3,9 @@ package eu.eudat.interceptors.tenant;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.lock.LockByKeyManager;
import eu.eudat.commons.scope.tenant.TenantScope; import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DmpReferenceEntity;
import eu.eudat.data.ReferenceEntity;
import eu.eudat.data.TenantUserEntity; import eu.eudat.data.TenantUserEntity;
import eu.eudat.data.UserEntity; import eu.eudat.data.UserEntity;
import eu.eudat.data.tenant.TenantScopedBaseEntity; import eu.eudat.data.tenant.TenantScopedBaseEntity;
@ -24,7 +23,6 @@ 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.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -46,6 +44,7 @@ import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component @Component
public class TenantInterceptor implements WebRequestInterceptor { public class TenantInterceptor implements WebRequestInterceptor {
@ -61,6 +60,7 @@ public class TenantInterceptor implements WebRequestInterceptor {
private final PlatformTransactionManager transactionManager; private final PlatformTransactionManager transactionManager;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final QueryUtilsService queryUtilsService; private final QueryUtilsService queryUtilsService;
private final LockByKeyManager lockByKeyManager;
@PersistenceContext @PersistenceContext
public EntityManager entityManager; public EntityManager entityManager;
@ -74,7 +74,7 @@ public class TenantInterceptor implements WebRequestInterceptor {
TenantScopeProperties tenantScopeProperties, TenantScopeProperties tenantScopeProperties,
UserAllowedTenantCacheService userAllowedTenantCacheService, UserAllowedTenantCacheService userAllowedTenantCacheService,
PlatformTransactionManager transactionManager, PlatformTransactionManager transactionManager,
ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService) { ErrorThesaurusProperties errors, QueryUtilsService queryUtilsService, LockByKeyManager lockByKeyManager) {
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.userScope = userScope; this.userScope = userScope;
this.currentPrincipalResolver = currentPrincipalResolver; this.currentPrincipalResolver = currentPrincipalResolver;
@ -85,10 +85,11 @@ public class TenantInterceptor implements WebRequestInterceptor {
this.transactionManager = transactionManager; this.transactionManager = transactionManager;
this.errors = errors; this.errors = errors;
this.queryUtilsService = queryUtilsService; this.queryUtilsService = queryUtilsService;
this.lockByKeyManager = lockByKeyManager;
} }
@Override @Override
public void preHandle(@NotNull 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;
@ -101,18 +102,29 @@ public class TenantInterceptor implements WebRequestInterceptor {
} }
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);
@ -143,40 +155,47 @@ 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()) {
boolean usedResource = false;
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder(); String lockId = userScope.getUserId().toString().toLowerCase(Locale.ROOT);
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class); try {
Root<UserEntity> root = query.from(UserEntity.class); if (this.tenantScopeProperties.getAutoCreateTenantUser()) usedResource = this.lockByKeyManager.tryLock(lockId, 5000, TimeUnit.MILLISECONDS);
query.where(criteriaBuilder.and(
criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active), CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
criteriaBuilder.in(root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class) CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class);
.query(query) Root<UserEntity> root = query.from(UserEntity.class);
.criteriaBuilder(criteriaBuilder) query.where(criteriaBuilder.and(
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(TenantUserEntity._userId)) criteriaBuilder.equal(root.get(UserEntity._isActive), IsActive.Active),
.filterFunc((subQueryRoot, cb) -> criteriaBuilder.in(root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class)
{ .query(query)
try { .criteriaBuilder(criteriaBuilder)
return cb.and( .keyPathFunc((subQueryRoot) -> subQueryRoot.get(TenantUserEntity._userId))
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()), .filterFunc((subQueryRoot, cb) ->
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()), {
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active) try {
); return cb.and(
} catch (InvalidApplicationException e) { criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()),
throw new RuntimeException(e); 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<Tuple> results = this.entityManager.createQuery(query).getResultList(); if (results.isEmpty() && this.tenantScopeProperties.getAutoCreateTenantUser()) {
if (results.isEmpty() && this.tenantScopeProperties.getAutoCreateTenantUser()) { return this.createTenantUser();
return this.createTenantUser(); } else {
} else { return !results.isEmpty();
return !results.isEmpty(); }
} finally {
if (usedResource) this.lockByKeyManager.unlock(lockId);
} }
} }

View File

@ -78,21 +78,26 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
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()) if (!scoped && this.tenantScope.isSet() && this.tenantScopeProperties.getEnforceTrustedTenant())
throw new MyForbiddenException(this.errorThesaurusProperties.getMissingTenant().getCode(), this.errorThesaurusProperties.getMissingTenant().getMessage()); 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()) if (this.conventionService.isNullOrEmpty(tenantCode)) tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName);
tenantCode = this.claimExtractor.asString(principal, this.clientTenantClaimName); if (tenantCode == null || this.conventionService.isNullOrEmpty(tenantCode)) return false;
if (tenantCode.equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())){
logger.debug("parsed tenant header and set tenant to default tenant");
this.tenantScope.setTenant(null, tenantCode);
this.claimExtractorContext.putReplaceParameter(TenantScope.TenantReplaceParameter, tenantCode);
return true;
}
UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode); UUID tenantId = this.conventionService.parseUUIDSafe(tenantCode);
if (tenantId == null && tenantCode == null)
return Boolean.FALSE;
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) {
@ -115,21 +120,22 @@ public class TenantScopeClaimInterceptor implements WebRequestInterceptor {
} }
} }
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) {

View File

@ -62,10 +62,16 @@ public class TenantScopeHeaderInterceptor implements WebRequestInterceptor {
String tenantCode = request.getHeader(TenantScope.TenantClaimName); String tenantCode = request.getHeader(TenantScope.TenantClaimName);
logger.debug("retrieved request tenant header is: {}", 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) {
@ -86,7 +92,7 @@ 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 {}", 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);

View File

@ -0,0 +1,10 @@
tenant:
configEncryptionAesKey: rmpTvZnRWzyisUtFADBcZCn0q7Z75Xdz
configEncryptionAesIv: ec05d521a23f80ad
multitenancy:
is-multitenant: true
default-tenant-code: default
interceptor:
client-claims-prefix: client_
enforce-trusted-tenant: false
auto-create-tenant-user: true

View File

@ -1,11 +1,8 @@
tenant: tenant:
configEncryptionAesKey: rmpTvZnRWzyisUtFADBcZCn0q7Z75Xdz
configEncryptionAesIv: ec05d521a23f80ad
multitenancy: multitenancy:
is-multitenant: true is-multitenant: false
interceptor: interceptor:
client-claims-prefix: client_
white-listed-clients: [ ] white-listed-clients: [ ]
enforce-trusted-tenant: false enforce-trusted-tenant: false
auto-create-tenant-user: true auto-create-tenant-user: false
white-listed-endpoints: [ '/api/principal/my-tenants', '/api/principal/me' ] white-listed-endpoints: [ '/api/principal/my-tenants', '/api/principal/me' ]