annotation authz changes

This commit is contained in:
Efstratios Giannopoulos 2024-03-29 11:47:27 +02:00
parent 7d42777aa0
commit 21e6015f89
58 changed files with 1732 additions and 837 deletions

View File

@ -0,0 +1,56 @@
package gr.cite.annotation.web.authorization;
import gr.cite.annotation.authorization.AffiliatedAuthorizationRequirement;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.commons.web.authz.handler.AuthorizationHandler;
import gr.cite.commons.web.authz.handler.AuthorizationHandlerContext;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("affiliatedAuthorizationHandler")
public class AffiliatedAuthorizationHandler extends AuthorizationHandler<AffiliatedAuthorizationRequirement> {
private final CustomPermissionAttributesConfiguration myConfiguration;
@Autowired
public AffiliatedAuthorizationHandler(CustomPermissionAttributesConfiguration myConfiguration) {
this.myConfiguration = myConfiguration;
}
@Override
public int handleRequirement(AuthorizationHandlerContext context, Object resource, AuthorizationRequirement requirement) {
AffiliatedAuthorizationRequirement req = (AffiliatedAuthorizationRequirement) requirement;
if (req.getRequiredPermissions() == null)
return ACCESS_NOT_DETERMINED;
AffiliatedResource rs = (AffiliatedResource) resource;
boolean isAuthenticated = ((MyPrincipal) context.getPrincipal()).isAuthenticated();
if (!isAuthenticated)
return ACCESS_NOT_DETERMINED;
if (myConfiguration.getMyPolicies() == null)
return ACCESS_NOT_DETERMINED;
int hits = 0;
Boolean entityAffiliated = rs != null && rs.getAffiliated() != null ? rs.getAffiliated() : null;
for (String permission : req.getRequiredPermissions()) {
CustomPermissionAttributesProperties.MyPermission policy = myConfiguration.getMyPolicies().get(permission);
boolean hasPermission = policy != null && policy.getEntityAffiliated() != null && policy.getEntityAffiliated() && entityAffiliated != null && entityAffiliated;
if (hasPermission) hits += 1;
}
if ((req.getMatchAll() && req.getRequiredPermissions().size() == hits) || (!req.getMatchAll() && hits > 0))
return ACCESS_GRANTED;
return ACCESS_NOT_DETERMINED;
}
@Override
public Class<? extends AuthorizationRequirement> supporting() {
return AffiliatedAuthorizationRequirement.class;
}
}

View File

@ -0,0 +1,24 @@
package gr.cite.annotation.web.authorization;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
@EnableConfigurationProperties(CustomPermissionAttributesProperties.class)
public class CustomPermissionAttributesConfiguration {
private final CustomPermissionAttributesProperties properties;
@Autowired
public CustomPermissionAttributesConfiguration(CustomPermissionAttributesProperties properties) {
this.properties = properties;
}
public HashMap<String, CustomPermissionAttributesProperties.MyPermission> getMyPolicies() {
return properties.getPolicies();
}
}

View File

@ -0,0 +1,38 @@
package gr.cite.annotation.web.authorization;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import java.util.HashMap;
@ConfigurationProperties(prefix = "permissions")
@ConditionalOnProperty(prefix = "permissions", name = "enabled", havingValue = "true")
public class CustomPermissionAttributesProperties {
private final HashMap<String, MyPermission> policies;
@ConstructorBinding
public CustomPermissionAttributesProperties(HashMap<String, MyPermission> policies) {
this.policies = policies;
}
public HashMap<String, MyPermission> getPolicies() {
return policies;
}
public static class MyPermission {
private final Boolean entityAffiliated;
@ConstructorBinding
public MyPermission(Boolean entityAffiliated) {
this.entityAffiliated = entityAffiliated;
}
public Boolean getEntityAffiliated() {
return entityAffiliated;
}
}
}

View File

@ -1,5 +1,8 @@
package gr.cite.annotation.web.config;
import gr.cite.annotation.authorization.AffiliatedAuthorizationRequirement;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.web.authorization.AffiliatedAuthorizationHandler;
import gr.cite.commons.web.authz.handler.AuthorizationHandler;
import gr.cite.commons.web.authz.handler.PermissionClientAuthorizationHandler;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
@ -37,16 +40,19 @@ public class SecurityConfiguration {
private final AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
private final Filter apiKeyFilter;
private final OwnedAuthorizationHandler ownedAuthorizationHandler;
private final AffiliatedAuthorizationHandler affiliatedAuthorizationHandler;
@Autowired
public SecurityConfiguration(WebSecurityProperties webSecurityProperties,
@Qualifier("tokenAuthenticationResolver") AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver,
@Qualifier("apiKeyFilter") Filter apiKeyFilter,
@Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler) {
@Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler,
@Qualifier("affiliatedAuthorizationHandler") AffiliatedAuthorizationHandler affiliatedAuthorizationHandler) {
this.webSecurityProperties = webSecurityProperties;
this.authenticationManagerResolver = authenticationManagerResolver;
this.apiKeyFilter = apiKeyFilter;
this.ownedAuthorizationHandler = ownedAuthorizationHandler;
this.affiliatedAuthorizationHandler = affiliatedAuthorizationHandler;
}
@Bean
@ -78,7 +84,7 @@ public class SecurityConfiguration {
//If not set / set to null, only the default authorization handlers will be used
@Override
public List<AuthorizationHandler<? extends AuthorizationRequirement>> addCustomHandlers() {
return List.of(ownedAuthorizationHandler);
return List.of(affiliatedAuthorizationHandler, ownedAuthorizationHandler);
}
//Here you can register your custom authorization requirements (if any)
@ -114,6 +120,9 @@ public class SecurityConfiguration {
if (OwnedResource.class.equals(type)) {
return new OwnedAuthorizationRequirement();
}
if (AffiliatedResource.class.equals(type)) {
return new AffiliatedAuthorizationRequirement(matchAll, permissions);
}
throw new IllegalArgumentException("resource");
}
};

View File

@ -2,6 +2,7 @@ package gr.cite.annotation.web.controllers;
import com.fasterxml.jackson.core.JsonProcessingException;
import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.builder.AnnotationBuilder;
@ -71,9 +72,9 @@ public class AnnotationController {
this.censorFactory.censor(AnnotationCensor.class).censor(lookup.getProject(), null);
AnnotationQuery query = lookup.enrich(this.queryFactory);
AnnotationQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.OwnerOrPermissionAssociated);
List<AnnotationEntity> data = query.collect();
List<Annotation> models = this.builderFactory.builder(AnnotationBuilder.class).build(lookup.getProject(), data);
List<Annotation> models = this.builderFactory.builder(AnnotationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(lookup.getProject(), data);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
this.auditService.track(AuditableAction.Annotation_Query, "lookup", lookup);
@ -87,8 +88,8 @@ public class AnnotationController {
this.censorFactory.censor(AnnotationCensor.class).censor(fieldSet, null);
AnnotationQuery query = this.queryFactory.query(AnnotationQuery.class).ids(id);
Annotation model = this.builderFactory.builder(AnnotationBuilder.class).build(fieldSet, query.firstAs(fieldSet));
AnnotationQuery query = this.queryFactory.query(AnnotationQuery.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).ids(id);
Annotation model = this.builderFactory.builder(AnnotationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Annotation.class.getSimpleName()}, LocaleContextHolder.getLocale()));

View File

@ -67,7 +67,7 @@ public class TenantConfigurationController {
TenantConfigurationQuery query = lookup.enrich(this.queryFactory);
List<TenantConfigurationEntity> data = query.collectAs(lookup.getProject());
List<TenantConfiguration> models = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(lookup.getProject(), data);
List<TenantConfiguration> models = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(lookup.getProject(), data);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
this.auditService.track(AuditableAction.Tenant_Configuration_Query, "lookup", lookup);
@ -83,7 +83,7 @@ public class TenantConfigurationController {
this.censorFactory.censor(TenantConfigurationCensor.class).censor(fieldSet);
TenantConfigurationQuery query = this.queryFactory.query(TenantConfigurationQuery.class).ids(id);
TenantConfiguration model = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));
TenantConfiguration model = this.builderFactory.builder(TenantConfigurationBuilder.class).authorize(AuthorizationFlags.OwnerOrPermissionAssociated).build(fieldSet, query.firstAs(fieldSet));
if (model == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, TenantConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale()));

View File

@ -32,6 +32,12 @@ cache:
maximumSize: 500
enableRecordStats: false
expireAfterWriteSeconds: 320
- names: [ "affiliation" ]
allowNullValues: true
initialCapacity: 100
maximumSize: 5000
enableRecordStats: false
expireAfterWriteSeconds: 20
mapCaches:
apiKey:
name: apikey
@ -48,3 +54,6 @@ cache:
userAllowedTenant:
name: userAccessTenant
keyPattern: user_access_tenant_$user_id$_$tenant_id$:v0
affiliation:
name: affiliation
keyPattern: affiliation_$entity$_$user$_$type$:v0

View File

@ -1,7 +1,14 @@
permissions:
extendedClaims: [ ]
policies:
DeferredAffiliation:
roles:
- Admin
- User
- Manager
- DescriptionTemplateEditor
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Tenants
BrowseTenant:
roles:
@ -49,30 +56,18 @@ permissions:
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# UserContactInfo
BrowseUserContactInfo:
roles:
- Admin
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
EditUserContactInfo:
roles:
- Admin
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteUserContactInfo:
roles:
- Admin
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
#Annotation
BrowseAnnotation:
roles:
- Admin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
NewAnnotation:
roles:
- Admin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
@ -85,6 +80,7 @@ permissions:
DeleteAnnotation:
roles:
- Admin
entityAffiliated: false
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
@ -101,5 +97,3 @@ permissions:
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# ViewPage Permissions

View File

@ -45,7 +45,8 @@ queue:
tenant-touch-topic: tenant.touch
user-removal-topic: user.remove
user-touch-topic: user.touch
annotation-entity-touch-topic: annotation.entity.touch
annotation-entities-touch-topic: annotation.entities.touch
annotation-entities-removal-topic: annotation.entities.remove
rabbitmq:
enable: false
interval-seconds: 30

View File

@ -0,0 +1,40 @@
package gr.cite.annotation.authorization;
import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AffiliatedAuthorizationRequirement implements AuthorizationRequirement {
private final Set<String> requiredPermissions;
private final boolean matchAll;
public AffiliatedAuthorizationRequirement(Set<String> requiredPermissions) {
this(false, requiredPermissions);
}
public AffiliatedAuthorizationRequirement(String... requiredPermissions) {
this(false, requiredPermissions);
}
public AffiliatedAuthorizationRequirement(boolean matchAll, Set<String> requiredPermissions) {
this.matchAll = matchAll;
this.requiredPermissions = requiredPermissions;
}
public AffiliatedAuthorizationRequirement(boolean matchAll, String... requiredPermissions) {
this.requiredPermissions = new HashSet<>();
this.matchAll = matchAll;
this.requiredPermissions.addAll(Arrays.stream(requiredPermissions).distinct().toList());
}
public Set<String> getRequiredPermissions() {
return requiredPermissions;
}
public boolean getMatchAll() {
return matchAll;
}
}

View File

@ -0,0 +1,22 @@
package gr.cite.annotation.authorization;
import gr.cite.commons.web.authz.policy.AuthorizationResource;
public class AffiliatedResource extends AuthorizationResource {
private Boolean isAffiliated;
public AffiliatedResource() {
}
public AffiliatedResource(Boolean isAffiliated) {
this.isAffiliated = isAffiliated;
}
public Boolean getAffiliated() {
return isAffiliated;
}
public void setAffiliated(Boolean affiliated) {
isAffiliated = affiliated;
}
}

View File

