multi tenant changes

This commit is contained in:
Efstratios Giannopoulos 2024-04-04 12:21:12 +03:00
parent ea6ea25116
commit bbe2d91830
30 changed files with 202 additions and 262 deletions

View File

@ -1,9 +1,9 @@
package gr.cite.annotation.web;
import gr.cite.annotation.web.interceptors.UserInterceptor;
import gr.cite.annotation.web.scope.tenant.TenantInterceptor;
import gr.cite.annotation.web.scope.tenant.TenantScopeClaimInterceptor;
import gr.cite.annotation.web.scope.tenant.TenantScopeHeaderInterceptor;
import gr.cite.annotation.web.scope.user.UserInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

View File

@ -29,6 +29,7 @@ import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -104,7 +105,7 @@ public class SecurityConfiguration {
//In the example below, the default client handler will be ignored by the resolver
@Override
public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() {
return List.of(PermissionClientAuthorizationHandler.class);
return new ArrayList<>();
}
};
}

View File

@ -1,17 +0,0 @@
//package gr.cite.intelcomp.stiviewer.web.controllers.error;
//
//import gr.cite.tools.exception.MyValidationException;
//import org.springframework.http.HttpStatus;
//import org.springframework.http.ResponseEntity;
//import org.springframework.web.bind.annotation.ControllerAdvice;
//import org.springframework.web.bind.annotation.ExceptionHandler;
//import org.springframework.web.context.request.WebRequest;
//
//@ControllerAdvice
//public class GenericErrorHandler {
//
// @ExceptionHandler(MyValidationException.class)
// public ResponseEntity<MyValidationException> handleValidationException(MyValidationException e, WebRequest webRequest) {
// return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(e);
// }
//}

View File

@ -1,92 +0,0 @@
package gr.cite.annotation.web.interceptors;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.lock.LockByKeyManager;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.UserCredentialEntity;
import gr.cite.annotation.model.UserCredential;
import gr.cite.annotation.query.UserCredentialQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
import java.util.UUID;
@Component
public class UserInterceptor implements WebRequestInterceptor {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserInterceptor.class));
private final UserScope userScope;
private final ClaimExtractor claimExtractor;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final UserInterceptorCacheService userInterceptorCacheService;
private final QueryFactory queryFactory;
@PersistenceContext
public EntityManager entityManager;
@Autowired
public UserInterceptor(
UserScope userScope,
ClaimExtractor claimExtractor,
CurrentPrincipalResolver currentPrincipalResolver,
UserInterceptorCacheService userInterceptorCacheService,
QueryFactory queryFactory) {
this.userScope = userScope;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractor = claimExtractor;
this.userInterceptorCacheService = userInterceptorCacheService;
this.queryFactory = queryFactory;
}
@Override
public void preHandle(WebRequest request) {
UUID userId = null;
if (this.currentPrincipalResolver.currentPrincipal().isAuthenticated()) {
String subjectId = this.claimExtractor.subjectString(this.currentPrincipalResolver.currentPrincipal());
if (subjectId == null || subjectId.isBlank()) throw new MyForbiddenException("Empty subjects not allowed");
UserInterceptorCacheService.UserInterceptorCacheValue cacheValue = this.userInterceptorCacheService.lookup(this.userInterceptorCacheService.buildKey(subjectId));
if (cacheValue != null) {
userId = cacheValue.getUserId();
} else {
userId = this.findExistingUserFromDbForce(subjectId);
cacheValue = new UserInterceptorCacheService.UserInterceptorCacheValue(subjectId, userId);
this.userInterceptorCacheService.put(cacheValue);
}
}
this.userScope.setUserId(userId);
}
private UUID findExistingUserFromDbForce(String subjectId){
UserCredentialEntity userCredential = this.queryFactory.query(UserCredentialQuery.class).externalIds(subjectId).firstAs(new BaseFieldSet().ensure(UserCredential._user));
if (userCredential != null) {
return userCredential.getUserId();
} else {
throw new MyForbiddenException("User not created try again.");
}
}
@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 +0,0 @@
package gr.cite.annotation.web.interceptors;
import gr.cite.tools.cache.CacheOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.user-by-subject-id")
public class UserInterceptorCacheOptions extends CacheOptions {
}

