Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Sofia Papacharalampous 2024-04-04 13:21:38 +03:00
commit 8cec0c3552
40 changed files with 335 additions and 276 deletions

View File

@ -1,9 +1,9 @@
package gr.cite.annotation.web; 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.TenantInterceptor;
import gr.cite.annotation.web.scope.tenant.TenantScopeClaimInterceptor; import gr.cite.annotation.web.scope.tenant.TenantScopeClaimInterceptor;
import gr.cite.annotation.web.scope.tenant.TenantScopeHeaderInterceptor; 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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 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.SecurityFilterChain;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; 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 //In the example below, the default client handler will be ignored by the resolver
@Override @Override
public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() { 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: Roles:
- type: resource_access - type: resource_access
path: dmp_web.roles 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: Scope:
- type: scope - type: scope
AccessToken: AccessToken:
- type: x-access-token - type: x-access-token
visibility: SENSITIVE visibility: SENSITIVE
Tenant:
- type: x-tenant
IssuedAt: IssuedAt:
- type: iat - type: iat
Issuer: Issuer:
@ -37,5 +51,8 @@ idpclient:
- type: azp - type: azp
Authorities: Authorities:
- type: authorities - type: authorities
ExternalProviderName: TenantCodes:
- type: identity_provider - type: tenant_roles
filterBy: "(.*):(.*)"
extractByExpression: "(.*):(.*)"
extractExpressionValue: "[[g2]]"

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package gr.cite.annotation.integrationevent; package gr.cite.annotation.integrationevent;
import gr.cite.annotation.data.QueueOutboxEntity;
import gr.cite.annotation.integrationevent.outbox.OutboxProperties; import gr.cite.annotation.integrationevent.outbox.OutboxProperties;
import gr.cite.annotation.integrationevent.outbox.OutboxRepositoryImpl; import gr.cite.annotation.integrationevent.outbox.OutboxRepositoryImpl;
import gr.cite.queueoutbox.IntegrationEventContextCreator; import gr.cite.queueoutbox.IntegrationEventContextCreator;
@ -56,7 +57,11 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
@Bean @Bean
public IntegrationEventContextCreator integrationEventContextCreator() { 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 @Bean

View File

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

View File

@ -245,6 +245,12 @@ public class InboxRepositoryImpl implements InboxRepository {
queueMessage.setId(UUID.randomUUID()); queueMessage.setId(UUID.randomUUID());
Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null; Object tenantId = inboxCreatorParams.getHeaders() != null ? inboxCreatorParams.getHeaders().getOrDefault(IntegrationEventMessageConstants.TENANT, null) : null;
if (tenantId instanceof UUID) queueMessage.setTenantId((UUID) tenantId); 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.setExchange(this.inboxProperties.getExchange());
queueMessage.setRoute(inboxCreatorParams.getRoutingKey()); queueMessage.setRoute(inboxCreatorParams.getRoutingKey());
queueMessage.setQueue(inboxCreatorParams.getQueueName()); 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.EntityUserQuery;
import gr.cite.annotation.query.TenantQuery; import gr.cite.annotation.query.TenantQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -83,7 +84,9 @@ public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements Ann
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); 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); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager(); 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.EntityUserQuery;
import gr.cite.annotation.query.TenantQuery; import gr.cite.annotation.query.TenantQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
@ -85,7 +86,9 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); 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); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager(); entityManager = entityManagerFactory.createEntityManager();
@ -114,6 +117,7 @@ public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements Ann
data.setId(UUID.randomUUID()); data.setId(UUID.randomUUID());
data.setEntityId(entityEvent.getEntityId()); data.setEntityId(entityEvent.getEntityId());
data.setUserId(user); data.setUserId(user);
data.setTenantId(properties.getTenantId());
data.setCreatedAt(Instant.now()); data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now()); data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active); 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.InboxPrincipal;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties; import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.service.tenant.TenantService; 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.auditing.AuditService;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
@ -53,7 +54,9 @@ public class TenantRemovalIntegrationEventHandlerImpl implements TenantRemovalIn
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); 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); TenantRemovalConsistencyHandler tenantRemovalConsistencyHandler = this.applicationContext.getBean(TenantRemovalConsistencyHandler.class);
if (!(tenantRemovalConsistencyHandler.isConsistent(new TenantRemovalConsistencyPredicates(event.getId())))) 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.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.model.persist.TenantTouchedIntegrationEventPersist; import gr.cite.annotation.model.persist.TenantTouchedIntegrationEventPersist;
import gr.cite.annotation.service.tenant.TenantService; 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.auditing.AuditService;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
@ -59,7 +61,8 @@ public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIn
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); 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); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager(); 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.model.Tenant;
import gr.cite.annotation.query.TenantQuery; import gr.cite.annotation.query.TenantQuery;
import gr.cite.annotation.service.user.UserService; 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.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.exception.MyValidationException;
@ -89,7 +90,9 @@ public class UserRemovalIntegrationEventHandlerImpl implements UserRemovalIntegr
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); 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); UserRemovalConsistencyHandler userRemovalConsistencyHandler = this.applicationContext.getBean(UserRemovalConsistencyHandler.class);
if (!(userRemovalConsistencyHandler.isConsistent(new UserRemovalConsistencyPredicates(event.getUserId())))) 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.model.Tenant;
import gr.cite.annotation.query.TenantQuery; import gr.cite.annotation.query.TenantQuery;
import gr.cite.annotation.service.user.UserService; 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.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
@ -82,7 +83,9 @@ public class UserTouchedIntegrationEventHandlerImpl implements UserTouchedIntegr
} }
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class); 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); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager(); entityManager = entityManagerFactory.createEntityManager();