@ -3,6 +3,6 @@ package gr.cite.annotation.authorization;
import java.util.EnumSet;
public enum AuthorizationFlags {
None, Permission, Owner;
public static final EnumSet<AuthorizationFlags> OwnerOrPermission = EnumSet.of(Owner, Permission);
None, Permission, Associated, Owner;
public static final EnumSet<AuthorizationFlags> OwnerOrPermissionAssociated = EnumSet.of(Owner, Permission, Associated);
}

View File

@ -1,15 +1,12 @@
package gr.cite.annotation.authorization;
public final class Permission {
public static String DeferredAffiliation = "DeferredAffiliation";
//User
public static String BrowseUser = "BrowseUser";
public static String EditUser = "EditUser";
public static String DeleteUser = "DeleteUser";
//UserContactInfo
public static String BrowseUserContactInfo = "BrowseUserContactInfo";
public static String EditUserContactInfo = "EditUserContactInfo";
public static String DeleteUserContactInfo = "DeleteUserContactInfo";
//Tenant
public static String BrowseTenant = "BrowseTenant";
@ -19,13 +16,12 @@ public final class Permission {
//Annotation
public static final String BrowseAnnotation = "BrowseAnnotation";
public static String NewAnnotation = "NewAnnotation";
public static String EditAnnotation = "EditAnnotation";
public static String DeleteAnnotation = "DeleteAnnotation";
public static final String BrowseTenantConfiguration = "BrowseTenantConfiguration";
public static final String EditTenantConfiguration = "EditTenantConfiguration";
// UI Pages
public static String ViewTenantConfigurationPage = "ViewTenantConfigurationPage";
}

View File

@ -0,0 +1,39 @@
package gr.cite.annotation.authorization;
import gr.cite.annotation.convention.ConventionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PermissionNameProvider {
private static final Logger logger = LoggerFactory.getLogger(PermissionNameProvider.class);
private final List<String> permissions;
public PermissionNameProvider(ConventionService conventionService) {
this.permissions = new ArrayList<>();
Class<Permission> clazz = Permission.class;
for (Field f : clazz.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())) {
try {
Object value = f.get(null);
if (value != null && !conventionService.isNullOrEmpty((String)value)) this.permissions.add((String)value);
} catch (Exception e) {
logger.error("Can not load permission " + f.getName() + " " + e.getMessage());
}
}
}
}
public List<String> getPermissions() {
return permissions;
}
}

View File

@ -0,0 +1,10 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.tools.cache.CacheOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.affiliation")
public class AffiliationCacheOptions extends CacheOptions {
}

View File

@ -0,0 +1,95 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.convention.ConventionService;
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.Locale;
import java.util.UUID;
@Service
public class AffiliationCacheService extends CacheService<AffiliationCacheService.AffiliationCacheValue> {
public static class AffiliationCacheValue {
public AffiliationCacheValue() {
}
public AffiliationCacheValue(UUID userId, UUID entityId, String entityType, AffiliatedResource affiliatedResource) {
this.userId = userId;
this.entityId = entityId;
this.entityType = entityType;
this.affiliatedResource = affiliatedResource;
}
private UUID userId;
private UUID entityId;
private String entityType;
private AffiliatedResource affiliatedResource;
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
public AffiliatedResource getAffiliatedResource() {
return affiliatedResource;
}
public void setAffiliatedResource(AffiliatedResource affiliatedResource) {
this.affiliatedResource = affiliatedResource;
}
}
private final ConventionService conventionService;
@Autowired
public AffiliationCacheService(AffiliationCacheOptions options, ConventionService conventionService) {
super(options);
this.conventionService = conventionService;
}
@Override
protected Class<AffiliationCacheValue> valueClass() {
return AffiliationCacheValue.class;
}
@Override
public String keyOf(AffiliationCacheValue value) {
return this.buildKey(value.getUserId(), value.getEntityId(), value.getEntityType());
}
public String buildKey(UUID userId, UUID entityId, String entityType) {
if (userId == null) throw new IllegalArgumentException("userId id is required");
if (entityId == null) throw new IllegalArgumentException("entityId id is required");
if (this.conventionService.isNullOrEmpty(entityType)) throw new IllegalArgumentException("entityType id is required");
HashMap<String, String> keyParts = new HashMap<>();
keyParts.put("$user$", userId.toString().replace("-", "").toLowerCase(Locale.ROOT));
keyParts.put("$entity$", entityId.toString().replace("-", "").toLowerCase(Locale.ROOT));
keyParts.put("$type$", entityType);
return this.generateKey(keyParts);
}
}

View File

@ -0,0 +1,20 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.annotation.authorization.AffiliatedResource;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public interface AuthorizationContentResolver {
AffiliatedResource entityAffiliation(UUID id);
Map<UUID, AffiliatedResource> entitiesAffiliation(List<UUID> ids);
List<String> getPermissionNames();
AffiliatedResource annotationAffiliation(UUID id);
Map<UUID, AffiliatedResource> annotationsAffiliation(List<UUID> ids);
}

View File

@ -0,0 +1,118 @@
package gr.cite.annotation.authorization.authorizationcontentresolver;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.authorization.PermissionNameProvider;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.EntityUser;
import gr.cite.annotation.query.AnnotationQuery;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequestScope
public class AuthorizationContentResolverImpl implements AuthorizationContentResolver {
private final QueryFactory queryFactory;
private final UserScope userScope;
private final AffiliationCacheService affiliationCacheService;
private final PermissionNameProvider permissionNameProvider;
public AuthorizationContentResolverImpl(QueryFactory queryFactory, UserScope userScope, AffiliationCacheService affiliationCacheService, PermissionNameProvider permissionNameProvider) {
this.queryFactory = queryFactory;
this.userScope = userScope;
this.affiliationCacheService = affiliationCacheService;
this.permissionNameProvider = permissionNameProvider;
}
@Override
public AffiliatedResource entityAffiliation(UUID id) {
return this.entitiesAffiliation(List.of(id)).getOrDefault(id, new AffiliatedResource());
}
@Override
public Map<UUID, AffiliatedResource> entitiesAffiliation(List<UUID> ids){
UUID userId = this.userScope.getUserIdSafe();
Map<UUID, AffiliatedResource> affiliatedResources = new HashMap<>();
for (UUID id : ids){
affiliatedResources.put(id, new AffiliatedResource());
}
if (userId == null || !userScope.isSet()) return affiliatedResources;
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, AnnotationEntity._entityId);
if (idsToResolve.isEmpty()) return affiliatedResources;
List<EntityUserEntity> entityUsers = this.queryFactory.query(EntityUserQuery.class).entityIds(ids).userIds(userId).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(EntityUser._id).ensure(EntityUser._entityId));
for (UUID entityId : entityUsers.stream().map(EntityUserEntity::getEntityId).distinct().toList()){
affiliatedResources.get(entityId).setAffiliated(true);
}
this.ensureAffiliatedInCache(idsToResolve, userId, affiliatedResources, AnnotationEntity._entityId);
return affiliatedResources;
}
@Override
public List<String> getPermissionNames() {
return permissionNameProvider.getPermissions();
}
@Override
public AffiliatedResource annotationAffiliation(UUID id) {
return this.annotationsAffiliation(List.of(id)).getOrDefault(id, new AffiliatedResource());
}
@Override
public Map<UUID, AffiliatedResource> annotationsAffiliation(List<UUID> ids){
UUID userId = this.userScope.getUserIdSafe();
Map<UUID, AffiliatedResource> affiliatedResources = new HashMap<>();
for (UUID id : ids){
affiliatedResources.put(id, new AffiliatedResource());
}
if (userId == null || !userScope.isSet()) return affiliatedResources;
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, AnnotationEntity.class.getSimpleName());
if (idsToResolve.isEmpty()) return affiliatedResources;
List<AnnotationEntity> annotationEntities = this.queryFactory.query(AnnotationQuery.class).ids(ids).collectAs(new BaseFieldSet().ensure(Annotation._id).ensure(Annotation._entityId).ensure(Annotation._id));
List<EntityUserEntity> entityUsers = this.queryFactory.query(EntityUserQuery.class).entityIds(annotationEntities.stream().map(AnnotationEntity::getEntityId).distinct().toList()).userIds(userId).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(EntityUser._id).ensure(EntityUser._entityId));
Map<UUID, List<EntityUserEntity>> dmpUsersMap = entityUsers.stream().collect(Collectors.groupingBy(EntityUserEntity::getEntityId));
for (AnnotationEntity annotation : annotationEntities){
List<EntityUserEntity> dmpDescriptionUsers = dmpUsersMap.getOrDefault(annotation.getEntityId(), new ArrayList<>());
if (!dmpDescriptionUsers.isEmpty()) {
affiliatedResources.get(annotation.getId()).setAffiliated(true);
}
}
this.ensureAffiliatedInCache(idsToResolve, userId, affiliatedResources, AnnotationEntity.class.getSimpleName());
return affiliatedResources;
}
private List<UUID> getAffiliatedFromCache(List<UUID> ids, UUID userId, Map<UUID, AffiliatedResource> affiliatedResources, String entityType){
List<UUID> idsToResolve = new ArrayList<>();
for (UUID id : ids){
AffiliationCacheService.AffiliationCacheValue cacheValue = this.affiliationCacheService.lookup(this.affiliationCacheService.buildKey(userId, id, entityType));
if (cacheValue != null) affiliatedResources.put(id, cacheValue.getAffiliatedResource());
else idsToResolve.add(id);
}
return idsToResolve;
}
private void ensureAffiliatedInCache(List<UUID> idsToResolve, UUID userId, Map<UUID, AffiliatedResource> affiliatedResources, String entityType){
for (UUID id : idsToResolve){
AffiliatedResource affiliatedResource = affiliatedResources.getOrDefault(id, null);
if (affiliatedResource != null) {
AffiliationCacheService.AffiliationCacheValue cacheValue = new AffiliationCacheService.AffiliationCacheValue(userId, id, entityType, affiliatedResource);
this.affiliationCacheService.put(cacheValue);
}
}
}
}

View File

@ -2,7 +2,6 @@ package gr.cite.annotation.common.validation;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.inbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEvent;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;

View File

@ -36,7 +36,8 @@ public class AppRabbitConfigurer extends RabbitConfigurer {
bindingItems.addAll(this.inboxProperties.getTenantTouchTopic());
bindingItems.addAll(this.inboxProperties.getUserRemovalTopic());
bindingItems.addAll(this.inboxProperties.getUserTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntityTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntitiesTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntitiesRemovalTopic());
return new InboxBindings(bindingItems);
}

View File