View File

@ -1,66 +0,0 @@
package gr.cite.annotation.web.interceptors;
import gr.cite.tools.cache.CacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.UUID;
@Service
public class UserInterceptorCacheService extends CacheService<UserInterceptorCacheService.UserInterceptorCacheValue> {
public static class UserInterceptorCacheValue {
public UserInterceptorCacheValue() {
}
public UserInterceptorCacheValue(String subjectId, UUID userId) {
this.subjectId = subjectId;
this.userId = userId;
}
private String subjectId;
public String getSubjectId() {
return subjectId;
}
public void setSubjectId(String subjectId) {
this.subjectId = subjectId;
}
private UUID userId;
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
}
@Autowired
public UserInterceptorCacheService(UserInterceptorCacheOptions options) {
super(options);
}
@Override
protected Class<UserInterceptorCacheValue> valueClass() {
return UserInterceptorCacheValue.class;
}
@Override
public String keyOf(UserInterceptorCacheValue value) {
return this.buildKey(value.getSubjectId());
}
public String buildKey(String subject) {
HashMap<String, String> keyParts = new HashMap<>();
keyParts.put("$subject$", subject);
return this.generateKey(keyParts);
}
}

View File

@ -20,11 +20,25 @@ idpclient:
Roles:
- type: resource_access
path: dmp_web.roles
- type: tenant_roles
filterBy: "(.*):::TenantCode::"
extractByExpression: "(.*):(.*)"
extractExpressionValue: "[[g1]]"
GlobalRoles:
- type: resource_access
path: dmp_web.roles
TenantRoles:
- type: tenant_roles
filterBy: "(.*):::TenantCode::"
extractByExpression: "(.*):(.*)"
extractExpressionValue: "[[g1]]"
Scope:
- type: scope
AccessToken:
- type: x-access-token
visibility: SENSITIVE
Tenant:
- type: x-tenant
IssuedAt:
- type: iat
Issuer:
@ -37,5 +51,8 @@ idpclient:
- type: azp
Authorities:
- type: authorities
ExternalProviderName:
- type: identity_provider
TenantCodes:
- type: tenant_roles
filterBy: "(.*):(.*)"
extractByExpression: "(.*):(.*)"
extractExpressionValue: "[[g2]]"

View File

@ -2,10 +2,10 @@ permissions:
policies:
DeferredAffiliation:
roles:
- Admin
- User
- Manager
- DescriptionTemplateEditor
- TenantAdmin
- TenantUser
- TenantManager
- TenantDescriptionTemplateEditor
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
@ -19,14 +19,14 @@ permissions:
EditTenant:
roles:
- Admin
clients: [ ]
clients: [ "opendmp-api-dev" ]
allowAnonymous: false
allowAuthenticated: false
DeleteTenant:
roles:
- Admin
claims: [ ]
clients: [ ]
clients: [ "opendmp-api-dev" ]
allowAnonymous: false
allowAuthenticated: false
AllowNoTenant:
@ -39,47 +39,47 @@ permissions:
# Users
BrowseUser:
roles:
- Admin
- TenantAdmin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditUser:
roles:
- Admin
clients: [ ]
- TenantAdmin
clients: [ "opendmp-api-dev" ]
allowAnonymous: false
allowAuthenticated: false
DeleteUser:
roles:
- Admin
- TenantAdmin
claims: [ ]
clients: [ ]
clients: [ "opendmp-api-dev" ]
allowAnonymous: false
allowAuthenticated: false
#Annotation
BrowseAnnotation:
roles:
- Admin
- TenantAdmin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
NewAnnotation:
roles:
- Admin
- TenantAdmin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditAnnotation:
roles:
- Admin
- TenantAdmin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
DeleteAnnotation:
roles:
- Admin
- TenantAdmin
entityAffiliated: false
clients: [ ]
allowAnonymous: false
@ -87,13 +87,13 @@ permissions:
#Tenant Configuration
BrowseTenantConfiguration:
roles:
- Admin
- TenantAdmin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
EditTenantConfiguration:
roles:
- Admin
- TenantAdmin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false

