support default tenant as null
This commit is contained in:
parent
77d2dcdc39
commit
cc407c202a
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
String lockId = userScope.getUserId().toString().toLowerCase(Locale.ROOT);
|
||||||
|
try {
|
||||||
|
if (this.tenantScopeProperties.getAutoCreateTenantUser()) usedResource = this.lockByKeyManager.tryLock(lockId, 5000, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
|
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class);
|
CriteriaQuery<Tuple> query = criteriaBuilder.createQuery(Tuple.class);
|
||||||
Root<UserEntity> root = query.from(UserEntity.class);
|
Root<UserEntity> root = query.from(UserEntity.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(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class)
|
criteriaBuilder.in(root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(TenantUserEntity.class, UUID.class)
|
||||||
.query(query)
|
.query(query)
|
||||||
.criteriaBuilder(criteriaBuilder)
|
.criteriaBuilder(criteriaBuilder)
|
||||||
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(TenantUserEntity._userId))
|
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(TenantUserEntity._userId))
|
||||||
.filterFunc((subQueryRoot, cb) ->
|
.filterFunc((subQueryRoot, cb) ->
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return cb.and(
|
return cb.and(
|
||||||
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()),
|
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._tenantId), this.tenantScope.getTenant()),
|
||||||
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()),
|
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._userId), this.userScope.getUserId()),
|
||||||
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active)
|
criteriaBuilder.equal(subQueryRoot.get(TenantUserEntity._isActive), IsActive.Active)
|
||||||
);
|
);
|
||||||
} catch (InvalidApplicationException e) {
|
} catch (InvalidApplicationException e) {
|
||||||
throw new RuntimeException(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
|
@ -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' ]
|
Loading…
Reference in New Issue