@ -17,7 +17,8 @@ public class InboxProperties {
private final List<String> userTouchTopic;
private final List<String> annotationEntityTouchTopic;
private final List<String> annotationEntitiesTouchTopic;
private final List<String> annotationEntitiesRemovalTopic;
public InboxProperties(
String exchange,
@ -25,13 +26,14 @@ public class InboxProperties {
List<String> tenantTouchTopic,
List<String> userRemovalTopic,
List<String> userTouchTopic,
List<String> annotationEntityTouchTopic) {
List<String> annotationEntitiesTouchTopic, List<String> annotationEntitiesRemovalTopic) {
this.exchange = exchange;
this.tenantRemovalTopic = tenantRemovalTopic;
this.tenantTouchTopic = tenantTouchTopic;
this.userRemovalTopic = userRemovalTopic;
this.userTouchTopic = userTouchTopic;
this.annotationEntityTouchTopic = annotationEntityTouchTopic;
this.annotationEntitiesTouchTopic = annotationEntitiesTouchTopic;
this.annotationEntitiesRemovalTopic = annotationEntitiesRemovalTopic;
}
public List<String> getTenantRemovalTopic() {
@ -50,8 +52,12 @@ public class InboxProperties {
return userTouchTopic;
}
public List<String> getAnnotationEntityTouchTopic() {
return annotationEntityTouchTopic;
public List<String> getAnnotationEntitiesTouchTopic() {
return annotationEntitiesTouchTopic;
}
public List<String> getAnnotationEntitiesRemovalTopic() {
return annotationEntitiesRemovalTopic;
}
public String getExchange() {

View File

@ -5,7 +5,8 @@ import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.data.QueueInboxEntity;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.annotation.integrationevent.inbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval.AnnotationEntitiesRemovalIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.annotationentitiestouch.AnnotationEntitiesTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.tenantremoval.TenantRemovalIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.tenanttouch.TenantTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.userremoval.UserRemovalIntegrationEventHandler;
@ -332,8 +333,10 @@ public class InboxRepositoryImpl implements InboxRepository {
handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchTopic()))
handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntityTouchTopic()))
handler = this.applicationContext.getBean(AnnotationEntityTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesTouchTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesRemovalTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesRemovalIntegrationEventHandler.class);
else {
logger.error("No handler found for message routing key '{}'. Discarding.", routingKey);
handler = null;

View File

@ -0,0 +1,70 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval;
import gr.cite.annotation.common.validation.BaseValidator;
import gr.cite.annotation.common.validation.UuidValidator;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesRemovalIntegrationEvent extends TrackedEvent {
private List<UUID> entityIds;
public static final String _entityIds = "entityIds";
public List<UUID> getEntityIds() {
return entityIds;
}
public void setEntityIds(List<UUID> entityIds) {
this.entityIds = entityIds;
}
@Component(AnnotationEntitiesRemovalIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntitiesRemovalIntegrationEventValidator extends BaseValidator<AnnotationEntitiesRemovalIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntitiesRemovalIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntitiesRemovalIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntitiesRemovalIntegrationEvent> modelClass() {
return AnnotationEntitiesRemovalIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntitiesRemovalIntegrationEvent item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getEntityIds()))
.failOn(AnnotationEntitiesRemovalIntegrationEvent._entityIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntitiesRemovalIntegrationEvent._entityIds}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getEntityIds()))
.on(AnnotationEntitiesRemovalIntegrationEvent._entityIds)
.over(item.getEntityIds())
.using((i) -> this.validatorFactory.validator(UuidValidator.class))
);
}
}
}

View File

@ -0,0 +1,7 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventHandler;
public interface AnnotationEntitiesRemovalIntegrationEventHandler extends IntegrationEventHandler {
}

View File

@ -1,4 +1,4 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitytouch;
package gr.cite.annotation.integrationevent.inbox.annotationentitiesremoval;
import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService;
@ -11,6 +11,7 @@ import gr.cite.annotation.integrationevent.inbox.IntegrationEventProperties;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory;
@ -24,17 +25,13 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements AnnotationEntityTouchedIntegrationEventHandler {
public class AnnotationEntitiesRemovalIntegrationEventHandlerImpl implements AnnotationEntitiesRemovalIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntityTouchedIntegrationEventHandlerImpl.class));
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntitiesRemovalIntegrationEventHandlerImpl.class));
private final JsonHandlingService jsonHandlingService;
@ -44,7 +41,7 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
private final QueryFactory queryFactory;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) {
public AnnotationEntitiesRemovalIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) {
this.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext;
@ -53,66 +50,41 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
@Override
public EventProcessingStatus handle(IntegrationEventProperties properties, String message) {
AnnotationEntityTouchedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntityTouchedIntegrationEvent.class, message);
AnnotationEntitiesRemovalIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntitiesRemovalIntegrationEvent.class, message);
if (event == null)
return EventProcessingStatus.Error;
logger.debug("Handling {}", AnnotationEntityTouchedIntegrationEvent.class.getSimpleName());
logger.debug("Handling {}", AnnotationEntitiesRemovalIntegrationEvent.class.getSimpleName());
this.validatorFactory.validator(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.class).validateForce(event);
this.validatorFactory.validator(AnnotationEntitiesRemovalIntegrationEvent.AnnotationEntitiesRemovalIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
// QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
// TenantScope scope = this.applicationContext.getBean(TenantScope.class);
// if (scope.isMultitenant() && event.getTenant() != null) {
// TenantEntity tenant = queryFactory.query(TenantQuery.class).ids(event.getTenant()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
// if (tenant == null) {
// logger.error("missing tenant from event message");
// return EventProcessingStatus.Error;
// }
// scope.setTenant(event.getTenant(), tenant.getCode());
// } else if (scope.isMultitenant()) {
// logger.error("missing tenant from event message");
// return EventProcessingStatus.Error;
// }
//
// ValidationService validator = this.applicationContext.getBean(ValidationService.class);
// validator.validateForce(model);
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class);
transaction = entityManager.getTransaction();
transaction.begin();
try {
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> associatedUsersEntities = entityUserQuery
.entityIds(event.getEntityId())
List<EntityUserEntity> items = entityUserQuery
.entityIds(event.getEntityIds())
.isActive(IsActive.Active)
.collect();
List<UUID> associatedUsers = associatedUsersEntities.stream()
.map(EntityUserEntity::getUserId)
.toList();
for (UUID user : event.getUserIds()) {
if (!associatedUsers.contains(user)) {
EntityUserEntity entityUserEntity = new EntityUserEntity();
entityUserEntity.setId(UUID.randomUUID());
entityUserEntity.setEntityId(event.getEntityId());
entityUserEntity.setUserId(user);
entityUserEntity.setCreatedAt(Instant.now());
entityUserEntity.setUpdatedAt(Instant.now());
entityUserEntity.setIsActive(IsActive.Active);
entityManager.persist(entityUserEntity);
}
}
deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(items);
entityManager.flush();
AuditService auditService = this.applicationContext.getBean(AuditService.class);

View File

@ -0,0 +1,138 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiestouch;
import gr.cite.annotation.common.validation.BaseValidator;
import gr.cite.annotation.common.validation.UuidValidator;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesTouchedIntegrationEvent extends TrackedEvent {
private List<AnnotationEntityTouchedIntegrationEvent> events;
public static final String _events = "events";
public List<AnnotationEntityTouchedIntegrationEvent> getEvents() {
return events;
}
public void setEvents(List<AnnotationEntityTouchedIntegrationEvent> events) {
this.events = events;
}
@Component(AnnotationEntitiesTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntitiesTouchedIntegrationEventValidator extends BaseValidator<AnnotationEntitiesTouchedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntitiesTouchedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntitiesTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntitiesTouchedIntegrationEvent> modelClass() {
return AnnotationEntitiesTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntitiesTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getEvents()))
.failOn(AnnotationEntitiesTouchedIntegrationEvent._events).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntitiesTouchedIntegrationEvent._events}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getEvents()))
.on(AnnotationEntitiesTouchedIntegrationEvent._events)
.over(item.getEvents())
.using((i) -> this.validatorFactory.validator(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.class))
);
}
}
public static class AnnotationEntityTouchedIntegrationEvent {
private UUID entityId;
public static final String _entityId = "entityId";
private List<UUID> userIds;
public static final String _userIds = "userIds";
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
@Component(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntityTouchedIntegrationEventValidator extends BaseValidator<AnnotationEntityTouchedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntityTouchedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntityTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntityTouchedIntegrationEvent> modelClass() {
return AnnotationEntityTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntityTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.iff(() -> !this.isNull(item.getEntityId()))
.must(() -> this.isValidGuid(item.getEntityId()))
.failOn(AnnotationEntityTouchedIntegrationEvent._entityId).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._entityId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getUserIds()))
.failOn(AnnotationEntityTouchedIntegrationEvent._userIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._userIds}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getUserIds()))
.on(AnnotationEntityTouchedIntegrationEvent._userIds)
.over(item.getUserIds())
.using((i) -> this.validatorFactory.validator(UuidValidator.class))
);
}
}
}
}

View File

@ -1,7 +1,7 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitytouch;
package gr.cite.annotation.integrationevent.inbox.annotationentitiestouch;
import gr.cite.annotation.integrationevent.inbox.IntegrationEventHandler;
public interface AnnotationEntityTouchedIntegrationEventHandler extends IntegrationEventHandler {
public interface AnnotationEntitiesTouchedIntegrationEventHandler extends IntegrationEventHandler {
}

View File

@ -0,0 +1,141 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitiestouch;
import gr.cite.annotation.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.data.EntityUserEntity;
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.query.EntityUserQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.OptimisticLockException;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntitiesTouchedIntegrationEventHandlerImpl implements AnnotationEntitiesTouchedIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntitiesTouchedIntegrationEventHandlerImpl.class));
private final JsonHandlingService jsonHandlingService;
private final ValidatorFactory validatorFactory;
private final ApplicationContext applicationContext;
private final QueryFactory queryFactory;
public AnnotationEntitiesTouchedIntegrationEventHandlerImpl(JsonHandlingService jsonHandlingService, ValidatorFactory validatorFactory, ApplicationContext applicationContext, QueryFactory queryFactory) {
this.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext;
this.queryFactory = queryFactory;
}
@Override
public EventProcessingStatus handle(IntegrationEventProperties properties, String message) {
AnnotationEntitiesTouchedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntitiesTouchedIntegrationEvent.class, message);
if (event == null)
return EventProcessingStatus.Error;
logger.debug("Handling {}", AnnotationEntitiesTouchedIntegrationEvent.class.getSimpleName());
this.validatorFactory.validator(AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntitiesTouchedIntegrationEventValidator.class).validateForce(event);
EntityManager entityManager = null;
EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) {
try {
CurrentPrincipalResolver currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager();
DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class);
transaction = entityManager.getTransaction();
transaction.begin();
try {
for (AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent entityEvent : event.getEvents()) {
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> items = entityUserQuery
.entityIds(entityEvent.getEntityId())
.isActive(IsActive.Active)
.collect();
List<UUID> updatedCreatedIds = new ArrayList<>();
for (UUID user : entityEvent.getUserIds()) {
EntityUserEntity data = items.stream().filter(x -> x.getUserId().equals(user)).findFirst().orElse(null);
if (data == null) {
data = new EntityUserEntity();
data.setId(UUID.randomUUID());
data.setEntityId(entityEvent.getEntityId());
data.setUserId(user);
data.setCreatedAt(Instant.now());
data.setUpdatedAt(Instant.now());
data.setIsActive(IsActive.Active);
entityManager.persist(data);
}
updatedCreatedIds.add(data.getId());
}
List<EntityUserEntity> toDelete = items.stream().filter(x -> updatedCreatedIds.stream().noneMatch(y -> y.equals(x.getId()))).collect(Collectors.toList());
deleterFactory.deleter(gr.cite.EntityUser.model.deleter.EntityUserDeleter.class).delete(toDelete);
entityManager.flush();
}
AuditService auditService = this.applicationContext.getBean(AuditService.class);
auditService.track(AuditableAction.User_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event)
));
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw e;
} finally {
currentPrincipalResolver.pop();
}
} catch (OptimisticLockException ex) {
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working
logger.debug("Concurrency exception getting queue outbox. Skipping: {} ", ex.getMessage());
if (transaction != null)
transaction.rollback();
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
if (transaction != null)
transaction.rollback();
} finally {
if (entityManager != null)
entityManager.close();
}
} catch (Exception ex) {
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
}
return EventProcessingStatus.Success;
}
}

View File