View File

@ -82,6 +82,7 @@ public class TenantEntityManager {
}
public void enableTenantFilters() throws InvalidApplicationException {
if (!tenantScope.isSet()) return;
if(!tenantScope.isDefaultTenant()) {
this.entityManager
.unwrap(Session.class)

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.integrationevent;
import gr.cite.annotation.data.QueueOutboxEntity;
import gr.cite.annotation.integrationevent.outbox.OutboxProperties;
import gr.cite.annotation.integrationevent.outbox.OutboxRepositoryImpl;
import gr.cite.queueoutbox.IntegrationEventContextCreator;
@ -56,7 +57,11 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
@Bean
public IntegrationEventContextCreator integrationEventContextCreator() {
return (message) -> new IntegrationEventContextImpl();
return (message) -> {
IntegrationEventContextImpl integrationEventContext = new IntegrationEventContextImpl();
if (message instanceof QueueOutboxEntity) integrationEventContext.setTenant(((QueueOutboxEntity)message).getTenantId());
return integrationEventContext;
};
}
@Bean

View File

@ -1,11 +1,13 @@
package gr.cite.annotation.integrationevent.inbox;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -22,12 +24,15 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
this.isAuthenticated = isAuthenticated;
}
public static InboxPrincipal build(IntegrationEventProperties properties) {
public static InboxPrincipal build(IntegrationEventProperties properties, ClaimExtractorProperties claimExtractorProperties) {
InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId");
inboxPrincipal.put("client_id", properties.getAppId());
List<ClaimExtractorProperties.KeyPath> clientKey = claimExtractorProperties.getMapping().getOrDefault("Client", null);
inboxPrincipal.put(clientKey != null && clientKey.getFirst() != null ? clientKey.getFirst().getType() : "client_id", properties.getAppId());
inboxPrincipal.put("active", "true");
inboxPrincipal.put("nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString());
inboxPrincipal.put("exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString());
List<ClaimExtractorProperties.KeyPath> notBeforeKey = claimExtractorProperties.getMapping().getOrDefault("NotBefore", null);
inboxPrincipal.put(notBeforeKey != null && notBeforeKey.getFirst() != null ? notBeforeKey.getFirst().getType() :"nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString());
List<ClaimExtractorProperties.KeyPath> expiresAt = claimExtractorProperties.getMapping().getOrDefault("ExpiresAt", null);
inboxPrincipal.put(expiresAt != null && expiresAt.getFirst() != null ? expiresAt.getFirst().getType() :"exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString());
return inboxPrincipal;
}
@ -45,7 +50,10 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
public List<String> getClaimAsStringList(String claim) {
if (claims == null)
return null;
return this.getClaimAsStringList(claim);
if (this.claims.containsKey(claim)){
return List.of(this.claims.get(claim).toString());
}
return null;
}
@Override

View File

@ -245,6 +245,12 @@ public class InboxRepositoryImpl implements InboxRepository {
queueMessage.setId(UUID.randomUUID());
Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null;
if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId);
else if (tenantId instanceof String) {
try {
queueMessage.setTenantId(UUID.fromString((String) tenantId));
} catch (Exception e) {
}
}
queueMessage.setExchange(this.inboxProperties.getExchange());
queueMessage.setRoute(inboxCreatorParams.getRoutingKey());
queueMessage.setQueue(inboxCreatorParams.getQueueName());

View File

@ -15,6 +15,7 @@ import gr.cite.annotation.model.Tenant;
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.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
@ -83,7 +84,9 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
}
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();

View File

@ -15,6 +15,7 @@ import gr.cite.annotation.model.Tenant;
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.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
@ -85,7 +86,9 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
}
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
@ -114,6 +117,7 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
data.setId(UUID.randomUUID());
data.setEntityId(entityEvent.getEntityId());
data.setUserId(user);
data.setTenantId(properties.getTenantId());
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);

View File

@ -10,6 +10,7 @@ import gr.cite.annotation.integrationevent.inbox.EventProcessingStatus;
import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.service.tenant.TenantService;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager;
@ -53,7 +54,9 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler = this.applicationContext.getBean(TenantRemovalConsistencyHandler.class);
if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId()))))

