diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/WebConfiguration.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/WebConfiguration.java index 775e62087..775930c70 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/WebConfiguration.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/WebConfiguration.java @@ -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; diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/config/SecurityConfiguration.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/config/SecurityConfiguration.java index 761b144ca..a52e25875 100644 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/config/SecurityConfiguration.java +++ b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/config/SecurityConfiguration.java @@ -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>> disableHandlers() { - return List.of(PermissionClientAuthorizationHandler.class); + return new ArrayList<>(); } }; } diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/error/GenericErrorHandler.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/error/GenericErrorHandler.java deleted file mode 100644 index 1a7ebfc3a..000000000 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/controllers/error/GenericErrorHandler.java +++ /dev/null @@ -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 handleValidationException(MyValidationException e, WebRequest webRequest) { -// return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(e); -// } -//} diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptor.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptor.java deleted file mode 100644 index 33b21485a..000000000 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptor.java +++ /dev/null @@ -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) { - } -} - diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptorCacheOptions.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptorCacheOptions.java deleted file mode 100644 index 30c08781e..000000000 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptorCacheOptions.java +++ /dev/null @@ -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 { -} diff --git a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptorCacheService.java b/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptorCacheService.java deleted file mode 100644 index af5012905..000000000 --- a/annotation-service/annotation-web/src/main/java/gr/cite/annotation/web/interceptors/UserInterceptorCacheService.java +++ /dev/null @@ -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 { - - 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 valueClass() { - return UserInterceptorCacheValue.class; - } - - @Override - public String keyOf(UserInterceptorCacheValue value) { - return this.buildKey(value.getSubjectId()); - } - - - public String buildKey(String subject) { - HashMap keyParts = new HashMap<>(); - keyParts.put("$subject$", subject); - return this.generateKey(keyParts); - } -} diff --git a/annotation-service/annotation-web/src/main/resources/config/idpclaims.yml b/annotation-service/annotation-web/src/main/resources/config/idpclaims.yml index 97ff4a10e..26e00f2a1 100644 --- a/annotation-service/annotation-web/src/main/resources/config/idpclaims.yml +++ b/annotation-service/annotation-web/src/main/resources/config/idpclaims.yml @@ -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 \ No newline at end of file + TenantCodes: + - type: tenant_roles + filterBy: "(.*):(.*)" + extractByExpression: "(.*):(.*)" + extractExpressionValue: "[[g2]]" \ No newline at end of file diff --git a/annotation-service/annotation-web/src/main/resources/config/permissions.yml b/annotation-service/annotation-web/src/main/resources/config/permissions.yml index 89c8bb4c4..e9afba04c 100644 --- a/annotation-service/annotation-web/src/main/resources/config/permissions.yml +++ b/annotation-service/annotation-web/src/main/resources/config/permissions.yml @@ -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 \ No newline at end of file diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java index 6ff264787..31f73207c 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/data/TenantEntityManager.java @@ -82,6 +82,7 @@ public class TenantEntityManager { } public void enableTenantFilters() throws InvalidApplicationException { + if (!tenantScope.isSet()) return; if(!tenantScope.isDefaultTenant()) { this.entityManager .unwrap(Session.class) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/OutboxIntegrationEventConfigurer.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/OutboxIntegrationEventConfigurer.java index 8a5eca059..52602ad8f 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/OutboxIntegrationEventConfigurer.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/OutboxIntegrationEventConfigurer.java @@ -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 diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxPrincipal.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxPrincipal.java index c94019a04..fa10849f8 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxPrincipal.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxPrincipal.java @@ -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 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 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 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 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 diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java index ccfda4f8c..2a06b0eb6 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/InboxRepositoryImpl.java @@ -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()); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java index 612dc8098..2629f5134 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiesremoval/AnnotationEntitiesRemovalIntegrationEventHandlerImpl.java @@ -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(); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java index 191942bef..c96b4273c 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/annotationentitiestouch/AnnotationEntitiesTouchedIntegrationEventHandlerImpl.java @@ -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); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java index 3b7921a37..86a264349 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenantremoval/TenantRemovalIntegrationEventHandlerImpl.java @@ -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())))) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java index 4c03dcdd0..373758c7c 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/tenanttouch/TenantTouchedIntegrationEventHandlerImpl.java @@ -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(); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java index cef55829a..08527936d 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/userremoval/UserRemovalIntegrationEventHandlerImpl.java @@ -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())))) diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java index 0bb00b430..0962369d0 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/integrationevent/inbox/usertouch/UserTouchedIntegrationEventHandlerImpl.java @@ -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(); diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java index b9f1a37d3..8ee773d2c 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/tenant/TenantServiceImpl.java @@ -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 { diff --git a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java index 684e7ba7e..b6bdb0a61 100644 --- a/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java +++ b/annotation-service/annotation/src/main/java/gr/cite/annotation/service/user/UserServiceImpl.java @@ -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 updatedCreatedIds = new ArrayList<>(); if (models != null) { + List 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()); } diff --git a/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java b/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java index c614f310d..39e172336 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java +++ b/dmp-backend/core/src/main/java/eu/eudat/audit/AuditableAction.java @@ -145,6 +145,8 @@ public class AuditableAction { public static final EventId Maintenance_GenerateElastic = new EventId(220000, "Maintenance_GenerateElastic"); public static final EventId Maintenance_ClearElastic = new EventId(230000, "Maintenance_ClearElastic"); + public static final EventId Maintenance_SendUserTouchEvents = new EventId(230001, "Maintenance_SendUserTouchEvents"); + public static final EventId Maintenance_SendTenantTouchEvents = new EventId(230002, "Maintenance_SendTenantTouchEvents"); public static final EventId Principal_Lookup = new EventId(240000, "Principal_Lookup"); public static final EventId Principal_MyTenants = new EventId(240001, "Principal_MyTenants"); diff --git a/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java b/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java index f031e632a..b7de28665 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java +++ b/dmp-backend/core/src/main/java/eu/eudat/authorization/Permission.java @@ -22,6 +22,8 @@ public final class Permission { public static String PublicBrowseReferenceType = "PublicBrowseReferenceType"; //Elastic public static String ManageElastic = "ManageElastic"; + //Queue Events + public static String ManageQueueEvents = "ManageQueueEvents"; //Deposit diff --git a/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java b/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java index cd9435b72..c8897881d 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java +++ b/dmp-backend/core/src/main/java/eu/eudat/data/TenantEntityManager.java @@ -87,6 +87,7 @@ public class TenantEntityManager { } public void enableTenantFilters() throws InvalidApplicationException { + if (!tenantScope.isSet()) return; if(!tenantScope.isDefaultTenant()) { this.entityManager .unwrap(Session.class) diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/OutboxIntegrationEventConfigurer.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/OutboxIntegrationEventConfigurer.java index db9278357..42a365559 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/OutboxIntegrationEventConfigurer.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/OutboxIntegrationEventConfigurer.java @@ -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 diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxPrincipal.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxPrincipal.java index 825fe6d64..3a590e3f1 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxPrincipal.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/inbox/InboxPrincipal.java @@ -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 claims; - private boolean isAuthenticated; + private final Map 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 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 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 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 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; - } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java index 4ef529ac2..579b2e9c7 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandler.java @@ -1,7 +1,5 @@ package eu.eudat.integrationevent.outbox.tenanttouched; -import javax.management.InvalidApplicationException; - public interface TenantTouchedIntegrationEventHandler { void handle(TenantTouchedIntegrationEvent event); diff --git a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java index 06cd22296..8d788478c 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/integrationevent/outbox/tenanttouched/TenantTouchedIntegrationEventHandlerImpl.java @@ -1,18 +1,24 @@ 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("outboxtenanttouchedintegrationeventhandler") +@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler { + private final OutboxService outboxService; - public TenantTouchedIntegrationEventHandlerImpl(OutboxService outboxService) { - this.outboxService = outboxService; - } + public TenantTouchedIntegrationEventHandlerImpl(OutboxService outboxService) { + this.outboxService = outboxService; + } - @Override + @Override public void handle(TenantTouchedIntegrationEvent event) { OutboxIntegrationEvent message = new OutboxIntegrationEvent(); message.setMessageId(UUID.randomUUID()); diff --git a/dmp-backend/core/src/main/java/eu/eudat/query/TenantUserQuery.java b/dmp-backend/core/src/main/java/eu/eudat/query/TenantUserQuery.java index c0ce98d05..76692c909 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/query/TenantUserQuery.java +++ b/dmp-backend/core/src/main/java/eu/eudat/query/TenantUserQuery.java @@ -194,7 +194,7 @@ public class TenantUserQuery extends QueryBase { @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; diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java index 2aa543af0..311b91c15 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantService.java @@ -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; } diff --git a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java index 9ae298789..63428733a 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java +++ b/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java @@ -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); } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/configurations/SecurityConfiguration.java b/dmp-backend/web/src/main/java/eu/eudat/configurations/SecurityConfiguration.java index 4cb016591..51c8c03a5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/configurations/SecurityConfiguration.java +++ b/dmp-backend/web/src/main/java/eu/eudat/configurations/SecurityConfiguration.java @@ -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>> disableHandlers() { - return List.of(PermissionClientAuthorizationHandler.class); + return new ArrayList<>(); } }; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/MaintenanceController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/MaintenanceController.java index 768633f43..049cbb992 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/MaintenanceController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/MaintenanceController.java @@ -2,32 +2,67 @@ package eu.eudat.controllers; import eu.eudat.audit.AuditableAction; import eu.eudat.authorization.Permission; +import eu.eudat.commons.enums.IsActive; +import eu.eudat.data.TenantEntity; +import eu.eudat.data.TenantEntityManager; +import eu.eudat.data.UserEntity; +import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent; +import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEventHandler; +import eu.eudat.integrationevent.outbox.usertouched.UserTouchedIntegrationEventHandler; +import eu.eudat.query.TenantQuery; +import eu.eudat.query.UserQuery; import eu.eudat.service.elastic.ElasticService; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.logging.LoggerService; +import jakarta.persistence.EntityManager; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import javax.management.InvalidApplicationException; +import java.util.List; + @RestController @RequestMapping(path = "api/maintenance") public class MaintenanceController { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(MaintenanceController.class)); + private final AuthorizationService authorizationService; + private final ElasticService elasticService; + private final AuditService auditService; - @Autowired - public MaintenanceController(AuthorizationService authorizationService, ElasticService elasticService, AuditService auditService) { - this.authorizationService = authorizationService; - this.elasticService = elasticService; - this.auditService = auditService; - } + private final QueryFactory queryFactory; + private final TenantEntityManager entityManager; + + private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler; + + private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler; + + @Autowired + public MaintenanceController( + AuthorizationService authorizationService, + ElasticService elasticService, + AuditService auditService, + QueryFactory queryFactory, + TenantEntityManager entityManager, + UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler, + TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler) { + this.authorizationService = authorizationService; + this.elasticService = elasticService; + this.auditService = auditService; + this.queryFactory = queryFactory; + this.entityManager = entityManager; + this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler; + this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler; + } @RequestMapping(method = RequestMethod.POST, value = {"/index/elastic"}) public void generateIndex() throws Exception { @@ -36,6 +71,7 @@ public class MaintenanceController { elasticService.resetDmpIndex(); elasticService.resetDescriptionIndex(); + this.auditService.track(AuditableAction.Maintenance_GenerateElastic); } @@ -43,9 +79,46 @@ public class MaintenanceController { public void clearIndex() throws Exception { logger.debug("clear elastic"); this.authorizationService.authorizeForce(Permission.ManageElastic); - + elasticService.deleteDescriptionIndex(); elasticService.deleteDmpIndex(); + this.auditService.track(AuditableAction.Maintenance_ClearElastic); } + + @RequestMapping(method = RequestMethod.DELETE, value = {"/events/users/touch"}) + public void sendUserTouchEvents() throws InvalidApplicationException { + logger.debug("send user touch queue events"); + this.authorizationService.authorizeForce(Permission.ManageQueueEvents); + + this.entityManager.disableTenantFilters(); + UserQuery userQuery = queryFactory.query(UserQuery.class).isActive(IsActive.Active); + List users = userQuery.collect(); + this.entityManager.enableTenantFilters(); + + for(UserEntity user : users) + this.userTouchedIntegrationEventHandler.handle(user.getId()); + + this.auditService.track(AuditableAction.Maintenance_SendUserTouchEvents); + } + + @RequestMapping(method = RequestMethod.DELETE, value = {"/events/tenants/touch"}) + public void sendTenantTouchEvents() throws InvalidApplicationException { + logger.debug("send tenant touch queue events"); + this.authorizationService.authorizeForce(Permission.ManageQueueEvents); + + this.entityManager.disableTenantFilters(); + TenantQuery tenantQuery = queryFactory.query(TenantQuery.class).isActive(IsActive.Active); + List tenants = tenantQuery.collect(); + this.entityManager.enableTenantFilters(); + + for (TenantEntity tenant : tenants) { + TenantTouchedIntegrationEvent event = new TenantTouchedIntegrationEvent(); + event.setId(tenant.getId()); + event.setCode(tenant.getCode()); + this.tenantTouchedIntegrationEventHandler.handle(event); + } + + this.auditService.track(AuditableAction.Maintenance_SendTenantTouchEvents); + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/TenantController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/TenantController.java index 4eafcaf46..feef64391 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/TenantController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/TenantController.java @@ -78,7 +78,7 @@ public class TenantController { } @PostMapping("query") - public QueryResult query(@RequestBody TenantLookup lookup) throws MyApplicationException, MyForbiddenException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + public QueryResult 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); diff --git a/dmp-backend/web/src/main/resources/config/permissions.yml b/dmp-backend/web/src/main/resources/config/permissions.yml index c7369a0ac..c9253be3e 100644 --- a/dmp-backend/web/src/main/resources/config/permissions.yml +++ b/dmp-backend/web/src/main/resources/config/permissions.yml @@ -88,6 +88,14 @@ permissions: clients: [ ] allowAnonymous: false allowAuthenticated: false + + # Queue Events + ManageQueueEvents: + roles: + - Admin + clients: [ ] + allowAnonymous: false + allowAuthenticated: false # Deposit BrowseDeposit: diff --git a/dmp-db-scema/updates/00.01.055_Add_ant_Annotation_table.sql b/dmp-db-scema/updates/00.01.055_Add_ant_Annotation_table.sql index 75ce5e407..fd8b68ece 100644 --- a/dmp-db-scema/updates/00.01.055_Add_ant_Annotation_table.sql +++ b/dmp-db-scema/updates/00.01.055_Add_ant_Annotation_table.sql @@ -11,10 +11,15 @@ CREATE TABLE IF NOT EXISTS public."ant_Annotation" "entity_type" character varying(512) COLLATE pg_catalog."default" NOT NULL, "anchor" character varying(512) COLLATE pg_catalog."default", "payload" text COLLATE pg_catalog."default", + "tenant" uuid NOT NULL, "created_at" timestamp without time zone NOT NULL, "updated_at" timestamp without time zone NOT NULL, "is_active" smallint NOT NULL, - CONSTRAINT "ant_Annotation_pkey" PRIMARY KEY (id) + CONSTRAINT "ant_Annotation_pkey" PRIMARY KEY (id), + CONSTRAINT "ant_Annotation_tenant_fkey" FOREIGN KEY ("tenant") + REFERENCES public."ant_Tenant" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION ); INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.055', '2024-02-20 12:00:00.000000+02', now(), 'Add table ant_Annotation.'); diff --git a/dmp-db-scema/updates/00.01.056_Add_ant_EntityUser_table.sql b/dmp-db-scema/updates/00.01.056_Add_ant_EntityUser_table.sql index 708d3d646..4845b1b2c 100644 --- a/dmp-db-scema/updates/00.01.056_Add_ant_EntityUser_table.sql +++ b/dmp-db-scema/updates/00.01.056_Add_ant_EntityUser_table.sql @@ -9,10 +9,15 @@ CREATE TABLE IF NOT EXISTS public."ant_EntityUser" "id" uuid NOT NULL, "entity_id" uuid NOT NULL, "user_id" uuid NOT NULL, + "tenant" uuid NOT NULL, "created_at" timestamp without time zone NOT NULL, "updated_at" timestamp without time zone NOT NULL, "is_active" smallint NOT NULL, CONSTRAINT "ant_EntityUser_pkey" PRIMARY KEY (id), + CONSTRAINT "ant_EntityUser_tenant_fkey" FOREIGN KEY ("tenant") + REFERENCES public."ant_Tenant" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION, CONSTRAINT "ant_EntityUser_user_fkey" FOREIGN KEY ("user_id") REFERENCES public."ant_User" (id) MATCH SIMPLE ON UPDATE NO ACTION diff --git a/dmp-frontend/src/app/core/common/enum/respone-error-code.ts b/dmp-frontend/src/app/core/common/enum/respone-error-code.ts new file mode 100644 index 000000000..8fbd45b17 --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/respone-error-code.ts @@ -0,0 +1,17 @@ +export enum ResponseErrorCode { + HashConflict = 100, + Forbidden = 101, + SystemError =102, + MissingTenant = 103, + TenantCodeRequired = 108, + TenantNameRequired = 109, + TenantNotAllowed = 113, + DescriptionTemplateNewVersionConflict = 114, + DmpNewVersionConflict = 115, + DmpIsFinalized = 116, + + DmpBlueprintHasNoDescriptionTemplates = 120, + DmpBlueprintNewVersionConflict = 121, + DmpDescriptionTemplateCanNotRemove = 122, + TenantTampering = 123 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/description-template/editor/description-template-editor.component.ts b/dmp-frontend/src/app/ui/admin/description-template/editor/description-template-editor.component.ts index 92f2e188c..a84bf1317 100644 --- a/dmp-frontend/src/app/ui/admin/description-template/editor/description-template-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/description-template/editor/description-template-editor.component.ts @@ -188,6 +188,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor { if (result) { this.formGroup.get('status').setValue(DescriptionTemplateStatus.Finalized); - if(this.isNewVersion) this.isNewVersion = false; this.persistEntity(); }}); } diff --git a/dmp-frontend/src/common/base/base-editor.ts b/dmp-frontend/src/common/base/base-editor.ts index f471e0146..2111040d2 100644 --- a/dmp-frontend/src/common/base/base-editor.ts +++ b/dmp-frontend/src/common/base/base-editor.ts @@ -24,6 +24,7 @@ import { isNullOrUndefined } from '@swimlane/ngx-datatable'; import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; import { AuthService } from '@app/core/services/auth/auth.service'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code'; @Component({ selector: 'app-base-editor-component', @@ -120,9 +121,13 @@ export abstract class BaseEditor