@ -1,86 +0,0 @@
package gr.cite.annotation.integrationevent.inbox.annotationentitytouch;
import gr.cite.annotation.common.validation.BaseValidator;
import gr.cite.annotation.common.validation.UuidValidator;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.TrackedEvent;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntityTouchedIntegrationEvent extends TrackedEvent {
private UUID entityId;
public static final String _entityId = "entityId";
private List<UUID> userIds;
public static final String _userIds = "userIds";
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
@Component(AnnotationEntityTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntityTouchedIntegrationEventValidator extends BaseValidator<AnnotationEntityTouchedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntityTouchedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntityTouchedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntityTouchedIntegrationEvent> modelClass() {
return AnnotationEntityTouchedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntityTouchedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.iff(() -> !this.isNull(item.getEntityId()))
.must(() -> this.isValidGuid(item.getEntityId()))
.failOn(AnnotationEntityTouchedIntegrationEvent._entityId).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._entityId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isListNullOrEmpty(item.getUserIds()))
.failOn(AnnotationEntityTouchedIntegrationEvent._userIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityTouchedIntegrationEvent._userIds}, LocaleContextHolder.getLocale())),
this.navSpec()
.iff(() -> !this.isListNullOrEmpty(item.getUserIds()))
.on(AnnotationEntityTouchedIntegrationEvent._userIds)
.over(item.getUserIds())
.using((i) -> this.validatorFactory.validator(UuidValidator.class))
);
}
}
}

View File

@ -7,6 +7,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
public class Annotation {
@ -67,6 +68,13 @@ public class Annotation {
public static final String _isActive = "isActive";
private String hash;
public static final String _hash = "hash";
private List<String> authorizationFlags;
public static final String _authorizationFlags = "authorizationFlags";
public UUID getId() {
return id;
}
@ -179,4 +187,19 @@ public class Annotation {
this.isActive = isActive;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public List<String> getAuthorizationFlags() {
return authorizationFlags;
}
public void setAuthorizationFlags(List<String> authorizationFlags) {
this.authorizationFlags = authorizationFlags;
}
}

View File

@ -0,0 +1,94 @@
package gr.cite.annotation.model;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive;
import java.time.Instant;
import java.util.UUID;
public class EntityUser {
private UUID id;
public static final String _id = "id";
private UUID entityId;
public static final String _entityId = "entityId";
private User user;
public static final String _user = "user";
private Instant createdAt;
public static final String _createdAt = "createdAt";
private Instant updatedAt;
public static final String _updatedAt = "updatedAt";
private IsActive isActive;
public static final String _isActive = "isActive";
private String hash;
public static final String _hash = "hash";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
public IsActive getIsActive() {
return isActive;
}
public void setIsActive(IsActive isActive) {
this.isActive = isActive;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
}

View File

@ -1,12 +1,15 @@
package gr.cite.annotation.model.builder;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.AnnotationAuthor;
import gr.cite.annotation.model.User;
import gr.cite.annotation.query.UserQuery;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
@ -29,13 +32,17 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
private final QueryFactory queryFactory;
private final BuilderFactory builderFactory;
private final AuthorizationContentResolver authorizationContentResolver;
private final AuthorizationService authorizationService;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public AnnotationBuilder(ConventionService conventionService, QueryFactory queryFactory, BuilderFactory builderFactory) {
public AnnotationBuilder(ConventionService conventionService, QueryFactory queryFactory, BuilderFactory builderFactory, AuthorizationContentResolver authorizationContentResolver, AuthorizationService authorizationService) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(AnnotationBuilder.class)));
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.authorizationContentResolver = authorizationContentResolver;
this.authorizationService = authorizationService;
}
public AnnotationBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -51,42 +58,32 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
return new ArrayList<>();
List<Annotation> models = new ArrayList<>();
if (data == null) return models;
FieldSet authorFields = fields.extractPrefixed(this.asPrefix(Annotation._author));
Map<UUID, AnnotationAuthor> authorsMap = this.collectAuthors(authorFields, data);
Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Annotation._authorizationFlags, this.authorizationContentResolver.getPermissionNames());
Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.annotationsAffiliation(data.stream().map(AnnotationEntity::getId).toList());
if (data == null)
return models;
for (AnnotationEntity d : data) {
Annotation m = new Annotation();
if (fields.hasField(this.asIndexer(Annotation._id)))
m.setId(d.getId());
if (fields.hasField(this.asIndexer(Annotation._entityId)))
m.setEntityId(d.getEntityId());
if (fields.hasField(this.asIndexer(Annotation._entityType)))
m.setEntityType(d.getEntityType());
if (fields.hasField(this.asIndexer(Annotation._anchor)))
m.setAnchor(d.getAnchor());
if (fields.hasField(this.asIndexer(Annotation._payload)))
m.setPayload(d.getPayload());
if (fields.hasField(this.asIndexer(Annotation._subjectId)))
m.setSubjectId(d.getSubjectId());
if (authorsMap != null && authorsMap.containsKey(d.getSubjectId()))
m.setAuthor(authorsMap.get(d.getSubjectId()));
if (fields.hasField(this.asIndexer(Annotation._threadId)))
m.setThreadId(d.getThreadId());
if (fields.hasField(this.asIndexer(Annotation._parentId)))
m.setParentId(d.getParentId());
if (fields.hasField(this.asIndexer(Annotation._protectionType)))
m.setProtectionType(d.getProtectionType());
if (fields.hasField(this.asIndexer(Annotation._timeStamp)))
m.setTimeStamp(d.getTimeStamp());
if (fields.hasField(this.asIndexer(Annotation._createdAt)))
m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(Annotation._updatedAt)))
m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(Annotation._isActive)))
m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(Annotation._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(Annotation._entityId))) m.setEntityId(d.getEntityId());
if (fields.hasField(this.asIndexer(Annotation._entityType))) m.setEntityType(d.getEntityType());
if (fields.hasField(this.asIndexer(Annotation._anchor))) m.setAnchor(d.getAnchor());
if (fields.hasField(this.asIndexer(Annotation._payload))) m.setPayload(d.getPayload());
if (fields.hasField(this.asIndexer(Annotation._subjectId))) m.setSubjectId(d.getSubjectId());
if (fields.hasField(this.asIndexer(Annotation._threadId))) m.setThreadId(d.getThreadId());
if (fields.hasField(this.asIndexer(Annotation._parentId))) m.setParentId(d.getParentId());
if (fields.hasField(this.asIndexer(Annotation._protectionType))) m.setProtectionType(d.getProtectionType());
if (fields.hasField(this.asIndexer(Annotation._timeStamp))) m.setTimeStamp(d.getTimeStamp());
if (fields.hasField(this.asIndexer(Annotation._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(Annotation._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(Annotation._isActive))) m.setIsActive(d.getIsActive());
if (fields.hasField(this.asIndexer(Annotation._hash))) m.setHash(this.hashValue(d.getUpdatedAt()));
if (!authorFields.isEmpty() && authorsMap != null && authorsMap.containsKey(d.getSubjectId())) m.setAuthor(authorsMap.get(d.getSubjectId()));
if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null)));
models.add(m);
}
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
@ -103,7 +100,7 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
.map(AnnotationEntity::getSubjectId)
.distinct()
.collect(Collectors.toList());
UserQuery query = this.queryFactory.query(UserQuery.class).authorize(this.authorize).ids(userIds);
UserQuery query = this.queryFactory.query(UserQuery.class).ids(userIds);
Map<UUID, List<User>> users = this.builderFactory.builder(UserBuilder.class).authorize(this.authorize).asMasterKey(query, clone, User::getId);
users.forEach((key, val) -> {

View File

@ -1,6 +1,8 @@
package gr.cite.annotation.model.builder;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.Builder;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.exception.MyApplicationException;
@ -92,4 +94,25 @@ public abstract class BaseBuilder<M, D> implements Builder {
return this.conventionService.asIndexer(names);
}
protected Set<String> extractAuthorizationFlags(FieldSet fields, String propertyName, List<String> permissionNames){
if (fields == null) return new HashSet<>();
if (permissionNames == null) return new HashSet<>();
FieldSet authorizationFlags = fields.extractPrefixed(this.asPrefix(propertyName));
List<String> permissions = new ArrayList<>();
for (String fieldValue : authorizationFlags.getFields()) permissions.addAll(permissionNames.stream().filter(x-> x.equalsIgnoreCase(fieldValue)).toList());
return new HashSet<>(permissions);
}
protected List<String> evaluateAuthorizationFlags(AuthorizationService authorizationService, Set<String> authorizationFlags, AffiliatedResource affiliatedResource) {
List<String> allowed = new ArrayList<>();
if (authorizationFlags == null) return allowed;
if (authorizationService == null) return allowed;
for (String permission : authorizationFlags) {
Boolean isAllowed = affiliatedResource == null ? authorizationService.authorize(permission) : authorizationService.authorizeAtLeastOne(List.of(affiliatedResource), permission);
if (isAllowed) allowed.add(permission);
}
return allowed;
}
}

View File

@ -0,0 +1,38 @@
package gr.cite.annotation.model.censorship;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.model.Annotation;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationAuthorCensor extends BaseCensor{
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationAuthorCensor.class));
protected final AuthorizationService authService;
public AnnotationAuthorCensor(ConventionService conventionService, AuthorizationService authService) {
super(conventionService);
this.authService = authService;
}
public void censor(FieldSet fields, UUID userId) {
logger.debug(new DataLogEntry("censoring fields", fields));
if (fields == null || fields.isEmpty())
return;
this.authService.authorizeForce(Permission.BrowseAnnotation, Permission.DeferredAffiliation);
}
}

View File

@ -2,7 +2,9 @@ package gr.cite.annotation.model.censorship;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.model.Annotation;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.censor.CensorFactory;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
@ -21,9 +23,12 @@ public class AnnotationCensor extends BaseCensor{
protected final AuthorizationService authService;
public AnnotationCensor(ConventionService conventionService, AuthorizationService authService) {
protected final CensorFactory censorFactory;
public AnnotationCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) {
super(conventionService);
this.authService = authService;
this.censorFactory = censorFactory;
}
public void censor(FieldSet fields, UUID userId) {
@ -31,7 +36,10 @@ public class AnnotationCensor extends BaseCensor{
if (fields == null || fields.isEmpty())
return;
this.authService.authorizeForce(Permission.BrowseAnnotation);
this.authService.authorizeForce(Permission.BrowseAnnotation, Permission.DeferredAffiliation);
FieldSet authorFields = fields.extractPrefixed(this.asIndexerPrefix(Annotation._author));
this.censorFactory.censor(AnnotationAuthorCensor.class).censor(authorFields, userId);
}
}

View File

@ -0,0 +1,74 @@
package gr.cite.EntityUser.model.deleter;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.data.TenantScopedEntityManager;
import gr.cite.annotation.query.EntityUserQuery;
import gr.cite.tools.data.deleter.Deleter;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class EntityUserDeleter implements Deleter {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(EntityUserDeleter.class));
private final TenantScopedEntityManager entityManager;
private final QueryFactory queryFactory;
@Autowired
public EntityUserDeleter(
TenantScopedEntityManager entityManager,
QueryFactory queryFactory
) {
this.entityManager = entityManager;
this.queryFactory = queryFactory;
}
public void deleteAndSaveByIds(List<UUID> ids) throws InvalidApplicationException {
logger.debug(new MapLogEntry("collecting to delete").And("count", Optional.ofNullable(ids).map(List::size).orElse(0)).And("ids", ids));
List<EntityUserEntity> data = this.queryFactory.query(EntityUserQuery.class).ids(ids).collect();
logger.trace("retrieved {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.deleteAndSave(data);
}
public void deleteAndSave(List<EntityUserEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
this.delete(data);
logger.trace("saving changes");
this.entityManager.flush();
logger.trace("changes saved");
}
public void delete(List<EntityUserEntity> data) throws InvalidApplicationException {
logger.debug("will delete {} items", Optional.ofNullable(data).map(List::size).orElse(0));
if (data == null || data.isEmpty())
return;
Instant now = Instant.now();
for (EntityUserEntity item : data) {
logger.trace("deleting item {}", item);
item.setIsActive(IsActive.Inactive);
item.setUpdatedAt(now);
logger.trace("updating item");
this.entityManager.merge(item);
logger.trace("updated item");
}
}
}

View File

@ -1,11 +1,16 @@
package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.EntityUser;
import gr.cite.annotation.query.utils.BuildSubQueryInput;
import gr.cite.annotation.query.utils.QueryUtilsService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
@ -13,6 +18,7 @@ import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@ -44,10 +50,13 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
private final UserScope userScope;
private final QueryUtilsService queryUtilsService;
private final AuthorizationService authService;
public AnnotationQuery(UserScope userScope, AuthorizationService authService) {
public AnnotationQuery(UserScope userScope, QueryUtilsService queryUtilsService, AuthorizationService authService) {
this.userScope = userScope;
this.queryUtilsService = queryUtilsService;
this.authService = authService;
}
@ -161,6 +170,11 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
return this;
}
public AnnotationQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.excludedIds) || this.isEmpty(this.isActives) || this.isEmpty(this.entityIds);
@ -171,6 +185,43 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
return AnnotationEntity.class;
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseAnnotation)) return null;
UUID userId = null;
if (this.authorize.contains(AuthorizationFlags.Associated)) userId = this.userScope.getUserIdSafe();
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (userId != null ) {
UUID finalUserId = userId;
Subquery<UUID> subquery = this.queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(EntityUserEntity.class, UUID.class)
.query(queryContext.Query)
.criteriaBuilder(queryContext.CriteriaBuilder)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(EntityUserEntity._entityId))
.filterFunc((subQueryRoot, cb) ->
cb.and(
cb.equal(subQueryRoot.get(EntityUserEntity._userId), finalUserId),
cb.equal(subQueryRoot.get(EntityUserEntity._isActive), IsActive.Active)
)
)
));
predicates.add(queryContext.CriteriaBuilder.or(
queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._subjectId)).value(userId),
queryContext.CriteriaBuilder.and(
queryContext.CriteriaBuilder.equal(queryContext.Root.get(AnnotationEntity._protectionType), AnnotationProtectionType.EntityAccessors),
queryContext.CriteriaBuilder.in(queryContext.Root.get(AnnotationEntity._entityId)).value(subquery)
)));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>();