View File

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

View File

@ -5,15 +5,19 @@ import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission; import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.JsonHandlingService; import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.*; import gr.cite.annotation.data.*;
import gr.cite.annotation.integrationevent.inbox.usertouch.UserTouchedIntegrationEvent; 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.User;
import gr.cite.annotation.model.builder.UserBuilder; import gr.cite.annotation.model.builder.UserBuilder;
import gr.cite.annotation.model.deleter.TenantUserDeleter; import gr.cite.annotation.model.deleter.TenantUserDeleter;
import gr.cite.annotation.model.deleter.UserContactInfoDeleter; import gr.cite.annotation.model.deleter.UserContactInfoDeleter;
import gr.cite.annotation.model.deleter.UserCredentialDeleter; import gr.cite.annotation.model.deleter.UserCredentialDeleter;
import gr.cite.annotation.model.deleter.UserDeleter; import gr.cite.annotation.model.deleter.UserDeleter;
import gr.cite.annotation.query.TenantQuery;
import gr.cite.annotation.query.TenantUserQuery; import gr.cite.annotation.query.TenantUserQuery;
import gr.cite.annotation.query.UserContactInfoQuery; import gr.cite.annotation.query.UserContactInfoQuery;
import gr.cite.annotation.query.UserCredentialQuery; import gr.cite.annotation.query.UserCredentialQuery;
@ -31,6 +35,8 @@ import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
@ -57,6 +63,9 @@ public class UserServiceImpl implements UserService {
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final TenantScope tenantScope;
private final MessageSource messageSource;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
@ -65,7 +74,7 @@ public class UserServiceImpl implements UserService {
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
ConventionService conventionService, ConventionService conventionService,
TenantEntityManager entityManager, TenantEntityManager entityManager,
BuilderFactory builderFactory, QueryFactory queryFactory, BuilderFactory builderFactory, QueryFactory queryFactory, TenantScope tenantScope, MessageSource messageSource,
JsonHandlingService jsonHandlingService) { JsonHandlingService jsonHandlingService) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -73,6 +82,8 @@ public class UserServiceImpl implements UserService {
this.entityManager = entityManager; this.entityManager = entityManager;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.tenantScope = tenantScope;
this.messageSource = messageSource;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
} }
@ -197,11 +208,20 @@ public class UserServiceImpl implements UserService {
.userIds(userId) .userIds(userId)
.isActive(IsActive.Active) .isActive(IsActive.Active)
.collect(); .collect();
List<UUID> updatedCreatedIds = new ArrayList<>(); List<UUID> updatedCreatedIds = new ArrayList<>();
if (models != null) { 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) { for (UserTouchedIntegrationEvent.TenantUser model : models) {
TenantUserEntity data = items.stream().filter(x -> x.getTenantId().equals(model.getTenant())).findFirst().orElse(null); TenantUserEntity data = items.stream().filter(x -> x.getTenantId().equals(model.getTenant())).findFirst().orElse(null);
if (data == null) { if (data == null) {
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 = new TenantUserEntity();
data.setId(UUID.randomUUID()); data.setId(UUID.randomUUID());
data.setUserId(userId); data.setUserId(userId);
@ -210,6 +230,9 @@ public class UserServiceImpl implements UserService {
data.setUpdatedAt(Instant.now()); data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active); data.setIsActive(IsActive.Active);
entityManager.persist(data); entityManager.persist(data);
} finally {
this.tenantScope.removeTempTenant(this.entityManager.getEntityManager());
}
} }
updatedCreatedIds.add(data.getId()); updatedCreatedIds.add(data.getId());
} }

