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

View File

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

View File

@ -32,6 +32,12 @@ cache:
maximumSize: 500 maximumSize: 500
enableRecordStats: false enableRecordStats: false
expireAfterWriteSeconds: 320 expireAfterWriteSeconds: 320
- names: [ "affiliation" ]
allowNullValues: true
initialCapacity: 100
maximumSize: 5000
enableRecordStats: false
expireAfterWriteSeconds: 20
mapCaches: mapCaches:
apiKey: apiKey:
name: apikey name: apikey
@ -48,3 +54,6 @@ cache:
userAllowedTenant: userAllowedTenant:
name: userAccessTenant name: userAccessTenant
keyPattern: user_access_tenant_$user_id$_$tenant_id$:v0 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: permissions:
extendedClaims: [ ]
policies: policies:
DeferredAffiliation:
roles:
- Admin
- User
- Manager
- DescriptionTemplateEditor
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Tenants # Tenants
BrowseTenant: BrowseTenant:
roles: roles:
@ -49,30 +56,18 @@ permissions:
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: 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 #Annotation
BrowseAnnotation: BrowseAnnotation:
roles: roles:
- Admin - Admin
entityAffiliated: true
clients: [ ]
allowAnonymous: true
allowAuthenticated: false
NewAnnotation:
roles:
- Admin
entityAffiliated: true
clients: [ ] clients: [ ]
allowAnonymous: true allowAnonymous: true
allowAuthenticated: false allowAuthenticated: false
@ -85,6 +80,7 @@ permissions:
DeleteAnnotation: DeleteAnnotation:
roles: roles:
- Admin - Admin
entityAffiliated: false
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
@ -101,5 +97,3 @@ permissions:
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
# ViewPage Permissions

View File

@ -45,7 +45,8 @@ queue:
tenant-touch-topic: tenant.touch tenant-touch-topic: tenant.touch
user-removal-topic: user.remove user-removal-topic: user.remove
user-touch-topic: user.touch 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: rabbitmq:
enable: false enable: false
interval-seconds: 30 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; import java.util.EnumSet;
public enum AuthorizationFlags { public enum AuthorizationFlags {
None, Permission, Owner; None, Permission, Associated, Owner;
public static final EnumSet<AuthorizationFlags> OwnerOrPermission = EnumSet.of(Owner, Permission); public static final EnumSet<AuthorizationFlags> OwnerOrPermissionAssociated = EnumSet.of(Owner, Permission, Associated);
} }

View File

@ -1,15 +1,12 @@
package gr.cite.annotation.authorization; package gr.cite.annotation.authorization;
public final class Permission { public final class Permission {
public static String DeferredAffiliation = "DeferredAffiliation";
//User //User
public static String BrowseUser = "BrowseUser"; public static String BrowseUser = "BrowseUser";
public static String EditUser = "EditUser"; public static String EditUser = "EditUser";
public static String DeleteUser = "DeleteUser"; public static String DeleteUser = "DeleteUser";
//UserContactInfo
public static String BrowseUserContactInfo = "BrowseUserContactInfo";
public static String EditUserContactInfo = "EditUserContactInfo";
public static String DeleteUserContactInfo = "DeleteUserContactInfo";
//Tenant //Tenant
public static String BrowseTenant = "BrowseTenant"; public static String BrowseTenant = "BrowseTenant";
@ -19,13 +16,12 @@ public final class Permission {
//Annotation //Annotation
public static final String BrowseAnnotation = "BrowseAnnotation"; public static final String BrowseAnnotation = "BrowseAnnotation";
public static String NewAnnotation = "NewAnnotation";
public static String EditAnnotation = "EditAnnotation"; public static String EditAnnotation = "EditAnnotation";
public static String DeleteAnnotation = "DeleteAnnotation"; public static String DeleteAnnotation = "DeleteAnnotation";
public static final String BrowseTenantConfiguration = "BrowseTenantConfiguration"; public static final String BrowseTenantConfiguration = "BrowseTenantConfiguration";
public static final String EditTenantConfiguration = "EditTenantConfiguration"; 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.convention.ConventionService;
import gr.cite.annotation.errorcode.ErrorThesaurusProperties; import gr.cite.annotation.errorcode.ErrorThesaurusProperties;
import gr.cite.annotation.integrationevent.inbox.annotationentitytouch.AnnotationEntityTouchedIntegrationEvent;
import gr.cite.tools.validation.specification.Specification; import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource; 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.getTenantTouchTopic());
bindingItems.addAll(this.inboxProperties.getUserRemovalTopic()); bindingItems.addAll(this.inboxProperties.getUserRemovalTopic());
bindingItems.addAll(this.inboxProperties.getUserTouchTopic()); bindingItems.addAll(this.inboxProperties.getUserTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntityTouchTopic()); bindingItems.addAll(this.inboxProperties.getAnnotationEntitiesTouchTopic());
bindingItems.addAll(this.inboxProperties.getAnnotationEntitiesRemovalTopic());
return new InboxBindings(bindingItems); return new InboxBindings(bindingItems);
} }