View File

@ -1,8 +1,16 @@
package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.EntityUser;
import gr.cite.annotation.query.utils.BuildSubQueryInput;
import gr.cite.annotation.query.utils.QueryUtilsService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext;
@ -12,6 +20,7 @@ import jakarta.persistence.criteria.Predicate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import java.time.Instant;
import java.util.*;
@Component
@ -20,10 +29,19 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
private Collection<UUID> ids, entityIds, userIds;
private Collection<IsActive> isActives;
private Collection<IsActive> isActives;;
private final AuthorizationService authService;
private final UserScope userScope;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public EntityUserQuery(AuthorizationService authService, UserScope userScope) {
this.authService = authService;
this.userScope = userScope;
}
public EntityUserQuery ids(UUID value) {
this.ids = List.of(value);
return this;
@ -98,6 +116,26 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
protected Class<EntityUserEntity> entityClass() {
return EntityUserEntity.class;
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseAnnotation)) return null;
UUID userId = null;
if (this.authorize.contains(AuthorizationFlags.Associated)) userId = this.userScope.getUserIdSafe();
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (userId != null ) {
predicates.add( queryContext.CriteriaBuilder.in(queryContext.Root.get(EntityUserEntity._userId)).value(userId));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
@ -136,11 +174,35 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
@Override
protected String fieldNameOf(FieldResolver item) {
if (item.match(EntityUser._id))
return EntityUserEntity._id;
else if (item.match(EntityUser._entityId))
return EntityUserEntity._entityId;
else if (item.match(EntityUser._user))
return EntityUserEntity._userId;
else if (item.prefix(EntityUser._user))
return EntityUserEntity._userId;
else if (item.match(EntityUser._createdAt))
return EntityUserEntity._createdAt;
else if (item.match(EntityUser._updatedAt))
return EntityUserEntity._updatedAt;
else if (item.match(EntityUser._hash))
return EntityUserEntity._updatedAt;
else if (item.match(EntityUser._isActive))
return EntityUserEntity._isActive;
else
return null;
}
@Override
protected EntityUserEntity convert(Tuple tuple, Set<String> columns) {
return null;
EntityUserEntity item = new EntityUserEntity();
item.setId(QueryBase.convertSafe(tuple, columns, EntityUserEntity._id, UUID.class));
item.setEntityId(QueryBase.convertSafe(tuple, columns, EntityUserEntity._entityId, UUID.class));
item.setUserId(QueryBase.convertSafe(tuple, columns, EntityUserEntity._userId, UUID.class));
item.setCreatedAt(QueryBase.convertSafe(tuple, columns, EntityUserEntity._createdAt, Instant.class));
item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, EntityUserEntity._updatedAt, Instant.class));
item.setIsActive(QueryBase.convertSafe(tuple, columns, EntityUserEntity._isActive, IsActive.class));
return item;
}
}

View File

@ -0,0 +1,90 @@
package gr.cite.annotation.query.utils;
import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.criteria.*;
import java.util.function.BiFunction;
import java.util.function.Function;
public class BuildSubQueryInput<Entity, Key> {
private final AbstractQuery<?> query;
private final CriteriaBuilder criteriaBuilder;
private final Class<Entity> entityType;
private final Class<Key> keyType;
private final Function<Root<Entity>, Expression<Key>> keyPathFunc;
private final BiFunction<Root<Entity>, CriteriaBuilder, Predicate> filterFunc;
public BuildSubQueryInput(Builder<Entity, Key> builder) {
query = builder.query;
criteriaBuilder = builder.criteriaBuilder;
entityType = builder.entityType;
keyType = builder.keyType;
keyPathFunc = builder.keyPathFunc;
filterFunc = builder.filterFunc;
}
public AbstractQuery<?> getQuery() {
return query;
}
public CriteriaBuilder getCriteriaBuilder() {
return criteriaBuilder;
}
public Class<Entity> getEntityType() {
return entityType;
}
public Class<Key> getKeyType() {
return keyType;
}
public Function<Root<Entity>, Expression<Key>> getKeyPathFunc() {
return keyPathFunc;
}
public BiFunction<Root<Entity>, CriteriaBuilder, Predicate> getFilterFunc() {
return filterFunc;
}
public static class Builder<Entity, Key> {
private final Class<Entity> entityType;
private final Class<Key> keyType;
private AbstractQuery<?> query;
private CriteriaBuilder criteriaBuilder;
private Function<Root<Entity>, Expression<Key>> keyPathFunc;
private BiFunction<Root<Entity>, CriteriaBuilder, Predicate> filterFunc;
public Builder(Class<Entity> entityType, Class<Key> keyType) {
this.entityType = entityType;
this.keyType = keyType;
}
public Builder(Class<Entity> entityType, Class<Key> keyType, QueryContext<?, ?> queryContext) {
this.entityType = entityType;
this.keyType = keyType;
this.query = queryContext.Query;
this.criteriaBuilder = queryContext.CriteriaBuilder;
}
public Builder<Entity, Key> query(AbstractQuery<?> query) {
this.query = query;
return this;
}
public Builder<Entity, Key> criteriaBuilder(CriteriaBuilder criteriaBuilder) {
this.criteriaBuilder = criteriaBuilder;
return this;
}
public Builder<Entity, Key> keyPathFunc(Function<Root<Entity>, Expression<Key>> keyPathFunc) {
this.keyPathFunc = keyPathFunc;
return this;
}
public Builder<Entity, Key> filterFunc(BiFunction<Root<Entity>, CriteriaBuilder, Predicate> filterFunc) {
this.filterFunc = filterFunc;
return this;
}
}
}

View File

@ -0,0 +1,11 @@
package gr.cite.annotation.query.utils;
import jakarta.persistence.criteria.AbstractQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Subquery;
import java.util.UUID;
public interface QueryUtilsService {
<Key, D> Subquery<Key> buildSubQuery(BuildSubQueryInput<D, Key> parameters);
}

View File

@ -0,0 +1,20 @@
package gr.cite.annotation.query.utils;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class QueryUtilsServiceImpl implements QueryUtilsService {
@Override
public <Key, D> Subquery<Key> buildSubQuery(BuildSubQueryInput<D, Key> parameters){
Subquery<Key> subQuery = parameters.getQuery().subquery(parameters.getKeyType());
Root<D> subQueryRoot = subQuery.from(parameters.getEntityType());
subQuery.select(parameters.getKeyPathFunc().apply(subQueryRoot)).distinct(true);
subQuery.where(parameters.getFilterFunc().apply(subQueryRoot, parameters.getCriteriaBuilder()));
return subQuery;
}
}

View File