View File

@ -145,6 +145,8 @@ public class AuditableAction {
public static final EventId Maintenance_GenerateElastic = new EventId(220000, "Maintenance_GenerateElastic"); 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_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_Lookup = new EventId(240000, "Principal_Lookup");
public static final EventId Principal_MyTenants = new EventId(240001, "Principal_MyTenants"); public static final EventId Principal_MyTenants = new EventId(240001, "Principal_MyTenants");

View File

@ -22,6 +22,8 @@ public final class Permission {
public static String PublicBrowseReferenceType = "PublicBrowseReferenceType"; public static String PublicBrowseReferenceType = "PublicBrowseReferenceType";
//Elastic //Elastic
public static String ManageElastic = "ManageElastic"; public static String ManageElastic = "ManageElastic";
//Queue Events
public static String ManageQueueEvents = "ManageQueueEvents";
//Deposit //Deposit

View File

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

View File

@ -1,6 +1,7 @@
package eu.eudat.integrationevent; package eu.eudat.integrationevent;
import eu.eudat.data.QueueOutboxEntity;
import eu.eudat.integrationevent.outbox.OutboxProperties; import eu.eudat.integrationevent.outbox.OutboxProperties;
import eu.eudat.integrationevent.outbox.OutboxRepositoryImpl; import eu.eudat.integrationevent.outbox.OutboxRepositoryImpl;
import gr.cite.queueoutbox.IntegrationEventContextCreator; import gr.cite.queueoutbox.IntegrationEventContextCreator;
@ -57,7 +58,11 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
@Bean @Bean
public IntegrationEventContextCreator integrationEventContextCreator() { 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 @Bean

View File

@ -1,6 +1,7 @@
package eu.eudat.integrationevent.inbox; package eu.eudat.integrationevent.inbox;
import gr.cite.commons.web.oidc.principal.MyPrincipal; 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.core.ClaimAccessor;
import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.security.oauth2.jwt.JwtClaimNames;
@ -11,8 +12,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class InboxPrincipal implements MyPrincipal, ClaimAccessor { public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
private Map<String, Object> claims; private final Map<String, Object> claims;
private boolean isAuthenticated;
private final boolean isAuthenticated;
public InboxPrincipal(Boolean isAuthenticated, String name) { public InboxPrincipal(Boolean isAuthenticated, String name) {
this.claims = new HashMap<>(); this.claims = new HashMap<>();
@ -20,6 +22,18 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
this.isAuthenticated = isAuthenticated; 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 @Override
public Boolean isAuthenticated() { public Boolean isAuthenticated() {
return this.isAuthenticated; return this.isAuthenticated;
@ -32,8 +46,12 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
@Override @Override
public List<String> getClaimAsStringList(String claim) { public List<String> getClaimAsStringList(String claim) {
if (claims == null) return null; if (claims == null)
return this.getClaimAsStringList(claim); return null;
if (this.claims.containsKey(claim)){
return List.of(this.claims.get(claim).toString());
}
return null;
} }
@Override @Override
@ -44,13 +62,4 @@ public class InboxPrincipal implements MyPrincipal, ClaimAccessor {
public void put(String key, Object value) { public void put(String key, Object value) {
this.claims.put(key, 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; package eu.eudat.integrationevent.outbox.tenanttouched;
import javax.management.InvalidApplicationException;
public interface TenantTouchedIntegrationEventHandler { public interface TenantTouchedIntegrationEventHandler {
void handle(TenantTouchedIntegrationEvent event); void handle(TenantTouchedIntegrationEvent event);

View File

@ -1,11 +1,17 @@
package eu.eudat.integrationevent.outbox.tenanttouched; package eu.eudat.integrationevent.outbox.tenanttouched;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent; import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; 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; import java.util.UUID;
@Component("outboxtenanttouchedintegrationeventhandler")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler { public class TenantTouchedIntegrationEventHandlerImpl implements TenantTouchedIntegrationEventHandler {
private final OutboxService outboxService; private final OutboxService outboxService;
public TenantTouchedIntegrationEventHandlerImpl(OutboxService outboxService) { public TenantTouchedIntegrationEventHandlerImpl(OutboxService outboxService) {

View File

@ -194,7 +194,7 @@ public class TenantUserQuery extends QueryBase<TenantUserEntity> {
@Override @Override
protected String fieldNameOf(FieldResolver item) { protected String fieldNameOf(FieldResolver item) {
if (item.match(TenantUser._id)) return TenantUserEntity._id; 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.prefix(TenantUser._tenant)) return TenantUserEntity._tenantId;
else if (item.match(TenantUser._isActive)) return TenantUserEntity._isActive; else if (item.match(TenantUser._isActive)) return TenantUserEntity._isActive;
else if (item.match(TenantUser._createdAt)) return TenantUserEntity._createdAt; 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, Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException,
InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException; 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; 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.TenantEntity;
import eu.eudat.data.TenantEntityManager; import eu.eudat.data.TenantEntityManager;
import eu.eudat.errorcode.ErrorThesaurusProperties; 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.Tenant;
import eu.eudat.model.builder.TenantBuilder; import eu.eudat.model.builder.TenantBuilder;
import eu.eudat.model.deleter.TenantDeleter; import eu.eudat.model.deleter.TenantDeleter;
@ -67,6 +71,8 @@ public class TenantServiceImpl implements TenantService {
private final EncryptionService encryptionService; private final EncryptionService encryptionService;
private final TenantProperties properties; private final TenantProperties properties;
private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler;
private final TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler;
@Autowired @Autowired
public TenantServiceImpl( public TenantServiceImpl(
@ -78,7 +84,7 @@ public class TenantServiceImpl implements TenantService {
MessageSource messageSource, MessageSource messageSource,
XmlHandlingService xmlHandlingService, XmlHandlingService xmlHandlingService,
ErrorThesaurusProperties errors, ErrorThesaurusProperties errors,
EncryptionService encryptionService, TenantProperties properties) { EncryptionService encryptionService, TenantProperties properties, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -89,6 +95,8 @@ public class TenantServiceImpl implements TenantService {
this.errors = errors; this.errors = errors;
this.encryptionService = encryptionService; this.encryptionService = encryptionService;
this.properties = properties; this.properties = properties;
this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler;
this.tenantRemovalIntegrationEventHandler = tenantRemovalIntegrationEventHandler;
} }
@Override @Override
@ -124,6 +132,11 @@ public class TenantServiceImpl implements TenantService {
this.entityManager.flush(); 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); 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 @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) { 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())); source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv()));
} }
} }
if (model.getConfig() != null && model.getConfig().getFileTransformers() != null && model.getConfig().getFileTransformers().getSources() != null) { 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())); 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; return model;
} }
@ -206,6 +227,10 @@ public class TenantServiceImpl implements TenantService {
this.authorizationService.authorizeForce(Permission.DeleteTenant); this.authorizationService.authorizeForce(Permission.DeleteTenant);
this.deleterFactory.deleter(TenantDeleter.class).deleteAndSaveByIds(List.of(id)); 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.Filter;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; 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 //In the example below, the default client handler will be ignored by the resolver
@Override @Override
public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() { public List<Class<? extends AuthorizationHandler<? extends AuthorizationRequirement>>> disableHandlers() {
return List.of(PermissionClientAuthorizationHandler.class); return new ArrayList<>();
} }
}; };
} }

View File

@ -2,33 +2,68 @@ package eu.eudat.controllers;
import eu.eudat.audit.AuditableAction; import eu.eudat.audit.AuditableAction;
import eu.eudat.authorization.Permission; 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 eu.eudat.service.elastic.ElasticService;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import jakarta.persistence.EntityManager;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.management.InvalidApplicationException;
import java.util.List;
@RestController @RestController
@RequestMapping(path = "api/maintenance") @RequestMapping(path = "api/maintenance")
public class MaintenanceController { public class MaintenanceController {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(MaintenanceController.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(MaintenanceController.class));
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
private final ElasticService elasticService; private final ElasticService elasticService;
private final AuditService auditService; private final AuditService auditService;
private final QueryFactory queryFactory;
private final TenantEntityManager entityManager;
private final UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler;
private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler;
@Autowired @Autowired
public MaintenanceController(AuthorizationService authorizationService, ElasticService elasticService, AuditService auditService) { public MaintenanceController(
AuthorizationService authorizationService,
ElasticService elasticService,
AuditService auditService,
QueryFactory queryFactory,
TenantEntityManager entityManager,
UserTouchedIntegrationEventHandler userTouchedIntegrationEventHandler,
TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.elasticService = elasticService; this.elasticService = elasticService;
this.auditService = auditService; this.auditService = auditService;
this.queryFactory = queryFactory;
this.entityManager = entityManager;
this.userTouchedIntegrationEventHandler = userTouchedIntegrationEventHandler;
this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler;
} }
@RequestMapping(method = RequestMethod.POST, value = {"/index/elastic"}) @RequestMapping(method = RequestMethod.POST, value = {"/index/elastic"})
public void generateIndex() throws Exception { public void generateIndex() throws Exception {
logger.debug("generate elastic "); logger.debug("generate elastic ");
@ -36,6 +71,7 @@ public class MaintenanceController {
elasticService.resetDmpIndex(); elasticService.resetDmpIndex();
elasticService.resetDescriptionIndex(); elasticService.resetDescriptionIndex();
this.auditService.track(AuditableAction.Maintenance_GenerateElastic); this.auditService.track(AuditableAction.Maintenance_GenerateElastic);
} }
@ -46,6 +82,43 @@ public class MaintenanceController {
elasticService.deleteDescriptionIndex(); elasticService.deleteDescriptionIndex();
elasticService.deleteDmpIndex(); elasticService.deleteDmpIndex();
this.auditService.track(AuditableAction.Maintenance_ClearElastic); 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<UserEntity> 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<TenantEntity> 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);
}
} }

View File

@ -78,7 +78,7 @@ public class TenantController {
} }
@PostMapping("query") @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()); logger.debug("querying {}", Tenant.class.getSimpleName());
this.censorFactory.censor(TenantCensor.class).censor(lookup.getProject(), null); this.censorFactory.censor(TenantCensor.class).censor(lookup.getProject(), null);
@ -97,7 +97,7 @@ public class TenantController {
} }
@GetMapping("{id}") @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)); logger.debug(new MapLogEntry("retrieving" + Tenant.class.getSimpleName()).And("id", id).And("fields", fieldSet));
this.censorFactory.censor(TenantCensor.class).censor(fieldSet, null); this.censorFactory.censor(TenantCensor.class).censor(fieldSet, null);

View File

@ -89,6 +89,14 @@ permissions:
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
# Queue Events
ManageQueueEvents:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Deposit # Deposit
BrowseDeposit: BrowseDeposit:
roles: roles:

View File

@ -11,10 +11,15 @@ CREATE TABLE IF NOT EXISTS public."ant_Annotation"
"entity_type" character varying(512) COLLATE pg_catalog."default" NOT NULL, "entity_type" character varying(512) COLLATE pg_catalog."default" NOT NULL,
"anchor" character varying(512) COLLATE pg_catalog."default", "anchor" character varying(512) COLLATE pg_catalog."default",
"payload" text COLLATE pg_catalog."default", "payload" text COLLATE pg_catalog."default",
"tenant" uuid NOT NULL,
"created_at" timestamp without time zone NOT NULL, "created_at" timestamp without time zone NOT NULL,
"updated_at" timestamp without time zone NOT NULL, "updated_at" timestamp without time zone NOT NULL,
"is_active" smallint 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.'); INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.055', '2024-02-20 12:00:00.000000+02', now(), 'Add table ant_Annotation.');

View File

@ -9,10 +9,15 @@ CREATE TABLE IF NOT EXISTS public."ant_EntityUser"
"id" uuid NOT NULL, "id" uuid NOT NULL,
"entity_id" uuid NOT NULL, "entity_id" uuid NOT NULL,
"user_id" uuid NOT NULL, "user_id" uuid NOT NULL,
"tenant" uuid NOT NULL,
"created_at" timestamp without time zone NOT NULL, "created_at" timestamp without time zone NOT NULL,
"updated_at" timestamp without time zone NOT NULL, "updated_at" timestamp without time zone NOT NULL,
"is_active" smallint NOT NULL, "is_active" smallint NOT NULL,
CONSTRAINT "ant_EntityUser_pkey" PRIMARY KEY (id), 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") CONSTRAINT "ant_EntityUser_user_fkey" FOREIGN KEY ("user_id")
REFERENCES public."ant_User" (id) MATCH SIMPLE REFERENCES public."ant_User" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON UPDATE NO ACTION

View File

@ -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
}

View File

@ -188,6 +188,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
const action = this.route.snapshot.data['action']; const action = this.route.snapshot.data['action'];
if (action && action == 'new-version') { if (action && action == 'new-version') {
this.formGroup.enable(); this.formGroup.enable();
this.formGroup.get('status').setValue(DescriptionTemplateStatus.Draft);
} }
} }
@ -283,7 +284,6 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) { if (result) {
this.formGroup.get('status').setValue(DescriptionTemplateStatus.Finalized); this.formGroup.get('status').setValue(DescriptionTemplateStatus.Finalized);
if(this.isNewVersion) this.isNewVersion = false;
this.persistEntity(); this.persistEntity();
}}); }});
} }