View File

@ -17,7 +17,8 @@ public class InboxProperties {
private final List<String> userTouchTopic; private final List<String> userTouchTopic;
private final List<String> annotationEntityTouchTopic; private final List<String> annotationEntitiesTouchTopic;
private final List<String> annotationEntitiesRemovalTopic;
public InboxProperties( public InboxProperties(
String exchange, String exchange,
@ -25,13 +26,14 @@ public class InboxProperties {
List<String> tenantTouchTopic, List<String> tenantTouchTopic,
List<String> userRemovalTopic, List<String> userRemovalTopic,
List<String> userTouchTopic, List<String> userTouchTopic,
List<String> annotationEntityTouchTopic) { List<String> annotationEntitiesTouchTopic, List<String> annotationEntitiesRemovalTopic) {
this.exchange = exchange; this.exchange = exchange;
this.tenantRemovalTopic = tenantRemovalTopic; this.tenantRemovalTopic = tenantRemovalTopic;
this.tenantTouchTopic = tenantTouchTopic; this.tenantTouchTopic = tenantTouchTopic;
this.userRemovalTopic = userRemovalTopic; this.userRemovalTopic = userRemovalTopic;
this.userTouchTopic = userTouchTopic; this.userTouchTopic = userTouchTopic;
this.annotationEntityTouchTopic = annotationEntityTouchTopic; this.annotationEntitiesTouchTopic = annotationEntitiesTouchTopic;
this.annotationEntitiesRemovalTopic = annotationEntitiesRemovalTopic;
} }
public List<String> getTenantRemovalTopic() { public List<String> getTenantRemovalTopic() {
@ -50,8 +52,12 @@ public class InboxProperties {
return userTouchTopic; return userTouchTopic;
} }
public List<String> getAnnotationEntityTouchTopic() { public List<String> getAnnotationEntitiesTouchTopic() {
return annotationEntityTouchTopic; return annotationEntitiesTouchTopic;
}
public List<String> getAnnotationEntitiesRemovalTopic() {
return annotationEntitiesRemovalTopic;
} }
public String getExchange() { 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.common.scope.fake.FakeRequestScope;
import gr.cite.annotation.data.QueueInboxEntity; import gr.cite.annotation.data.QueueInboxEntity;
import gr.cite.annotation.integrationevent.TrackedEvent; 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.tenantremoval.TenantRemovalIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.tenanttouch.TenantTouchedIntegrationEventHandler; import gr.cite.annotation.integrationevent.inbox.tenanttouch.TenantTouchedIntegrationEventHandler;
import gr.cite.annotation.integrationevent.inbox.userremoval.UserRemovalIntegrationEventHandler; import gr.cite.annotation.integrationevent.inbox.userremoval.UserRemovalIntegrationEventHandler;
@ -332,8 +333,10 @@ public class InboxRepositoryImpl implements InboxRepository {
handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class); handler = this.applicationContext.getBean(UserRemovalIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchTopic())) else if (this.routingKeyMatched(routingKey, this.inboxProperties.getUserTouchTopic()))
handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class); handler = this.applicationContext.getBean(UserTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntityTouchTopic())) else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesTouchTopic()))
handler = this.applicationContext.getBean(AnnotationEntityTouchedIntegrationEventHandler.class); handler = this.applicationContext.getBean(AnnotationEntitiesTouchedIntegrationEventHandler.class);
else if (this.routingKeyMatched(routingKey, this.inboxProperties.getAnnotationEntitiesRemovalTopic()))
handler = this.applicationContext.getBean(AnnotationEntitiesRemovalIntegrationEventHandler.class);
else { else {
logger.error("No handler found for message routing key '{}'. Discarding.", routingKey); logger.error("No handler found for message routing key '{}'. Discarding.", routingKey);
handler = null; 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.audit.AuditableAction;
import gr.cite.annotation.common.JsonHandlingService; 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.annotation.query.EntityUserQuery;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.tools.auditing.AuditService; import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
@ -24,17 +25,13 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Instant; import java.util.*;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Component @Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) @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; private final JsonHandlingService jsonHandlingService;
@ -44,7 +41,7 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
private final QueryFactory queryFactory; 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.jsonHandlingService = jsonHandlingService;
this.validatorFactory = validatorFactory; this.validatorFactory = validatorFactory;
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
@ -53,66 +50,41 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
@Override @Override
public EventProcessingStatus handle(IntegrationEventProperties properties, String message) { 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) if (event == null)
return EventProcessingStatus.Error; 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; EntityManager entityManager = null;
EntityTransaction transaction = null; EntityTransaction transaction = null;
try (FakeRequestScope ignored = new FakeRequestScope()) { try (FakeRequestScope ignored = new FakeRequestScope()) {
try { 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 currentPrincipalResolver = this.applicationContext.getBean(CurrentPrincipalResolver.class);
currentPrincipalResolver.push(InboxPrincipal.build(properties)); currentPrincipalResolver.push(InboxPrincipal.build(properties));
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class); EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
entityManager = entityManagerFactory.createEntityManager(); entityManager = entityManagerFactory.createEntityManager();
DeleterFactory deleterFactory = this.applicationContext.getBean(DeleterFactory.class);
transaction = entityManager.getTransaction(); transaction = entityManager.getTransaction();
transaction.begin(); transaction.begin();
try { try {
EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class); EntityUserQuery entityUserQuery = this.queryFactory.query(EntityUserQuery.class);
List<EntityUserEntity> associatedUsersEntities = entityUserQuery List<EntityUserEntity> items = entityUserQuery
.entityIds(event.getEntityId()) .entityIds(event.getEntityIds())
.isActive(IsActive.Active) .isActive(IsActive.Active)
.collect(); .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); 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; 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 jakarta.persistence.Convert;
import java.time.Instant; import java.time.Instant;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public class Annotation { public class Annotation {
@ -67,6 +68,13 @@ public class Annotation {
public static final String _isActive = "isActive"; 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() { public UUID getId() {
return id; return id;
} }
@ -179,4 +187,19 @@ public class Annotation {
this.isActive = isActive; 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; package gr.cite.annotation.model.builder;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.data.AnnotationEntity; import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.model.Annotation; import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.AnnotationAuthor; import gr.cite.annotation.model.AnnotationAuthor;
import gr.cite.annotation.model.User; import gr.cite.annotation.model.User;
import gr.cite.annotation.query.UserQuery; 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.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
@ -29,13 +32,17 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final AuthorizationContentResolver authorizationContentResolver;
private final AuthorizationService authorizationService;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); 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))); super(conventionService, new LoggerService(LoggerFactory.getLogger(AnnotationBuilder.class)));
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.authorizationContentResolver = authorizationContentResolver;
this.authorizationService = authorizationService;
} }
public AnnotationBuilder authorize(EnumSet<AuthorizationFlags> values) { public AnnotationBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -51,42 +58,32 @@ public class AnnotationBuilder extends BaseBuilder<Annotation, AnnotationEntity>
return new ArrayList<>(); return new ArrayList<>();
List<Annotation> models = new ArrayList<>(); List<Annotation> models = new ArrayList<>();
if (data == null) return models;
FieldSet authorFields = fields.extractPrefixed(this.asPrefix(Annotation._author)); FieldSet authorFields = fields.extractPrefixed(this.asPrefix(Annotation._author));
Map<UUID, AnnotationAuthor> authorsMap = this.collectAuthors(authorFields, data); 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) { for (AnnotationEntity d : data) {
Annotation m = new Annotation(); Annotation m = new Annotation();
if (fields.hasField(this.asIndexer(Annotation._id))) if (fields.hasField(this.asIndexer(Annotation._id))) m.setId(d.getId());
m.setId(d.getId()); if (fields.hasField(this.asIndexer(Annotation._entityId))) m.setEntityId(d.getEntityId());
if (fields.hasField(this.asIndexer(Annotation._entityId))) if (fields.hasField(this.asIndexer(Annotation._entityType))) m.setEntityType(d.getEntityType());
m.setEntityId(d.getEntityId()); if (fields.hasField(this.asIndexer(Annotation._anchor))) m.setAnchor(d.getAnchor());
if (fields.hasField(this.asIndexer(Annotation._entityType))) if (fields.hasField(this.asIndexer(Annotation._payload))) m.setPayload(d.getPayload());
m.setEntityType(d.getEntityType()); if (fields.hasField(this.asIndexer(Annotation._subjectId))) m.setSubjectId(d.getSubjectId());
if (fields.hasField(this.asIndexer(Annotation._anchor))) if (fields.hasField(this.asIndexer(Annotation._threadId))) m.setThreadId(d.getThreadId());
m.setAnchor(d.getAnchor()); if (fields.hasField(this.asIndexer(Annotation._parentId))) m.setParentId(d.getParentId());
if (fields.hasField(this.asIndexer(Annotation._payload))) if (fields.hasField(this.asIndexer(Annotation._protectionType))) m.setProtectionType(d.getProtectionType());
m.setPayload(d.getPayload()); if (fields.hasField(this.asIndexer(Annotation._timeStamp))) m.setTimeStamp(d.getTimeStamp());
if (fields.hasField(this.asIndexer(Annotation._subjectId))) if (fields.hasField(this.asIndexer(Annotation._createdAt))) m.setCreatedAt(d.getCreatedAt());
m.setSubjectId(d.getSubjectId()); if (fields.hasField(this.asIndexer(Annotation._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
if (authorsMap != null && authorsMap.containsKey(d.getSubjectId())) if (fields.hasField(this.asIndexer(Annotation._isActive))) m.setIsActive(d.getIsActive());
m.setAuthor(authorsMap.get(d.getSubjectId())); if (fields.hasField(this.asIndexer(Annotation._hash))) m.setHash(this.hashValue(d.getUpdatedAt()));
if (fields.hasField(this.asIndexer(Annotation._threadId))) if (!authorFields.isEmpty() && authorsMap != null && authorsMap.containsKey(d.getSubjectId())) m.setAuthor(authorsMap.get(d.getSubjectId()));
m.setThreadId(d.getThreadId()); if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null)));
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());
models.add(m); models.add(m);
} }
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0)); 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) .map(AnnotationEntity::getSubjectId)
.distinct() .distinct()
.collect(Collectors.toList()); .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); Map<UUID, List<User>> users = this.builderFactory.builder(UserBuilder.class).authorize(this.authorize).asMasterKey(query, clone, User::getId);
users.forEach((key, val) -> { users.forEach((key, val) -> {

View File

@ -1,6 +1,8 @@
package gr.cite.annotation.model.builder; package gr.cite.annotation.model.builder;
import gr.cite.annotation.authorization.AffiliatedResource;
import gr.cite.annotation.convention.ConventionService; 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.builder.Builder;
import gr.cite.tools.data.query.QueryBase; import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
@ -92,4 +94,25 @@ public abstract class BaseBuilder<M, D> implements Builder {
return this.conventionService.asIndexer(names); 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.authorization.Permission;
import gr.cite.annotation.convention.ConventionService; import gr.cite.annotation.convention.ConventionService;
import gr.cite.annotation.model.Annotation;
import gr.cite.commons.web.authz.service.AuthorizationService; 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.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry; import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
@ -21,9 +23,12 @@ public class AnnotationCensor extends BaseCensor{
protected final AuthorizationService authService; protected final AuthorizationService authService;
public AnnotationCensor(ConventionService conventionService, AuthorizationService authService) { protected final CensorFactory censorFactory;
public AnnotationCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) {
super(conventionService); super(conventionService);
this.authService = authService; this.authService = authService;
this.censorFactory = censorFactory;
} }
public void censor(FieldSet fields, UUID userId) { public void censor(FieldSet fields, UUID userId) {
@ -31,7 +36,10 @@ public class AnnotationCensor extends BaseCensor{
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; 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; package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags; 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.AnnotationProtectionType;
import gr.cite.annotation.common.enums.IsActive; import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope; import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity; import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.EntityUserEntity;
import gr.cite.annotation.model.Annotation; 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.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver; import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase; 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.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -44,10 +50,13 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
private final UserScope userScope; private final UserScope userScope;
private final QueryUtilsService queryUtilsService;
private final AuthorizationService authService; private final AuthorizationService authService;
public AnnotationQuery(UserScope userScope, AuthorizationService authService) { public AnnotationQuery(UserScope userScope, QueryUtilsService queryUtilsService, AuthorizationService authService) {
this.userScope = userScope; this.userScope = userScope;
this.queryUtilsService = queryUtilsService;
this.authService = authService; this.authService = authService;
} }
@ -161,6 +170,11 @@ public class AnnotationQuery extends QueryBase<AnnotationEntity> {
return this; return this;
} }
public AnnotationQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override @Override
protected Boolean isFalseQuery() { protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.excludedIds) || this.isEmpty(this.isActives) || this.isEmpty(this.entityIds); 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; 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 @Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) { protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();

View File

@ -1,8 +1,16 @@
package gr.cite.annotation.query; package gr.cite.annotation.query;
import gr.cite.annotation.authorization.AuthorizationFlags; 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.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.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.FieldResolver;
import gr.cite.tools.data.query.QueryBase; import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext; import gr.cite.tools.data.query.QueryContext;
@ -12,6 +20,7 @@ import jakarta.persistence.criteria.Predicate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope; import org.springframework.web.context.annotation.RequestScope;
import java.time.Instant;
import java.util.*; import java.util.*;
@Component @Component
@ -20,10 +29,19 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
private Collection<UUID> ids, entityIds, userIds; 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); 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) { public EntityUserQuery ids(UUID value) {
this.ids = List.of(value); this.ids = List.of(value);
return this; return this;
@ -98,6 +116,26 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
protected Class<EntityUserEntity> entityClass() { protected Class<EntityUserEntity> entityClass() {
return EntityUserEntity.class; 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 @Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) { protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
@ -136,11 +174,35 @@ public class EntityUserQuery extends QueryBase<EntityUserEntity> {
@Override @Override
protected String fieldNameOf(FieldResolver item) { 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; return null;
} }
@Override @Override
protected EntityUserEntity convert(Tuple tuple, Set<String> columns) { 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; package gr.cite.annotation.service.annotation;
import gr.cite.annotation.authorization.AuthorizationFlags; import gr.cite.annotation.authorization.AuthorizationFlags;
import gr.cite.annotation.authorization.OwnedResource;
import gr.cite.annotation.authorization.Permission; 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.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope; import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity; import gr.cite.annotation.data.AnnotationEntity;
@ -23,6 +25,8 @@ import gr.cite.tools.logging.MapLogEntry;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
@ -45,17 +49,23 @@ public class AnnotationServiceImpl implements AnnotationService {
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final UserScope userScope; private final UserScope userScope;
private final AuthorizationContentResolver authorizationContentResolver;
private final MessageSource messageSource;
public AnnotationServiceImpl( public AnnotationServiceImpl(
AuthorizationService authorizationService, AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
EntityManager entityManager, EntityManager entityManager,
BuilderFactory builderFactory, UserScope userScope) { BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
this.entityManager = entityManager; this.entityManager = entityManager;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.userScope = userScope; this.userScope = userScope;
this.authorizationContentResolver = authorizationContentResolver;
this.messageSource = messageSource;
} }
@Override @Override
@ -63,7 +73,7 @@ public class AnnotationServiceImpl implements AnnotationService {
public Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException { public Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException {
logger.debug(new MapLogEntry("persisting annotation").And("model", model).And("fields", fields)); 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(); AnnotationEntity data = new AnnotationEntity();
data.setId(UUID.randomUUID()); data.setId(UUID.randomUUID());
@ -91,7 +101,10 @@ public class AnnotationServiceImpl implements AnnotationService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting Annotation: {}", id); 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)); this.deleterFactory.deleter(AnnotationDeleter.class).deleteAndSaveByIds(List.of(id));
} }

View File

@ -16,7 +16,7 @@ import java.util.List;
@Service @Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class PermissionNameProvider { 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; private final List<String> permissions;
public PermissionNameProvider(ConventionService conventionService) { 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 DESCRIPTION_TOUCH = "DESCRIPTION_TOUCH";
public static final String ANNOTATION_ENTITY_TOUCH = "ANNOTATION_ENTITY_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"; 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 descriptionTouchTopic;
private final String annotationEntityTouchTopic; private final String annotationEntitiesTouchTopic;
private final String annotationEntitiesRemovalTopic;
private final String notifyTopic; private final String notifyTopic;
@ -42,7 +43,8 @@ public class OutboxProperties {
String userTouchTopic, String userTouchTopic,
String dmpTouchTopic, String dmpTouchTopic,
String descriptionTouchTopic, String descriptionTouchTopic,
String annotationEntityTouchTopic, String annotationEntitiesTouchTopic,
String annotationEntitiesRemovalTopic,
String notifyTopic, String notifyTopic,
String forgetMeCompletedTopic, String forgetMeCompletedTopic,
String whatYouKnowAboutMeCompletedTopic, String whatYouKnowAboutMeCompletedTopic,
@ -57,7 +59,8 @@ public class OutboxProperties {
this.userTouchTopic = userTouchTopic; this.userTouchTopic = userTouchTopic;
this.dmpTouchTopic = dmpTouchTopic; this.dmpTouchTopic = dmpTouchTopic;
this.descriptionTouchTopic = descriptionTouchTopic; this.descriptionTouchTopic = descriptionTouchTopic;
this.annotationEntityTouchTopic = annotationEntityTouchTopic; this.annotationEntitiesTouchTopic = annotationEntitiesTouchTopic;
this.annotationEntitiesRemovalTopic = annotationEntitiesRemovalTopic;
this.notifyTopic = notifyTopic; this.notifyTopic = notifyTopic;
this.forgetMeCompletedTopic = forgetMeCompletedTopic; this.forgetMeCompletedTopic = forgetMeCompletedTopic;
this.whatYouKnowAboutMeCompletedTopic = whatYouKnowAboutMeCompletedTopic; this.whatYouKnowAboutMeCompletedTopic = whatYouKnowAboutMeCompletedTopic;
@ -100,8 +103,12 @@ public class OutboxProperties {
return descriptionTouchTopic; return descriptionTouchTopic;
} }
public String getAnnotationEntityTouchTopic() { public String getAnnotationEntitiesTouchTopic() {
return annotationEntityTouchTopic; return annotationEntitiesTouchTopic;
}
public String getAnnotationEntitiesRemovalTopic() {
return annotationEntitiesRemovalTopic;
} }
public String getNotifyTopic() { public String getNotifyTopic() {

View File

@ -416,7 +416,11 @@ public class OutboxRepositoryImpl implements OutboxRepository {
break; break;
} }
case OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH: { case OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH: {
routingKey = this.outboxProperties.getAnnotationEntityTouchTopic(); routingKey = this.outboxProperties.getAnnotationEntitiesTouchTopic();
break;
}
case OutboxIntegrationEvent.ANNOTATION_ENTITY_REMOVE: {
routingKey = this.outboxProperties.getAnnotationEntitiesRemovalTopic();
break; break;
} }
case OutboxIntegrationEvent.FORGET_ME_COMPLETED: { 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 { public interface AnnotationEntityTouchedIntegrationEventHandler {
void handle(AnnotationEntityTouchedIntegrationEvent event); void handleDescription(UUID descriptionId);
void handleDmp(UUID dmpId);
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;
}
} }

View File

@ -1,13 +1,24 @@
package eu.eudat.integrationevent.outbox.annotationentitytouch; 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.OutboxIntegrationEvent;
import eu.eudat.integrationevent.outbox.OutboxService; 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 gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Component @Component
@ -18,12 +29,14 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
private final OutboxService outboxService; private final OutboxService outboxService;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(OutboxService outboxService) { private final QueryFactory queryFactory;
public AnnotationEntityTouchedIntegrationEventHandlerImpl(OutboxService outboxService, QueryFactory queryFactory) {
this.outboxService = outboxService; this.outboxService = outboxService;
this.queryFactory = queryFactory;
} }
@Override private void handle(AnnotationEntitiesTouchedIntegrationEvent event) {
public void handle(AnnotationEntityTouchedIntegrationEvent event) {
OutboxIntegrationEvent message = new OutboxIntegrationEvent(); OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID()); message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH); message.setType(OutboxIntegrationEvent.ANNOTATION_ENTITY_TOUCH);
@ -31,4 +44,40 @@ public class AnnotationEntityTouchedIntegrationEventHandlerImpl implements Annot
this.outboxService.publish(message); 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.errorcode.ErrorThesaurusProperties;
import eu.eudat.event.DescriptionTouchedEvent; import eu.eudat.event.DescriptionTouchedEvent;
import eu.eudat.event.EventBroker; 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.descriptiontouched.DescriptionTouchedIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
@ -99,6 +101,8 @@ public class DescriptionServiceImpl implements DescriptionService {
private final StorageFileService storageFileService; private final StorageFileService storageFileService;
private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler; private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver; private final AuthorizationContentResolver authorizationContentResolver;
private final AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler;
private final AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler;
@Autowired @Autowired
public DescriptionServiceImpl( public DescriptionServiceImpl(
@ -113,7 +117,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory, QueryFactory queryFactory,
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
UserScope userScope, 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.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -135,6 +139,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.storageFileService = storageFileService; this.storageFileService = storageFileService;
this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler; this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver; this.authorizationContentResolver = authorizationContentResolver;
this.annotationEntityTouchedIntegrationEventHandler = annotationEntityTouchedIntegrationEventHandler;
this.annotationEntityRemovalIntegrationEventHandler = annotationEntityRemovalIntegrationEventHandler;
} }
@Override @Override
@ -230,6 +236,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.descriptionTouchedIntegrationEventHandler.handle(DescriptionTouchedIntegrationEventHandler.buildEventFromPersistModel(model)); this.descriptionTouchedIntegrationEventHandler.handle(DescriptionTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
this.elasticService.persistDescription(data); this.elasticService.persistDescription(data);
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), 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; 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 @Override
public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException { public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException {
logger.debug(new MapLogEntry("persisting data dmp").And("model", model).And("fields", fields)); 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.elasticService.persistDescription(data);
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId())); 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); 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.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(id)), Permission.DeleteDescription);
this.deleterFactory.deleter(DescriptionDeleter.class).deleteAndSaveByIds(List.of(id), false); this.deleterFactory.deleter(DescriptionDeleter.class).deleteAndSaveByIds(List.of(id), false);
this.annotationEntityRemovalIntegrationEventHandler.handleDescription(id);
} }
//endregion //endregion
@ -774,6 +721,8 @@ public class DescriptionServiceImpl implements DescriptionService {
this.elasticService.persistDescription(newDescription); this.elasticService.persistDescription(newDescription);
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(newDescription.getId());
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(existing.getId());
} }
//endregion //endregion

View File

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

View File

@ -35,7 +35,8 @@ queue:
user-removal-topic: user.remove user-removal-topic: user.remove
dmp-touch-topic: dmp.touch dmp-touch-topic: dmp.touch
description-touch-topic: description.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 what-you-know-about-me-completed-topic: whatyouknowaboutme.completed
generate-file-topic: generate.file generate-file-topic: generate.file
rabbitmq: 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; 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, if ([DescriptionTemplateFieldType.FREE_TEXT, DescriptionTemplateFieldType.CHECK_BOX, DescriptionTemplateFieldType.TEXT_AREA,
DescriptionTemplateFieldType.RICH_TEXT_AREA, DescriptionTemplateFieldType.UPLOAD, DescriptionTemplateFieldType.BOOLEAN_DECISION, 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){ if (DescriptionTemplateFieldType.UPLOAD == fieldType){
return false; //not apply visibility logic return false; //not apply visibility logic
} else { } else {