@ -1,7 +1,9 @@
package gr.cite.annotation.service.annotation;
import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.OwnedResource;
import gr.cite.annotation.authorization.Permission;
import gr.cite.annotation.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
@ -23,6 +25,8 @@ import gr.cite.tools.logging.MapLogEntry;
import jakarta.persistence.EntityManager;
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;
@ -45,17 +49,23 @@ public class AnnotationServiceImpl implements AnnotationService {
private final BuilderFactory builderFactory;
private final UserScope userScope;
private final AuthorizationContentResolver authorizationContentResolver;
private final MessageSource messageSource;
public AnnotationServiceImpl(
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
EntityManager entityManager,
BuilderFactory builderFactory, UserScope userScope) {
BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) {
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.entityManager = entityManager;
this.builderFactory = builderFactory;
this.userScope = userScope;
this.authorizationContentResolver = authorizationContentResolver;
this.messageSource = messageSource;
}
@Override
@ -63,7 +73,7 @@ public class AnnotationServiceImpl implements AnnotationService {
public Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException {
logger.debug(new MapLogEntry("persisting annotation").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditAnnotation);
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.entityAffiliation(model.getEntityId())), Permission.NewAnnotation);
AnnotationEntity data = new AnnotationEntity();
data.setId(UUID.randomUUID());
@ -91,7 +101,10 @@ public class AnnotationServiceImpl implements AnnotationService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting Annotation: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteAnnotation);
AnnotationEntity annotation = this.entityManager.find(AnnotationEntity.class, id);
if (annotation == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{id, Annotation.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.authorizationService.authorizeAtLeastOneForce(annotation.getSubjectId() != null ? List.of(new OwnedResource(annotation.getSubjectId())) : null, Permission.DeleteAnnotation);
this.deleterFactory.deleter(AnnotationDeleter.class).deleteAndSaveByIds(List.of(id));
}

View File

@ -16,7 +16,7 @@ import java.util.List;
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PermissionNameProvider {
private static final Logger logger = LoggerFactory.getLogger(DepositServiceImpl.class);
private static final Logger logger = LoggerFactory.getLogger(PermissionNameProvider.class);
private final List<String> permissions;
public PermissionNameProvider(ConventionService conventionService) {

View File

@ -28,6 +28,7 @@ public class OutboxIntegrationEvent extends IntegrationEvent {
public static final String DESCRIPTION_TOUCH = "DESCRIPTION_TOUCH";
public static final String ANNOTATION_ENTITY_TOUCH = "ANNOTATION_ENTITY_TOUCH";
public static final String ANNOTATION_ENTITY_REMOVE = "ANNOTATION_ENTITY_REMOVE";
public static final String WHAT_YOU_KNOW_ABOUT_ME_COMPLETED = "WHAT_YOU_KNOW_ABOUT_ME_COMPLETED";

View File

@ -23,7 +23,8 @@ public class OutboxProperties {
private final String descriptionTouchTopic;
private final String annotationEntityTouchTopic;
private final String annotationEntitiesTouchTopic;
private final String annotationEntitiesRemovalTopic;
private final String notifyTopic;
@ -42,7 +43,8 @@ public class OutboxProperties {
String userTouchTopic,
String dmpTouchTopic,
String descriptionTouchTopic,
String annotationEntityTouchTopic,
String annotationEntitiesTouchTopic,
String annotationEntitiesRemovalTopic,
String notifyTopic,
String forgetMeCompletedTopic,
String whatYouKnowAboutMeCompletedTopic,
@ -57,7 +59,8 @@ public class OutboxProperties {
this.userTouchTopic = userTouchTopic;
this.dmpTouchTopic = dmpTouchTopic;
this.descriptionTouchTopic = descriptionTouchTopic;
this.annotationEntityTouchTopic = annotationEntityTouchTopic;
this.annotationEntitiesTouchTopic = annotationEntitiesTouchTopic;
this.annotationEntitiesRemovalTopic = annotationEntitiesRemovalTopic;
this.notifyTopic = notifyTopic;
this.forgetMeCompletedTopic = forgetMeCompletedTopic;
this.whatYouKnowAboutMeCompletedTopic = whatYouKnowAboutMeCompletedTopic;
@ -100,8 +103,12 @@ public class OutboxProperties {
return descriptionTouchTopic;
}
public String getAnnotationEntityTouchTopic() {
return annotationEntityTouchTopic;
public String getAnnotationEntitiesTouchTopic() {
return annotationEntitiesTouchTopic;
}
public String getAnnotationEntitiesRemovalTopic() {
return annotationEntitiesRemovalTopic;
}
public String getNotifyTopic() {

View File

@ -416,7 +416,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
break;
}
case OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH: {
routingKey = this.outboxProperties.getAnnotationEntityTouchTopic();
routingKey = this.outboxProperties.getAnnotationEntitiesTouchTopic();
break;
}
case OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE: {
routingKey = this.outboxProperties.getAnnotationEntitiesRemovalTopic();
break;
}
case OutboxIntegrationEvent.FORGET_ME_COMPLETED: {

View File

@ -0,0 +1,19 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval;
import eu.eudat.integrationevent.TrackedEvent;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesRemovalIntegrationEvent extends TrackedEvent {
private List<UUID> entityIds;
public List<UUID> getEntityIds() {
return entityIds;
}
public void setEntityIds(List<UUID> entityIds) {
this.entityIds = entityIds;
}
}

View File

@ -0,0 +1,9 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval;
import java.util.UUID;
public interface AnnotationEntityRemovalIntegrationEventHandler {
void handleDescription(UUID descriptionId);
void handleDmp(UUID dmpId);
}

View File

@ -0,0 +1,67 @@
package eu.eudat.integrationevent.outbox.annotationentityremoval;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.model.Description;
import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntityRemovalIntegrationEventHandlerImpl implements AnnotationEntityRemovalIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntityRemovalIntegrationEventHandlerImpl.class));
private final OutboxService outboxService;
private final QueryFactory queryFactory;
public AnnotationEntityRemovalIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) {
this.outboxService = outboxService;
this.queryFactory = queryFactory;
}
private void handle(AnnotationEntitiesRemovalIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE);
message.setEvent(event);
this.outboxService.publish(message);
}
@Override
public void handleDescription(UUID descriptionId) {
AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent();
event.setEntityIds(List.of(descriptionId));
this.handle(event);
}
@Override
public void handleDmp(UUID dmpId) {
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id));
AnnotationEntitiesRemovalIntegrationEvent event = new AnnotationEntitiesRemovalIntegrationEvent();
event.setEntityIds(new ArrayList<>());
event.getEntityIds().add(dmpId);
for (DescriptionEntity description : descriptionEntities) event.getEntityIds().add(description.getId());
this.handle(event);
}
}

View File

@ -0,0 +1,43 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch;
import eu.eudat.integrationevent.TrackedEvent;
import java.util.List;
import java.util.UUID;
public class AnnotationEntitiesTouchedIntegrationEvent extends TrackedEvent {
private List<AnnotationEntityTouchedIntegrationEvent> events;
public List<AnnotationEntityTouchedIntegrationEvent> getEvents() {
return events;
}
public void setEvents(List<AnnotationEntityTouchedIntegrationEvent> events) {
this.events = events;
}
public static class AnnotationEntityTouchedIntegrationEvent {
private UUID entityId;
private List<UUID> userIds;
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
}
}

View File

@ -1,30 +0,0 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch;
import eu.eudat.integrationevent.TrackedEvent;
import java.util.List;
import java.util.UUID;
public class AnnotationEntityTouchedIntegrationEvent extends TrackedEvent {
private UUID entityId;
private List<UUID> userIds;
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public List<UUID> getUserIds() {
return userIds;
}
public void setUserIds(List<UUID> userIds) {
this.userIds = userIds;
}
}

View File

@ -9,17 +9,6 @@ import java.util.UUID;
public interface AnnotationEntityTouchedIntegrationEventHandler {
void handle(AnnotationEntityTouchedIntegrationEvent event);
static AnnotationEntityTouchedIntegrationEvent buildEventFromPersistModel(DmpPersist persist) {
AnnotationEntityTouchedIntegrationEvent event = new AnnotationEntityTouchedIntegrationEvent();
event.setEntityId(persist.getId());
List<UUID> users = new ArrayList<>();
persist.getUsers().forEach(dmpUserPersist -> {
users.add(dmpUserPersist.getUser());
});
event.setUserIds(users);
return event;
}
void handleDescription(UUID descriptionId);
void handleDmp(UUID dmpId);
}

View File

@ -1,13 +1,24 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.integrationevent.outbox.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService;
import eu.eudat.model.Description;
import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Component
@ -18,12 +29,14 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
private final OutboxService outboxService;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(OutboxService outboxService) {
private final QueryFactory queryFactory;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) {
this.outboxService = outboxService;
this.queryFactory = queryFactory;
}
@Override
public void handle(AnnotationEntityTouchedIntegrationEvent event) {
private void handle(AnnotationEntitiesTouchedIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH);
@ -31,4 +44,40 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
this.outboxService.publish(message);
}
@Override
public void handleDescription(UUID descriptionId) {
DescriptionEntity entity = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).firstAs(new BaseFieldSet().ensure(Description._dmp));
if (entity == null) return;
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(entity.getDmpId()).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user));
AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent();
event.setEvents(List.of(this.buildEventItem(descriptionId, dmpUsers)));
this.handle(event);
}
@Override
public void handleDmp(UUID dmpId) {
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).dmpIds(dmpId).collectAs(new BaseFieldSet().ensure(Description._id));
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._user));
AnnotationEntitiesTouchedIntegrationEvent event = new AnnotationEntitiesTouchedIntegrationEvent();
event.setEvents(new ArrayList<>());
event.getEvents().add(this.buildEventItem(dmpId, dmpUsers));
for (DescriptionEntity description : descriptionEntities) event.getEvents().add(this.buildEventItem(description.getId(), dmpUsers));
this.handle(event);
}
private AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent buildEventItem(UUID entityId, List<DmpUserEntity> dmpUsers){
AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent eventItem = new AnnotationEntitiesTouchedIntegrationEvent.AnnotationEntityTouchedIntegrationEvent();
eventItem.setEntityId(entityId);
List<UUID> users = new ArrayList<>();
for (DmpUserEntity dmpUser : dmpUsers){
users.add(dmpUser.getUserId());
}
eventItem.setUserIds(users);
return eventItem;
}
}

View File

@ -22,6 +22,8 @@ import eu.eudat.data.*;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.DescriptionTouchedEvent;
import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.annotationentityremoval.AnnotationEntityRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.descriptiontouched.DescriptionTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
@ -99,6 +101,8 @@ public class DescriptionServiceImpl implements DescriptionService {
private final StorageFileService storageFileService;
private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver;
private final AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler;
private final AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler;
@Autowired
public DescriptionServiceImpl(
@ -113,7 +117,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory,
JsonHandlingService jsonHandlingService,
UserScope userScope,
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver, AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -135,6 +139,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.storageFileService = storageFileService;
this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver;
this.annotationEntityTouchedIntegrationEventHandler = annotationEntityTouchedIntegrationEventHandler;
this.annotationEntityRemovalIntegrationEventHandler = annotationEntityRemovalIntegrationEventHandler;
}
@Override
@ -230,6 +236,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.descriptionTouchedIntegrationEventHandler.handle(DescriptionTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
this.elasticService.persistDescription(data);
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
}
@ -294,71 +302,6 @@ public class DescriptionServiceImpl implements DescriptionService {
return event;
}
// public List<eu.eudat.commons.types.descriptiontemplate.FieldEntity> getFieldById(String id){
// List<eu.eudat.commons.types.descriptiontemplate.FieldEntity> fieldEntities = new ArrayList<>();
// if (id == null || id.isBlank()) return fieldEntities;
// if (this.getFieldSets() != null){
// for (FieldSetEntity fieldSetEntity: this.getFieldSets()) {
// fieldEntities.addAll(fieldSetEntity.getFieldById(id));
// }
// }
// if (this.getSections() != null){
// for (SectionEntity sectionEntity: this.getSections()) {
// fieldEntities.addAll(sectionEntity.getFieldById(id));
// }
// }
// return fieldEntities;
// }
private void descriptionForce(DescriptionEntity description) throws Exception {
List<String> datasetProfileValidators = new LinkedList<>();
DescriptionTemplateEntity descriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, description.getDescriptionTemplateId());
if (descriptionTemplateEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{description.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
eu.eudat.commons.types.descriptiontemplate.DefinitionEntity descriptionTemplateDefinition = this.xmlHandlingService.fromXml(eu.eudat.commons.types.descriptiontemplate.DefinitionEntity.class, descriptionTemplateEntity.getDefinition());
// DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
// DocumentBuilder builder = builderFactory.newDocumentBuilder();
// Document xmlDocument = builder.parse(new ByteArrayInputStream(profile.getDefinition().getBytes()));
//
// XPath xPath = XPathFactory.newInstance().newXPath();
// String expression = "//validation/@type[.=1]/ancestor::field/@id";
// NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
//
// for (int i = 0; i < nodeList.getLength(); i++) {
// Node node = nodeList.item(i);
// datasetProfileValidators.add(node.getNodeValue());
// }
//
// expression = "//validation/@type[.=1]/ancestor::fieldSet";
// nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
//
//
// JSONObject obj = new JSONObject(description.getProperties());
// VisibilityRuleService visibilityRuleService = new VisibilityRuleServiceImpl();
// visibilityRuleService.setProperties(obj.toMap());
//
// description.setProfile(profile);
// PagedDatasetProfile pagedDatasetProfile = this.getPagedProfile(new DatasetWizardModel(), description);
// visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules());
//
//
// String failedField = null;
//
// for (String validator : datasetProfileValidators) {
// if (obj.has(validator) && isNullOrEmpty(obj.getString(validator)) && isElementVisible(nodeList, validator, visibilityRuleService)) {
// //throw new Exception("Field value of " + validator + " must be filled.");
// failedField = validator;
// break;
// }
// }
// return failedField;
}
@Override
public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException {
logger.debug(new MapLogEntry("persisting data dmp").And("model", model).And("fields", fields));
@ -385,6 +328,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.elasticService.persistDescription(data);
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId()));
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
}
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
}
@ -706,6 +651,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(id)), Permission.DeleteDescription);
this.deleterFactory.deleter(DescriptionDeleter.class).deleteAndSaveByIds(List.of(id), false);
this.annotationEntityRemovalIntegrationEventHandler.handleDescription(id);
}
//endregion
@ -774,6 +721,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.elasticService.persistDescription(newDescription);
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(newDescription.getId());
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(existing.getId());
}
//endregion

View File