View File

@ -24,6 +24,7 @@ import { isNullOrUndefined } from '@swimlane/ngx-datatable';
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component';
import { AuthService } from '@app/core/services/auth/auth.service'; import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
@Component({ @Component({
selector: 'app-base-editor-component', selector: 'app-base-editor-component',
@ -120,9 +121,13 @@ export abstract class BaseEditor<EditorModelType extends BaseEditorModel, Entity
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse); const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
if (error.statusCode === 400) { if (error.statusCode === 400) {
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error); this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
if(errorResponse.error.code === 120){ if(errorResponse.error.code === ResponseErrorCode.DmpBlueprintHasNoDescriptionTemplates){
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error); this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
} }
if(errorResponse.error.code === ResponseErrorCode.DmpDescriptionTemplateCanNotRemove){
this.uiNotificationService.snackBarNotification(errorResponse.error.error, SnackBarNotificationLevel.Error);
this.refreshOnNavigateToData(null);
}
this.formService.validateAllFormFields(this.formGroup); this.formService.validateAllFormFields(this.formGroup);
} else { } else {
this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning); this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning);

View File

@ -7,6 +7,7 @@ export interface BaseEntity {
createdAt?: Date; createdAt?: Date;
updatedAt?: Date; updatedAt?: Date;
hash?: string; hash?: string;
belongsToCurrentTenant?: boolean;
} }
export interface BaseEntityPersist { export interface BaseEntityPersist {