View File

@ -10,6 +10,8 @@ import gr.cite.annotation.integrationevent.inbox.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.persist.TenantTouchedIntegrationEventPersist;
import gr.cite.annotation.service.tenant.TenantService;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory;
@ -59,7 +61,8 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();

View File

@ -14,6 +14,7 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.query.TenantQuery;
import gr.cite.annotation.service.user.UserService;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyValidationException;
@ -89,7 +90,9 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
}
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class);
if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId()))))

View File

@ -13,6 +13,7 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.query.TenantQuery;
import gr.cite.annotation.service.user.UserService;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
@ -82,7 +83,9 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
}
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
ClaimExtractorProperties claimExtractorProperties = this.applicationContext.getBean(ClaimExtractorProperties.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();

View File

@ -75,6 +75,7 @@ public class TenantServiceImpl implements TenantService {
data.setCode(model.getCode());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
this.entityManager.persist(data);
} else {

View File

@ -5,15 +5,19 @@ import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.*;
import gr.cite.annotation.integrationevent.inbox.usertouch.UserTouchedIntegrationEvent;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.Tenant;
import gr.cite.annotation.model.User;
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.query.TenantQuery;
import gr.cite.annotation.query.TenantUserQuery;
import gr.cite.annotation.query.UserContactInfoQuery;
import gr.cite.annotation.query.UserCredentialQuery;
@ -31,6 +35,8 @@ import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
@ -57,6 +63,9 @@ public class UserServiceImpl implements UserService {
private final BuilderFactory builderFactory;
private final QueryFactory queryFactory;
private final TenantScope tenantScope;
private final MessageSource messageSource;
private final JsonHandlingService jsonHandlingService;
@ -65,7 +74,7 @@ public class UserServiceImpl implements UserService {
DeleterFactory deleterFactory,
ConventionService conventionService,
TenantEntityManager entityManager,
BuilderFactory builderFactory, QueryFactory queryFactory,
BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource,
JsonHandlingService jsonHandlingService) {
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -73,6 +82,8 @@ public class UserServiceImpl implements UserService {
this.entityManager = entityManager;
this.builderFactory = builderFactory;
this.queryFactory = queryFactory;
this.tenantScope = tenantScope;
this.messageSource = messageSource;
this.jsonHandlingService = jsonHandlingService;
}
@ -197,19 +208,31 @@ public class UserServiceImpl implements UserService {
.userIds(userId)
.isActive(IsActive.Active)
.collect();
List<UUID> updatedCreatedIds = new ArrayList<>();
if (models != null) {
List<TenantEntity> tenantEntities = this.queryFactory.query(TenantQuery.class)
.ids(models.stream().map(UserTouchedIntegrationEvent.TenantUser::getTenant).toList())
.isActive(IsActive.Active)
.collectAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
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);
try {
TenantEntity tenant = tenantEntities.stream().filter(x -> x.getId().equals(model.getTenant())).findFirst().orElse(null);
if (tenant == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getTenant(), Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.tenantScope.setTempTenant(this.entityManager.getEntityManager(), tenant.getId(), tenant.getCode());
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);
} finally {
this.tenantScope.removeTempTenant(this.entityManager.getEntityManager());
}
}
updatedCreatedIds.add(data.getId());
}

View File

@ -87,6 +87,7 @@ public class TenantEntityManager {
}
public void enableTenantFilters() throws InvalidApplicationException {
if (!tenantScope.isSet()) return;
if(!tenantScope.isDefaultTenant()) {
this.entityManager
.unwrap(Session.class)

View File

@ -1,6 +1,7 @@
package eu.eudat.integrationevent;
import eu.eudat.data.QueueOutboxEntity;
import eu.eudat.integrationevent.outbox.OutboxProperties;
import eu.eudat.integrationevent.outbox.OutboxRepositoryImpl;
import gr.cite.queueoutbox.IntegrationEventContextCreator;
@ -57,7 +58,11 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
@Bean
public IntegrationEventContextCreator integrationEventContextCreator() {
return (message) -> new IntegrationEventContextImpl();
return (message) -> {
IntegrationEventContextImpl integrationEventContext = new IntegrationEventContextImpl();
if (message instanceof QueueOutboxEntity) integrationEventContext.setTenant(((QueueOutboxEntity)message).getTenantId());
return integrationEventContext;
};
}
@Bean

View File

@ -1,6 +1,7 @@
package eu.eudat.integrationevent.inbox;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
@ -11,8 +12,9 @@ import java.util.List;
import java.util.Map;
public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
private Map<String, Object> claims;
private boolean isAuthenticated;
private final Map<String, Object> claims;
private final boolean isAuthenticated;
public InboxPrincipal(Boolean isAuthenticated, String name) {
this.claims = new HashMap<>();
@ -20,6 +22,18 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
this.isAuthenticated = isAuthenticated;
}
public static InboxPrincipal build(IntegrationEventProperties properties, ClaimExtractorProperties claimExtractorProperties) {
InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId");
List<ClaimExtractorProperties.KeyPath> clientKey = claimExtractorProperties.getMapping().getOrDefault("Client", null);
inboxPrincipal.put(clientKey != null && clientKey.getFirst() != null ? clientKey.getFirst().getType() : "client_id", properties.getAppId());
inboxPrincipal.put("active", "true");
List<ClaimExtractorProperties.KeyPath> notBeforeKey = claimExtractorProperties.getMapping().getOrDefault("NotBefore", null);
inboxPrincipal.put(notBeforeKey != null && notBeforeKey.getFirst() != null ? notBeforeKey.getFirst().getType() :"nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString());
List<ClaimExtractorProperties.KeyPath> expiresAt = claimExtractorProperties.getMapping().getOrDefault("ExpiresAt", null);
inboxPrincipal.put(expiresAt != null && expiresAt.getFirst() != null ? expiresAt.getFirst().getType() :"exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString());
return inboxPrincipal;
}
@Override
public Boolean isAuthenticated() {
return this.isAuthenticated;
@ -32,8 +46,12 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
@Override
public List<String> getClaimAsStringList(String claim) {
if (claims == null) return null;
return this.getClaimAsStringList(claim);
if (claims == null)
return null;
if (this.claims.containsKey(claim)){
return List.of(this.claims.get(claim).toString());
}
return null;
}
@Override
@ -44,13 +62,4 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
public void put(String key, Object value) {
this.claims.put(key, value);
}
public static InboxPrincipal build(IntegrationEventProperties properties) {
InboxPrincipal inboxPrincipal = new InboxPrincipal(true, "IntegrationEventQueueAppId");
inboxPrincipal.put("client_id", properties.getAppId());
inboxPrincipal.put("active", "true");
inboxPrincipal.put("nbf", Instant.now().minus(30, ChronoUnit.SECONDS).toString());
inboxPrincipal.put("exp", Instant.now().plus(10, ChronoUnit.MINUTES).toString());
return inboxPrincipal;
}
}

View File

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

View File

@ -1,10 +1,15 @@
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 org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler {
private final OutboxService outboxService;

View File

@ -194,7 +194,7 @@ public class TenantUserQuery extends QueryBase<TenantUserEntity> {
@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.match(TenantUser._tenant)) 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;

View File

@ -25,6 +25,6 @@ public interface TenantService {
Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException,
InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException;
Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException;
Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidApplicationException;
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
}

View File

@ -9,6 +9,10 @@ import eu.eudat.convention.ConventionService;
import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEvent;
import eu.eudat.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent;
import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEventHandler;
import eu.eudat.model.Tenant;
import eu.eudat.model.builder.TenantBuilder;
import eu.eudat.model.deleter.TenantDeleter;
@ -67,18 +71,20 @@ public class TenantServiceImpl implements TenantService {
private final EncryptionService encryptionService;
private final TenantProperties properties;
private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler;
private final TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler;
@Autowired
public TenantServiceImpl(
TenantEntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
MessageSource messageSource,
XmlHandlingService xmlHandlingService,
ErrorThesaurusProperties errors,
EncryptionService encryptionService, TenantProperties properties) {
TenantEntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
MessageSource messageSource,
XmlHandlingService xmlHandlingService,
ErrorThesaurusProperties errors,
EncryptionService encryptionService, TenantProperties properties, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -89,6 +95,8 @@ public class TenantServiceImpl implements TenantService {
this.errors = errors;
this.encryptionService = encryptionService;
this.properties = properties;
this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler;
this.tenantRemovalIntegrationEventHandler = tenantRemovalIntegrationEventHandler;
}
@Override
@ -124,6 +132,11 @@ public class TenantServiceImpl implements TenantService {
this.entityManager.flush();
TenantTouchedIntegrationEvent tenantTouchedIntegrationEvent = new TenantTouchedIntegrationEvent();
tenantTouchedIntegrationEvent.setId(data.getId());
tenantTouchedIntegrationEvent.setCode(data.getCode());
this.tenantTouchedIntegrationEventHandler.handle(tenantTouchedIntegrationEvent);
return this.builderFactory.builder(TenantBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Tenant._id), data);
}
@ -185,17 +198,25 @@ public class TenantServiceImpl implements TenantService {
}
@Override
public Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
public Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidApplicationException {
if (model.getConfig() != null && model.getConfig().getDeposit() != null && model.getConfig().getDeposit().getSources() != null) {
for (TenantSource source : model.getConfig().getDeposit().getSources().stream().collect(Collectors.toList())) {
for (TenantSource source : model.getConfig().getDeposit().getSources().stream().toList()) {
source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv()));
}
}
if (model.getConfig() != null && model.getConfig().getFileTransformers() != null && model.getConfig().getFileTransformers().getSources() != null) {
for (TenantSource source : model.getConfig().getFileTransformers().getSources().stream().collect(Collectors.toList())) {
for (TenantSource source : model.getConfig().getFileTransformers().getSources().stream().toList()) {
source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv()));
}
}
TenantEntity data = this.entityManager.find(TenantEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale()));
TenantTouchedIntegrationEvent tenantTouchedIntegrationEvent = new TenantTouchedIntegrationEvent();
tenantTouchedIntegrationEvent.setId(data.getId());
tenantTouchedIntegrationEvent.setCode(data.getCode());
this.tenantTouchedIntegrationEventHandler.handle(tenantTouchedIntegrationEvent);
return model;
}
@ -206,6 +227,10 @@ public class TenantServiceImpl implements TenantService {
this.authorizationService.authorizeForce(Permission.DeleteTenant);
this.deleterFactory.deleter(TenantDeleter.class).deleteAndSaveByIds(List.of(id));
TenantRemovalIntegrationEvent tenantRemovalIntegrationEvent = new TenantRemovalIntegrationEvent();
tenantRemovalIntegrationEvent.setId(id);
this.tenantRemovalIntegrationEventHandler.handle(tenantRemovalIntegrationEvent);
}
}

View File

@ -26,6 +26,7 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -101,7 +102,7 @@ public class SecurityConfiguration {
//In the example below, the default client handler will be ignored by the resolver
@Override
public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() {
return List.of(PermissionClientAuthorizationHandler.class);
return new ArrayList<>();
}
};
}

View File

@ -78,7 +78,7 @@ public class TenantController {
}
@PostMapping("query")
public QueryResult<Tenant> query(@RequestBody TenantLookup lookup) throws MyApplicationException, MyForbiddenException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
public QueryResult<Tenant> query(@RequestBody TenantLookup lookup) throws MyApplicationException, MyForbiddenException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidApplicationException {
logger.debug("querying {}", Tenant.class.getSimpleName());
this.censorFactory.censor(TenantCensor.class).censor(lookup.getProject(), null);
@ -97,7 +97,7 @@ public class TenantController {
}
@GetMapping("{id}")
public Tenant get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
public Tenant get(@PathVariable("id") UUID id, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidApplicationException {
logger.debug(new MapLogEntry("retrieving" + Tenant.class.getSimpleName()).And("id", id).And("fields", fieldSet));
this.censorFactory.censor(TenantCensor.class).censor(fieldSet, null);