@ -24,6 +24,7 @@ import eu.eudat.data.*;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.DmpTouchedEvent;
import eu.eudat.event.EventBroker;
import eu.eudat.integrationevent.outbox.annotationentityremoval.AnnotationEntityRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.dmptouched.DmpTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
@ -124,6 +125,7 @@ public class DmpServiceImpl implements DmpService {
private final DmpTouchedIntegrationEventHandler dmpTouchedIntegrationEventHandler;
private final AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler;
private final AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver;
@Autowired
@ -148,7 +150,7 @@ public class DmpServiceImpl implements DmpService {
ValidatorFactory validatorFactory,
ElasticService elasticService,
DmpTouchedIntegrationEventHandler dmpTouchedIntegrationEventHandler,
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -170,6 +172,7 @@ public class DmpServiceImpl implements DmpService {
this.elasticService = elasticService;
this.dmpTouchedIntegrationEventHandler = dmpTouchedIntegrationEventHandler;
this.annotationEntityTouchedIntegrationEventHandler = annotationEntityTouchedIntegrationEventHandler;
this.annotationEntityRemovalIntegrationEventHandler = annotationEntityRemovalIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver;
}
@ -202,13 +205,13 @@ public class DmpServiceImpl implements DmpService {
this.dmpTouchedIntegrationEventHandler.handle(DmpTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.annotationEntityTouchedIntegrationEventHandler.handle(AnnotationEntityTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.sendNotification(data);
this.assignUsers(data.getId(), this.inviteUserOrAssignUsers(data.getId(), model.getUsers(), false), null, false);
this.elasticService.persistDmp(data);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(data.getId());
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
}
@ -334,6 +337,8 @@ public class DmpServiceImpl implements DmpService {
this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false);
if (previousFinalized != null) this.elasticService.persistDmp(previousFinalized);
this.annotationEntityRemovalIntegrationEventHandler.handleDmp(data.getId());
}
@Override
@ -445,6 +450,9 @@ public class DmpServiceImpl implements DmpService {
this.elasticService.persistDmp(oldDmpEntity);
this.elasticService.persistDmp(newDmp);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(newDmp.getId());
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(oldDmpEntity.getId());
return this.builderFactory.builder(DmpBuilder.class).build(BaseFieldSet.build(fields, Dmp._id), newDmp);
}
@ -565,9 +573,10 @@ public class DmpServiceImpl implements DmpService {
this.entityManager.flush();
this.elasticService.persistDmp(newDmp);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(newDmp.getId());
DmpEntity resultingDmpEntity = this.queryFactory.query(DmpQuery.class).ids(newDmp.getId()).firstAs(fields);
return this.builderFactory.builder(DmpBuilder.class).build(fields, resultingDmpEntity);
}
@ -615,6 +624,9 @@ public class DmpServiceImpl implements DmpService {
.collect();
this.elasticService.persistDmp(dmpEntity);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmpEntity.getId());
return this.builderFactory.builder(DmpUserBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fieldSet, DmpUser._id, DmpUser._hash), persisted);
}
@ -636,6 +648,8 @@ public class DmpServiceImpl implements DmpService {
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDmpId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
this.elasticService.persistDmp(dmpEntity);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmpEntity.getId());
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
}
@ -727,8 +741,6 @@ public class DmpServiceImpl implements DmpService {
return data;
}
private @NotNull DmpBlueprintValueEntity buildDmpBlueprintValueEntity(DmpBlueprintValuePersist persist){
DmpBlueprintValueEntity data = new DmpBlueprintValueEntity();
if (persist == null) return data;
@ -738,8 +750,6 @@ public class DmpServiceImpl implements DmpService {
return data;
}
private @NotNull List<DmpReferencePersist> buildDmpReferencePersists(DmpPropertiesPersist persist){
List<DmpReferencePersist> dmpReferencePersists = new ArrayList<>();
if (persist.getDmpBlueprintValues() != null && !persist.getDmpBlueprintValues().isEmpty()){
@ -839,7 +849,6 @@ public class DmpServiceImpl implements DmpService {
this.entityManager.flush();
}
private void patchAndSaveTemplates(UUID id, List<DmpDescriptionTemplatePersist> models) throws InvalidApplicationException {
if (models == null) models = new ArrayList<>();
List<DmpDescriptionTemplateEntity> items = this.queryFactory.query(DmpDescriptionTemplateQuery.class).isActive(IsActive.Active).dmpIds(id).collect();
@ -942,6 +951,8 @@ public class DmpServiceImpl implements DmpService {
this.entityManager.flush();
this.elasticService.persistDmp(dmp);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmp.getId());
this.sendNotification(dmp);
}
@ -964,12 +975,16 @@ public class DmpServiceImpl implements DmpService {
this.updateVersionStatusAndSave(dmp, DmpStatus.Finalized, dmp.getStatus());
this.entityManager.flush();
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmp.getId());
this.sendNotification(dmp);
}
// invites
public void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
this.inviteUserOrAssignUsers(id, users, true);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(id);
}
@ -1074,7 +1089,6 @@ public class DmpServiceImpl implements DmpService {
return persist.getToken();
}
public void dmpInvitationAccept(String token) {
ActionConfirmationEntity action = this.queryFactory.query(ActionConfirmationQuery.class).tokens(token).types(ActionConfirmationType.DmpInvitation).isActive(IsActive.Active).first();
@ -1102,6 +1116,8 @@ public class DmpServiceImpl implements DmpService {
action.setStatus(ActionConfirmationStatus.Accepted);
this.entityManager.merge(action);
this.annotationEntityTouchedIntegrationEventHandler.handleDmp(dmpInvitation.getDmpId());
}
}

View File

@ -35,7 +35,8 @@ queue:
user-removal-topic: user.remove
dmp-touch-topic: dmp.touch
description-touch-topic: description.touch
annotation-entity-touch-topic: annotation.entity.touch
annotation-entities-touch-topic: annotation.entities.touch
annotation-entities-removal-topic: annotation.entities.remove
what-you-know-about-me-completed-topic: whatyouknowaboutme.completed
generate-file-topic: generate.file
rabbitmq:

View File

@ -1,456 +0,0 @@
// import { ApplicationRef, Injectable, NgZone } from '@angular/core';
// import { AbstractControl, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
// import { isNumeric } from '@app/utilities/enhancers/utils';
// import { Observable, Subject } from 'rxjs';
// import { VisibilityRule } from './models/visibility-rule';
// import { VisibilityRuleSource } from './models/visibility-rule-source';
// import { VisibilityRulesContext } from './models/visibility-rules-context';
// import { DescriptionTemplate, DescriptionTemplatePage, DescriptionTemplateSection } from '@app/core/model/description-template/description-template';
// import { Rule } from './models/rule';
// @Injectable()
// export class VisibilityRulesService {
// private readonly VISIBILITY_RULE_LOGIC: 'OR' | 'AND' = 'OR';
// private readonly DEFAULTVISIBILITY = false;
// private visibilityRuleContext: VisibilityRulesContext;
// private form: AbstractControl;
// public isVisibleMap: { [key: string]: boolean } = {};
// private elementVisibilityMapSubject = new Subject<{ [key: string]: boolean }>();
// private elementComputationalMap = new Map<String, Map<String, boolean>>(); /// keep saved the values of each form control validity value
// private _changeMade$ = new Subject<void>();
// // get isVisibleMap(): MapWithDefault {
// // // console.log('isVisibleMap');
// // return this.elementVisibilityMap;
// // }; /// keep saved the values of each form control validity value
// constructor(
// public applicationReference: ApplicationRef,
// public ngZone: NgZone
// ) {
// }
// getElementVisibilityMapObservable(): Observable<{ [key: string]: boolean }> {
// // this.isVisibleMap
// // console.log('getElementVisibilityMapObservable: ');
// return this.elementVisibilityMapSubject.asObservable();
// }
// public checkElementVisibility(id: string): boolean {
// //console.log('checkElementVisibility: ' + id);
// return true;
// // if (this.visibilityRuleContext.rules.filter(item => item.targetControlId === id).length === 0) { return true; }
// // console.log(this.elementVisibilityMap.has(id) ? this.elementVisibilityMap.get(id) : false);
// // return this.elementVisibilityMap.has(id) ? this.elementVisibilityMap.get(id) : false;
// }
// public buildVisibilityRules(item: Array<Rule>, form: AbstractControl) {
// this.visibilityRuleContext = new VisibilityRulesContext();
// this.visibilityRuleContext.buildVisibilityRuleContext(item || []);
// this.form = form;
// this.resetVisibilityRules();
// }
// public updateValueAndVisibility(id: string, value: any) {
// //console.log('updateValueAndVisibility: ' + id + ' value: ' + value);
// const visibilityRules = this.visibilityRuleContext.rules.filter(item => item.sourceVisibilityRules.filter(source => source.sourceControlId === id).length > 0);
// if (visibilityRules.length > 0) {
// visibilityRules.forEach(item => this.evaluateVisibility(item, value, id));
// this.elementVisibilityMapSubject.next(this.isVisibleMap);
// }
// }
// private evaluateVisibility(visibilityRule: VisibilityRule, value: any, sourceId: string) {// source controlId is the same
// //console.log('evaluateVisibility: ' + visibilityRule + ' value: ' + value + ' sourceId: ' + sourceId);
// const targetId = visibilityRule.targetControlId;
// const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map<String, boolean>();
// if (value instanceof Array) {
// const parsedSourceControlValues = visibilityRule.sourceVisibilityRules.map(e => this.parseValue(e.sourceControlValue));
// const parsedValues = value.map(e => this.parseValue(e));
// const isVisible = parsedValues.map(v => parsedSourceControlValues.includes(v)).reduce((acc, current) => acc || current, false);
// // if(isVisible){
// // this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
// // this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
// // return;
// // }
// visibilityMap.set(sourceId, isVisible);
// } else {
// const visibilityDependencySource = visibilityRule.sourceVisibilityRules.filter(x => x.sourceControlId === sourceId);
// const shouldBeVisible = visibilityDependencySource.reduce((isVisible, x) => {
// const shouldBeHidden = value !== null && (this.parseValue(value) !== this.parseValue(x.sourceControlValue));
// return this.VISIBILITY_RULE_LOGIC === 'OR' ? (isVisible || !shouldBeHidden) : (isVisible && !shouldBeHidden);
// // if(value !== null && )
// }, this.VISIBILITY_RULE_LOGIC === 'AND');
// visibilityMap.set(sourceId, shouldBeVisible);
// }
// this.elementComputationalMap.set(targetId, visibilityMap);// unnessecary
// const isVisible = this._computeVisibility(targetId);
// this._emitChangesIfNeeded(targetId, isVisible);
// const previousVisibility = this.isVisibleMap[targetId];
// this.isVisibleMap[targetId] = isVisible;
// if (!isVisible && previousVisibility !== isVisible) {
// this.resetControlWithId(this.form, targetId);
// }
// // for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) {
// // if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) {
// // this._emitChangesIfNeeded(visibilityRule.targetControlId, false);
// // this.elementVisibilityMap.set(visibilityRule.targetControlId, false);
// // this.resetControlWithId(this.form, visibilityRule.targetControlId);
// // //this.updateValueAndVisibility(visibilityRule.targetControlId, null);
// // // this.clearValues(targetPathKey);
// // return;
// // }
// // }
// // this._emitChangesIfNeeded(visibilityRule.targetControlId, true);
// // this.elementVisibilityMap.set(visibilityRule.targetControlId, true);
// // this.updateValueAndVisibility(visibilityRule.targetControlId, null);
// }
// private _computeVisibility(targetId: string): boolean {
// //console.log('_computeVisibility: ' + targetId);
// const visibilityMap = this.elementComputationalMap.get(targetId);
// const values = visibilityMap.values();
// let currentVal = values.next();
// let visibilityValues: boolean[] = [];
// while (!currentVal.done) {
// visibilityValues.push(currentVal.value);
// currentVal = values.next();
// }
// if (visibilityValues.length) {
// return visibilityValues.reduce((r, c) => {
// if (this.VISIBILITY_RULE_LOGIC === 'OR') {
// return r || c;
// } else {
// return r && c;
// }
// }, visibilityValues[0]);
// }
// return this.DEFAULTVISIBILITY;
// }
// private resetVisibilityRules() {
// //console.log('resetVisibilityRules: ');
// this.isVisibleMap = {};
// this.elementComputationalMap.clear();
// this.elementComputationalMap = new Map<String, Map<String, boolean>>();
// this._populateComputationMap(); /// !IMPORTANT FOR THE AND LOGIC
// this._changeMade$.next();
// }
// private _populateComputationMap(): void {
// //console.log('_populateComputationMap: ');
// // this.visibilityRuleContext.rules.forEach(rule => {
// // const targetId = rule.targetControlId;
// // const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map<String, boolean>();
// // rule.sourceVisibilityRules.forEach(vr => {
// // visibilityMap.set(vr.sourceControlId, this.DEFAULTVISIBILITY);
// // });
// // this.elementComputationalMap.set(targetId, visibilityMap);
// // });
// }
// parseValue(value: any) {
// if (typeof value === 'string') {
// if (isNumeric(value)) { return value; }
// else if (value === 'true') {
// return true;
// }
// else if (value === 'false') {
// return false;
// }
// else { return this.translate(value); }
// } else { return value; }
// }
// search(path, obj, target) {
// for (const k in obj) {
// if (obj.hasOwnProperty(k)) {
// if (obj[k] === target) {
// return path + '.' + k;
// } else if (typeof obj[k] === 'object') {
// const result = this.search(path + '.' + k, obj[k], target);
// if (result) {
// return result;
// }
// }
// }
// }
// return false;
// }
// scanIfChildsOfCompositeFieldHasVisibleItems(compositeFieldParent: UntypedFormGroup): boolean {
// // console.log('scanIfChildsOfCompositeFieldHasVisibleItems: ' + compositeFieldParent);
// //TODO: implement this
// return true;
// // let isVisible = false;
// // (<UntypedFormArray>(compositeFieldParent.get('fields'))).controls.forEach(element => {
// // if (this.checkElementVisibility(element.get('id').value)) {
// // isVisible = true;
// // }
// // });
// // return isVisible;
// }
// private translate(item: any) {
// try {
// return JSON.parse(item).value;
// } catch (error) {
// return item;
// }
// }
// private resetControlWithId(formControl: AbstractControl, id: string) {
// //'resetControlWithId: ' + id);
// //TODO: implement this
// // if (formControl instanceof UntypedFormGroup) {
// // if ((formControl as UntypedFormGroup).contains('id') && (formControl as UntypedFormGroup).contains('value') && (formControl as UntypedFormGroup).get('id').value === id) {
// // this.resetFieldFormGroup(formControl);
// // } if ((formControl as UntypedFormGroup).contains('id') && (formControl as UntypedFormGroup).contains('fields') && (formControl as UntypedFormGroup).get('id').value === id) {
// // this.resetCompositeFieldFormGroup(formControl);
// // } else {
// // Object.keys(formControl.controls).forEach(item => {
// // const control = formControl.get(item);
// // this.resetControlWithId(control, id);
// // });
// // }
// // } else if (formControl instanceof UntypedFormArray) {
// // formControl.controls.forEach(item => {
// // this.resetControlWithId(item, id);
// // });
// // }
// }
// private resetFieldFormGroup(formGroup: UntypedFormGroup) {
// //console.log('resetFieldFormGroup: ' + formGroup);
// //TODO: implement this
// // const renderStyle = formGroup.getRawValue().viewStyle.renderStyle;
// // if (renderStyle === DatasetProfileFieldViewStyle.Validation || renderStyle === DatasetProfileFieldViewStyle.DatasetIdentifier) {
// // formGroup.get('value').setValue({ identifier: '', type: '' });
// // } else {
// // formGroup.get('value').setValue(formGroup.get('defaultValue').value ? this.parseValue(formGroup.get('defaultValue').value.value) : undefined);
// // }
// }
// private resetCompositeFieldFormGroup(formGroup: UntypedFormGroup) {
// //console.log('resetCompositeFieldFormGroup: ' + formGroup);
// //TODO: implement this
// // (formGroup.get('fields') as UntypedFormArray).controls.forEach((element: UntypedFormGroup) => {
// // this.resetFieldFormGroup(element);
// // });
// // (formGroup.get('multiplicityItems') as UntypedFormArray).controls.splice(0);
// }
// private _emitChangesIfNeeded(id: string, valueToBeSet: boolean) {
// if (this.isVisibleMap[id]) {
// if (this.isVisibleMap[id] != valueToBeSet) {
// this._changeMade$.next();
// }
// } else {
// this._changeMade$.next();
// }
// }
// public get visibilityChange() {
// return this._changeMade$.asObservable();
// }
// public getVisibilityDependency(targetId: string): VisibilityRuleSource[] | null {
// //console.log('getVisibilityDependency: ' + targetId);
// return this.visibilityRuleContext.rules.reduce((hasDependency, rule) => {
// if (hasDependency) return hasDependency;
// if (rule.targetControlId === targetId) {
// return rule.sourceVisibilityRules;
// }
// return null;
// }, null) as VisibilityRuleSource[];
// }
// public getVisibilityTargets(sourceId: string): string[] {
// console.log('getVisibilityTargets: ' + sourceId);
// return this.visibilityRuleContext.rules.filter(x => {
// const result = x.sourceVisibilityRules.filter(y => y.sourceControlId === sourceId);
// return result.length;
// }).map(x => x.targetControlId);
// }
// // public appendVisibilityRule(rule: VisibilityRule): void{
// // const existingTargetRule = this.visibilityRuleContext.rules.find( r => r.targetControlId === rule.targetControlId);
// // if(existingTargetRule){
// // rule.sourceVisibilityRules.forEach(svr =>{
// // existingTargetRule.sourceVisibilityRules.push(svr);
// // });
// // }else{
// // this.visibilityRuleContext.rules.push(rule);
// // }
// // }
// //removes rule that has the specific id either as a source either as a target
// public removeAllIdReferences(id: string): void {
// //console.log('removeAllIdReferences: ' + id);
// // * Remove from visibility rues and visibility rules context
// //remove as a target
// const temp = this.visibilityRuleContext.rules.map((x, i) => (x.targetControlId === id) ? i : null);
// const indexes = temp.filter(x => x !== null);
// indexes.reverse().forEach(index => this.visibilityRuleContext.rules.splice(index, 1));
// this.isVisibleMap[id] = undefined;
// //remove as a source
// const tbd = this.visibilityRuleContext.rules.reduce((to_be_deleted, rule, ruleIdx) => {
// const idxs = rule.sourceVisibilityRules.map((x, i) => (x.sourceControlId === id) ? i : null).filter(x => x !== null);
// idxs.reverse().forEach(index => rule.sourceVisibilityRules.splice(index, 1));
// if (!rule.sourceVisibilityRules.length) {
// to_be_deleted.push(ruleIdx);
// }
// return to_be_deleted
// }, []);
// //clean up empty
// tbd.reverse().forEach(index => {
// this.visibilityRuleContext.rules.splice(index, 1);
// });
// // * Remove from computational map
// // as a target
// if (this.elementComputationalMap.get(id)) {
// this.elementComputationalMap.delete(id);
// }
// // as a source
// const keyIterator = this.elementComputationalMap.keys();
// let currentKey = keyIterator.next();
// while (!currentKey.done) {
// const currentVals = this.elementComputationalMap.get(currentKey.value);
// currentVals.delete(id);
// currentKey = keyIterator.next();
// }
// }
// public addNewRule(rule: Rule, currentVisibility = this.DEFAULTVISIBILITY): void {
// //console.log('addNewRule: ' + rule + ' currentVisibility: ' + currentVisibility);
// const targetId = rule.targetField;
// const sourceId = rule.sourceField;
// this.visibilityRuleContext.addToVisibilityRulesContext(rule);
// let visibilityMap = this.elementComputationalMap.get(targetId);
// if (!visibilityMap) {
// visibilityMap = new Map<String, boolean>();
// this.elementComputationalMap.set(targetId, visibilityMap);
// }
// visibilityMap.set(sourceId, currentVisibility);
// const isVisible = this._computeVisibility(targetId);
// this._emitChangesIfNeeded(targetId, isVisible);
// this.isVisibleMap[targetId] = isVisible;
// this.elementVisibilityMapSubject.next(this.isVisibleMap);
// }
// /**
// * Check what sourceId hides or shows the target field
// * return true if no rule found
// */
// public checkTargetVisibilityProvidedBySource(sourceId: string, targetId: string): boolean {
// //console.log('checkTargetVisibilityProvidedBySource: ' + sourceId + ' targetId: ' + targetId);
// const computationalMap = this.elementComputationalMap.get(targetId);
// if (computationalMap) {
// return !!computationalMap.get(sourceId);
// }
// return true;
// }
// public getVisibilityRulesFromDescriptionTempalte(descriptionTemplate: DescriptionTemplate): Rule[] {
// //console.log('getVisibilityRulesFromDescriptionTempalte: ' + descriptionTemplate);
// const result: Rule[] = this.getVisibilityRulesFromDescriptionTempalteSections(descriptionTemplate?.definition?.pages);
// return result;
// }
// public getVisibilityRulesFromDescriptionTempalteSections(pages: DescriptionTemplatePage[]): Rule[] {
// //console.log('getVisibilityRulesFromDescriptionTempalteSections: ' + sections);
// const result: Rule[] = [];
// pages.forEach(page => {
// page?.sections?.forEach(section => {
// if (section.sections != null) { result.push(...this.getVisibilityRulesFromDescriptionTempalteSections(section.sections)); };
// section?.fieldSets?.forEach(fieldSet => {
// fieldSet?.fields?.forEach(field => {
// field.visibilityRules?.forEach(visibilityRule => {
// result.push({
// sourceField: field.id.toString(),
// targetField: visibilityRule.target,
// dateValue: visibilityRule.dateValue,
// textValue: visibilityRule.textValue,
// textListValue: visibilityRule.textListValue
// })
// });
// });
// });
// });
// });
// return result;
// }
// }
// class MapWithDefault extends Map<string, boolean> {
// get(key) {
// //console.log('MapWithDefault');
// if (!this.has(key)) {
// this.set(key, true);
// }
// return super.get(key);
// }
// }

View File

@ -263,7 +263,7 @@ export class VisibilityRulesService {
const fieldType: DescriptionTemplateFieldType = rule.field != null && rule.field.data != null ? rule.field.data.fieldType : DescriptionTemplateFieldType.FREE_TEXT;
if ([DescriptionTemplateFieldType.FREE_TEXT, DescriptionTemplateFieldType.CHECK_BOX, DescriptionTemplateFieldType.TEXT_AREA,
DescriptionTemplateFieldType.RICH_TEXT_AREA, DescriptionTemplateFieldType.UPLOAD, DescriptionTemplateFieldType.BOOLEAN_DECISION,
DescriptionTemplateFieldType.RADIO_BOX, DescriptionTemplateFieldType.CURRENCY, DescriptionTemplateFieldType.SELECT].includes(fieldType) && field.textValue != null && field.textValue.length > 0) {
DescriptionTemplateFieldType.RADIO_BOX, DescriptionTemplateFieldType.CURRENCY].includes(fieldType) && field.textValue != null && field.textValue.length > 0) {
if (DescriptionTemplateFieldType.UPLOAD == fieldType){
return false; //not apply visibility logic
} else {