status authz changes

This commit is contained in:
CITE\amentis 2024-09-30 12:11:27 +03:00
parent ff52d32d32
commit fcacf93024
13 changed files with 393 additions and 25 deletions

View File

@ -0,0 +1,24 @@
package org.opencdmp.event;
import java.util.UUID;
public class DescriptionStatusTouchedEvent {
public DescriptionStatusTouchedEvent() {
}
public DescriptionStatusTouchedEvent(UUID id) {
this.id = id;
}
private UUID id;
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
}

View File

@ -57,6 +57,10 @@ public class EventBroker {
this.applicationEventPublisher.publishEvent(event); this.applicationEventPublisher.publishEvent(event);
} }
public void emit(DescriptionStatusTouchedEvent event) {
this.applicationEventPublisher.publishEvent(event);
}
public void emit(TagTouchedEvent event) { public void emit(TagTouchedEvent event) {
this.applicationEventPublisher.publishEvent(event); this.applicationEventPublisher.publishEvent(event);
} }

View File

@ -200,6 +200,8 @@ public class DescriptionStatusQuery extends QueryBase<DescriptionStatusEntity> {
return DescriptionStatusEntity._tenantId; return DescriptionStatusEntity._tenantId;
else if (item.match(DescriptionStatus._internalStatus)) else if (item.match(DescriptionStatus._internalStatus))
return DescriptionStatusEntity._internalStatus; return DescriptionStatusEntity._internalStatus;
else if (item.match(DescriptionStatusEntity._definition))
return DescriptionStatusEntity._definition;
else if (item.prefix(DescriptionStatusEntity._definition)) else if (item.prefix(DescriptionStatusEntity._definition))
return DescriptionStatusEntity._definition; return DescriptionStatusEntity._definition;
else else

View File

@ -0,0 +1,11 @@
package org.opencdmp.service.custompolicy;
import gr.cite.tools.cache.CacheOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "cache.custom-policy-by-tenant")
public class CustomPolicyCacheOptions extends CacheOptions {
}

View File

@ -0,0 +1,76 @@
package org.opencdmp.service.custompolicy;
import gr.cite.tools.cache.CacheService;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Service
public class CustomPolicyCacheService extends CacheService<CustomPolicyCacheService.CustomPolicyCacheValue> {
public static class CustomPolicyCacheValue {
public CustomPolicyCacheValue() {}
public CustomPolicyCacheValue(String tenantCode, Map<UUID, PlanStatusDefinitionEntity> planStatusDefinitionMap, Map<UUID, DescriptionStatusDefinitionEntity> descriptionStatusDefinitionMap) {
this.tenantCode = tenantCode;
this.planStatusDefinitionMap = planStatusDefinitionMap;
this.descriptionStatusDefinitionMap = descriptionStatusDefinitionMap;
}
private String tenantCode;
private Map<UUID, PlanStatusDefinitionEntity> planStatusDefinitionMap;
private Map<UUID, DescriptionStatusDefinitionEntity> descriptionStatusDefinitionMap;
public String getTenantCode() {
return tenantCode;
}
public void setTenantCode(String tenantCode) {
this.tenantCode = tenantCode;
}
public Map<UUID, PlanStatusDefinitionEntity> getPlanStatusDefinitionMap() {
return planStatusDefinitionMap;
}
public void setPlanStatusDefinitionMap(Map<UUID, PlanStatusDefinitionEntity> planStatusDefinitionMap) {
this.planStatusDefinitionMap = planStatusDefinitionMap;
}
public Map<UUID, DescriptionStatusDefinitionEntity> getDescriptionStatusDefinitionMap() {
return descriptionStatusDefinitionMap;
}
public void setDescriptionStatusDefinitionMap(Map<UUID, DescriptionStatusDefinitionEntity> descriptionStatusDefinitionMap) {
this.descriptionStatusDefinitionMap = descriptionStatusDefinitionMap;
}
}
@Autowired
public CustomPolicyCacheService(CustomPolicyCacheOptions options) {
super(options);
}
@Override
protected Class<CustomPolicyCacheValue> valueClass() {return CustomPolicyCacheValue.class;}
public String keyOf(CustomPolicyCacheValue value) {
return this.buildKey(value.getTenantCode());
}
public String buildKey(String tenantCode) {
HashMap<String, String> keyParts = new HashMap<>();
keyParts.put("$tenantCode$", tenantCode);
return this.generateKey(keyParts);
}
}

View File

@ -0,0 +1,23 @@
package org.opencdmp.service.custompolicy;
import gr.cite.commons.web.authz.configuration.Permission;
import org.opencdmp.commons.enums.PlanUserRole;
import java.util.HashMap;
import java.util.UUID;
public interface CustomPolicyService {
HashMap<String, Permission> buildPlanStatusPolicies();
String getPlanStatusCanEditStatusPermission(UUID id);
HashMap<String, Permission> buildDescriptionStatusPolicies();
String getDescriptionStatusCanEditStatusPermission(UUID id);
String getPlanStatusCanEditStatusAffiliatedPermission(UUID id, PlanUserRole planUserRole);
String getDescriptionStatusCanEditStatusAffiliatedPermission(UUID id, PlanUserRole planUserRole);
}

View File

@ -0,0 +1,141 @@
package org.opencdmp.service.custompolicy;
import gr.cite.commons.web.authz.configuration.Permission;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import org.opencdmp.commons.XmlHandlingService;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.PlanUserRole;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.data.PlanStatusEntity;
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.descriptionstatus.DescriptionStatusDefinitionAuthorization;
import org.opencdmp.model.planstatus.PlanStatus;
import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization;
import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.query.PlanStatusQuery;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.util.*;
@Service
public class CustomPolicyServiceImpl implements CustomPolicyService{
private final QueryFactory queryFactory;
private final XmlHandlingService xmlHandlingService;
private final TenantScope tenantScope;
private final CustomPolicyCacheService customPolicyCacheService;
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(CustomPolicyServiceImpl.class));
public CustomPolicyServiceImpl(QueryFactory queryFactory, XmlHandlingService xmlHandlingService, TenantScope tenantScope, CustomPolicyCacheService customPolicyCacheService) {
this.queryFactory = queryFactory;
this.xmlHandlingService = xmlHandlingService;
this.tenantScope = tenantScope;
this.customPolicyCacheService = customPolicyCacheService;
}
@Override
public HashMap<String, Permission> buildPlanStatusPolicies() {
HashMap<String, Permission> policies = new HashMap<>();
String tenantCode = null;
try {
tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
CustomPolicyCacheService.CustomPolicyCacheValue cacheValue = this.customPolicyCacheService.lookup(this.customPolicyCacheService.buildKey(tenantCode));
if (cacheValue == null || cacheValue.getPlanStatusDefinitionMap() == null) {
Map<UUID, PlanStatusDefinitionEntity> definitionStatusMap = new HashMap<>();
List<PlanStatusEntity> entities = this.queryFactory.query(PlanStatusQuery.class).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(PlanStatus._id).ensure(PlanStatus._definition));
for (PlanStatusEntity entity: entities) {
PlanStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(PlanStatusDefinitionEntity.class, entity.getDefinition());
if (definition != null) {
definitionStatusMap.put(entity.getId(), definition);
if (definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null) {
policies.put(this.getPlanStatusCanEditStatusPermission(entity.getId()), new Permission(new HashSet<>(definition.getAuthorization().getEdit().getRoles()), new ArrayList<>(), new HashSet<>(), definition.getAuthorization().getEdit().getAllowAnonymous(), definition.getAuthorization().getEdit().getAllowAuthenticated()));
}
}
}
if (cacheValue != null && cacheValue.getDescriptionStatusDefinitionMap() != null) {
cacheValue = new CustomPolicyCacheService.CustomPolicyCacheValue(tenantCode, definitionStatusMap, cacheValue.getDescriptionStatusDefinitionMap());
} else {
cacheValue = new CustomPolicyCacheService.CustomPolicyCacheValue(tenantCode, definitionStatusMap, null);
}
this.customPolicyCacheService.put(cacheValue);
} else {
for (UUID statusId: cacheValue.getPlanStatusDefinitionMap().keySet()) {
PlanStatusDefinitionEntity definition = cacheValue.getPlanStatusDefinitionMap().get(statusId);
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null) {
policies.put(this.getPlanStatusCanEditStatusPermission(statusId), new Permission(new HashSet<>(definition.getAuthorization().getEdit().getRoles()), new ArrayList<>(), new HashSet<>(), definition.getAuthorization().getEdit().getAllowAnonymous(), definition.getAuthorization().getEdit().getAllowAuthenticated()));
}
}
}
return policies;
}
@Override
public HashMap<String, Permission> buildDescriptionStatusPolicies(){
HashMap<String, Permission> policies = new HashMap<>();
String tenantCode = null;
try {
tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : "";
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
CustomPolicyCacheService.CustomPolicyCacheValue cacheValue = this.customPolicyCacheService.lookup(this.customPolicyCacheService.buildKey(tenantCode));
if (cacheValue == null || cacheValue.getDescriptionStatusDefinitionMap() == null) {
Map<UUID, DescriptionStatusDefinitionEntity> definitionStatusMap = new HashMap<>();
List<DescriptionStatusEntity> entities = this.queryFactory.query(DescriptionStatusQuery.class).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._definition));
for (DescriptionStatusEntity entity : entities) {
DescriptionStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DescriptionStatusDefinitionEntity.class, entity.getDefinition());
if (definition != null) {
definitionStatusMap.put(entity.getId(), definition);
if (definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null) {
policies.put(this.getDescriptionStatusCanEditStatusPermission(entity.getId()), new Permission(new HashSet<>(definition.getAuthorization().getEdit().getRoles()), new ArrayList<>(), new HashSet<>(), definition.getAuthorization().getEdit().getAllowAnonymous(), definition.getAuthorization().getEdit().getAllowAuthenticated()));
}
}
}
if (cacheValue != null && cacheValue.getPlanStatusDefinitionMap() != null) {
cacheValue = new CustomPolicyCacheService.CustomPolicyCacheValue(tenantCode, cacheValue.getPlanStatusDefinitionMap(), definitionStatusMap);
} else {
cacheValue = new CustomPolicyCacheService.CustomPolicyCacheValue(tenantCode, null, definitionStatusMap);
}
this.customPolicyCacheService.put(cacheValue);
} else {
for (UUID statusId: cacheValue.getDescriptionStatusDefinitionMap().keySet()) {
DescriptionStatusDefinitionEntity definition = cacheValue.getDescriptionStatusDefinitionMap().get(statusId);
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null) {
policies.put(this.getDescriptionStatusCanEditStatusPermission(statusId), new Permission(new HashSet<>(definition.getAuthorization().getEdit().getRoles()), new ArrayList<>(), new HashSet<>(), definition.getAuthorization().getEdit().getAllowAnonymous(), definition.getAuthorization().getEdit().getAllowAuthenticated()));
}
}
}
return policies;
}
@Override
public String getPlanStatusCanEditStatusPermission(UUID id){
return "PlanStatus" + "_" + id + "_" + PlanStatusDefinitionAuthorization._edit;
}
@Override
public String getDescriptionStatusCanEditStatusPermission(UUID id){
return "DescriptionStatus" + "_" + id + "_" + DescriptionStatusDefinitionAuthorization._edit;
}
@Override
public String getPlanStatusCanEditStatusAffiliatedPermission(UUID id, PlanUserRole planUserRole){
return "PlanStatus" + "_" + id + "_" + planUserRole.name() + "_" + PlanStatusDefinitionAuthorization._edit;
}
@Override
public String getDescriptionStatusCanEditStatusAffiliatedPermission(UUID id, PlanUserRole planUserRole){
return "DescriptionStatus" + "_" + id + "_" + planUserRole.name() + "_" + DescriptionStatusDefinitionAuthorization._edit;
}
}

View File

@ -69,6 +69,7 @@ import org.opencdmp.model.reference.Reference;
import org.opencdmp.model.referencetype.ReferenceType; import org.opencdmp.model.referencetype.ReferenceType;
import org.opencdmp.query.*; import org.opencdmp.query.*;
import org.opencdmp.service.accounting.AccountingService; import org.opencdmp.service.accounting.AccountingService;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.opencdmp.service.descriptiontemplate.DescriptionTemplateService; import org.opencdmp.service.descriptiontemplate.DescriptionTemplateService;
import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService; import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService;
import org.opencdmp.service.elastic.ElasticService; import org.opencdmp.service.elastic.ElasticService;
@ -144,6 +145,7 @@ public class DescriptionServiceImpl implements DescriptionService {
private final UsageLimitService usageLimitService; private final UsageLimitService usageLimitService;
private final AccountingService accountingService; private final AccountingService accountingService;
private final DescriptionWorkflowService descriptionWorkflowService; private final DescriptionWorkflowService descriptionWorkflowService;
private final CustomPolicyService customPolicyService;
@Autowired @Autowired
public DescriptionServiceImpl( public DescriptionServiceImpl(
@ -158,7 +160,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, AuthorizationContentResolver authorizationContentResolver, AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, TenantScope tenantScope, ResponseUtilsService responseUtilsService, DescriptionTemplateService descriptionTemplateService, TagService tagService, UsageLimitService usageLimitService, AccountingService accountingService, DescriptionWorkflowService descriptionWorkflowService) { XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, AuthorizationContentResolver authorizationContentResolver, AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, TenantScope tenantScope, ResponseUtilsService responseUtilsService, DescriptionTemplateService descriptionTemplateService, TagService tagService, UsageLimitService usageLimitService, AccountingService accountingService, DescriptionWorkflowService descriptionWorkflowService, CustomPolicyService customPolicyService) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -188,6 +190,7 @@ public class DescriptionServiceImpl implements DescriptionService {
this.usageLimitService = usageLimitService; this.usageLimitService = usageLimitService;
this.accountingService = accountingService; this.accountingService = accountingService;
this.descriptionWorkflowService = descriptionWorkflowService; this.descriptionWorkflowService = descriptionWorkflowService;
this.customPolicyService = customPolicyService;
} }
@Override @Override
@ -499,8 +502,16 @@ public class DescriptionServiceImpl implements DescriptionService {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.EditDescription); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.EditDescription);
DescriptionEntity data = this.entityManager.find(DescriptionEntity.class, model.getId()); DescriptionEntity data = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.AllExceptPublic).ids(model.getId()).isActive(IsActive.Active).first();
if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (data == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
try {
this.authorizationService.authorizeForce(this.customPolicyService.getDescriptionStatusCanEditStatusPermission(model.getStatusId()));
} catch (Exception e) {
PlanUserEntity planUserEntity = this.queryFactory.query(PlanUserQuery.class).planIds(data.getPlanId()).userIds(this.userScope.getUserId()).isActives(IsActive.Active).firstAs(new BaseFieldSet().ensure(PlanUser._role));
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(data.getPlanId())), this.customPolicyService.getDescriptionStatusCanEditStatusAffiliatedPermission(model.getStatusId(), planUserEntity.getRole()));
}
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
if (!data.getStatusId().equals(model.getStatusId())){ if (!data.getStatusId().equals(model.getStatusId())){
DescriptionStatusEntity oldStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().ids(data.getStatusId()).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id).ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._internalStatus)); DescriptionStatusEntity oldStatusEntity = this.queryFactory.query(DescriptionStatusQuery.class).disableTracking().ids(data.getStatusId()).isActive(IsActive.Active).firstAs(new BaseFieldSet().ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._id).ensure(org.opencdmp.model.descriptionstatus.DescriptionStatus._internalStatus));

View File

@ -26,6 +26,9 @@ import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.DescriptionEntity; import org.opencdmp.data.DescriptionEntity;
import org.opencdmp.data.DescriptionStatusEntity; import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.data.TenantEntityManager; import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.event.DescriptionStatusTouchedEvent;
import org.opencdmp.event.EventBroker;
import org.opencdmp.event.PlanStatusTouchedEvent;
import org.opencdmp.model.builder.descriptionstatus.DescriptionStatusBuilder; import org.opencdmp.model.builder.descriptionstatus.DescriptionStatusBuilder;
import org.opencdmp.model.deleter.DescriptionStatusDeleter; import org.opencdmp.model.deleter.DescriptionStatusDeleter;
import org.opencdmp.model.description.Description; import org.opencdmp.model.description.Description;
@ -63,8 +66,9 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
private final XmlHandlingService xmlHandlingService; private final XmlHandlingService xmlHandlingService;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final DescriptionWorkflowService descriptionWorkflowService; private final DescriptionWorkflowService descriptionWorkflowService;
private final EventBroker eventBroker;
public DescriptionStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authService, TenantEntityManager entityManager, ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService, QueryFactory queryFactory, DescriptionWorkflowService descriptionWorkflowService) { public DescriptionStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authService, TenantEntityManager entityManager, ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService, QueryFactory queryFactory, DescriptionWorkflowService descriptionWorkflowService, EventBroker eventBroker) {
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -75,6 +79,7 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
this.xmlHandlingService = xmlHandlingService; this.xmlHandlingService = xmlHandlingService;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.descriptionWorkflowService = descriptionWorkflowService; this.descriptionWorkflowService = descriptionWorkflowService;
this.eventBroker = eventBroker;
} }
@ -112,6 +117,8 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
this.entityManager.flush(); this.entityManager.flush();
this.eventBroker.emit(new DescriptionStatusTouchedEvent(data.getId()));
return this.builderFactory.builder(DescriptionStatusBuilder.class).build(BaseFieldSet.build(fields, DescriptionStatus._id), data); return this.builderFactory.builder(DescriptionStatusBuilder.class).build(BaseFieldSet.build(fields, DescriptionStatus._id), data);
} }

View File

@ -96,6 +96,7 @@ import org.opencdmp.model.referencetype.ReferenceType;
import org.opencdmp.query.*; import org.opencdmp.query.*;
import org.opencdmp.service.accounting.AccountingService; import org.opencdmp.service.accounting.AccountingService;
import org.opencdmp.service.actionconfirmation.ActionConfirmationService; import org.opencdmp.service.actionconfirmation.ActionConfirmationService;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.opencdmp.service.description.DescriptionService; import org.opencdmp.service.description.DescriptionService;
import org.opencdmp.service.descriptiontemplate.DescriptionTemplateService; import org.opencdmp.service.descriptiontemplate.DescriptionTemplateService;
import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService; import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService;
@ -187,6 +188,7 @@ public class PlanServiceImpl implements PlanService {
private final AccountingService accountingService; private final AccountingService accountingService;
private final DescriptionWorkflowService descriptionWorkflowService; private final DescriptionWorkflowService descriptionWorkflowService;
private final PlanWorkflowServiceImpl planWorkflowService; private final PlanWorkflowServiceImpl planWorkflowService;
private final CustomPolicyService customPolicyService;
@Autowired @Autowired
public PlanServiceImpl( public PlanServiceImpl(
@ -209,7 +211,7 @@ public class PlanServiceImpl implements PlanService {
FileTransformerService fileTransformerService, FileTransformerService fileTransformerService,
ValidatorFactory validatorFactory, ValidatorFactory validatorFactory,
ElasticService elasticService, DescriptionTemplateService descriptionTemplateService, ElasticService elasticService, DescriptionTemplateService descriptionTemplateService,
AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope, ResponseUtilsService responseUtilsService, PlanBlueprintService planBlueprintService, UsageLimitService usageLimitService, AccountingService accountingService, DescriptionWorkflowService descriptionWorkflowService, PlanWorkflowServiceImpl planWorkflowService) { AnnotationEntityTouchedIntegrationEventHandler annotationEntityTouchedIntegrationEventHandler, AnnotationEntityRemovalIntegrationEventHandler annotationEntityRemovalIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope, ResponseUtilsService responseUtilsService, PlanBlueprintService planBlueprintService, UsageLimitService usageLimitService, AccountingService accountingService, DescriptionWorkflowService descriptionWorkflowService, PlanWorkflowServiceImpl planWorkflowService, CustomPolicyService customPolicyService) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -240,6 +242,7 @@ public class PlanServiceImpl implements PlanService {
this.accountingService = accountingService; this.accountingService = accountingService;
this.descriptionWorkflowService = descriptionWorkflowService; this.descriptionWorkflowService = descriptionWorkflowService;
this.planWorkflowService = planWorkflowService; this.planWorkflowService = planWorkflowService;
this.customPolicyService = customPolicyService;
} }
public Plan persist(PlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, IOException { public Plan persist(PlanPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, IOException {
@ -1610,6 +1613,14 @@ public class PlanServiceImpl implements PlanService {
public void setStatus(UUID id, UUID newStatusId, List<UUID> descriptionIds) throws InvalidApplicationException, IOException { public void setStatus(UUID id, UUID newStatusId, List<UUID> descriptionIds) throws InvalidApplicationException, IOException {
PlanEntity plan = this.queryFactory.query(PlanQuery.class).authorize(AuthorizationFlags.AllExceptPublic).ids(id).isActive(IsActive.Active).first(); PlanEntity plan = this.queryFactory.query(PlanQuery.class).authorize(AuthorizationFlags.AllExceptPublic).ids(id).isActive(IsActive.Active).first();
if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{id, Plan.class.getSimpleName()}, LocaleContextHolder.getLocale()));
try {
this.authorizationService.authorizeForce(this.customPolicyService.getPlanStatusCanEditStatusPermission(newStatusId));
} catch (Exception e) {
PlanUserEntity planUserEntity = this.queryFactory.query(PlanUserQuery.class).planIds(id).userIds(this.userScope.getUserId()).isActives(IsActive.Active).firstAs(new BaseFieldSet().ensure(PlanUser._role));
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.planAffiliation(id)), this.customPolicyService.getPlanStatusCanEditStatusAffiliatedPermission(newStatusId, planUserEntity.getRole()));
}
if (plan.getStatusId().equals(newStatusId)) throw new MyApplicationException("Old status equals with new"); if (plan.getStatusId().equals(newStatusId)) throw new MyApplicationException("Old status equals with new");
PlanStatusEntity oldPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, plan.getStatusId(), true); PlanStatusEntity oldPlanStatusEntity = this.entityManager.find(PlanStatusEntity.class, plan.getStatusId(), true);

View File

@ -6,20 +6,24 @@ import gr.cite.commons.web.authz.policy.AuthorizationRequirement;
import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.commons.web.oidc.principal.MyPrincipal;
import org.opencdmp.commons.enums.PlanUserRole; import org.opencdmp.commons.enums.PlanUserRole;
import org.opencdmp.commons.enums.UserDescriptionTemplateRole; import org.opencdmp.commons.enums.UserDescriptionTemplateRole;
import org.opencdmp.configurations.OpencdmpPermissionPolicyContextImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@Component("affiliatedAuthorizationHandler") @Component("affiliatedAuthorizationHandler")
public class AffiliatedAuthorizationHandler extends AuthorizationHandler<AffiliatedAuthorizationRequirement> { public class AffiliatedAuthorizationHandler extends AuthorizationHandler<AffiliatedAuthorizationRequirement> {
private final CustomPermissionAttributesConfiguration myConfiguration; private final CustomPermissionAttributesConfiguration myConfiguration;
private final OpencdmpPermissionPolicyContextImpl opencdmpPermissionPolicyContext;
@Autowired @Autowired
public AffiliatedAuthorizationHandler(CustomPermissionAttributesConfiguration myConfiguration) { public AffiliatedAuthorizationHandler(CustomPermissionAttributesConfiguration myConfiguration, OpencdmpPermissionPolicyContextImpl opencdmpPermissionPolicyContext) {
this.myConfiguration = myConfiguration; this.myConfiguration = myConfiguration;
} this.opencdmpPermissionPolicyContext = opencdmpPermissionPolicyContext;
}
@Override @Override
public int handleRequirement(AuthorizationHandlerContext context, Object resource, AuthorizationRequirement requirement) { public int handleRequirement(AuthorizationHandlerContext context, Object resource, AuthorizationRequirement requirement) {
@ -44,7 +48,16 @@ public class AffiliatedAuthorizationHandler extends AuthorizationHandler<Affilia
CustomPermissionAttributesProperties.MyPermission policy = this.myConfiguration.getMyPolicies().get(permission); CustomPermissionAttributesProperties.MyPermission policy = this.myConfiguration.getMyPolicies().get(permission);
boolean hasPlanPermission = policy != null && this.hasPermission(policy.getPlan(), planUserRoles); boolean hasPlanPermission = policy != null && this.hasPermission(policy.getPlan(), planUserRoles);
boolean hasDescriptionTemplatePermission = policy != null && this.hasPermission(policy.getDescriptionTemplate(), userDescriptionTemplateRoles); boolean hasDescriptionTemplatePermission = policy != null && this.hasPermission(policy.getDescriptionTemplate(), userDescriptionTemplateRoles);
if (hasPlanPermission || hasDescriptionTemplatePermission) hits += 1;
boolean hasPlanCustomPermission = false;
if (permission.startsWith("PlanStatus_") || permission.startsWith("DescriptionStatus_")) {
HashMap<String, CustomPermissionAttributesProperties. MyPermission> customPolicies = this.opencdmpPermissionPolicyContext.buildAffiliatedCustomPermissions();
if (customPolicies == null || customPolicies.isEmpty()) return ACCESS_DENIED;
CustomPermissionAttributesProperties.MyPermission customPolicy = customPolicies.get(permission);
hasPlanCustomPermission = customPolicy != null && this.hasPermission(customPolicy.getPlan(), planUserRoles);
}
if (hasPlanPermission || hasPlanCustomPermission || hasDescriptionTemplatePermission) hits += 1;
} }
if ((req.getMatchAll() && req.getRequiredPermissions().size() == hits) || (!req.getMatchAll() && hits > 0)) if ((req.getMatchAll() && req.getRequiredPermissions().size() == hits) || (!req.getMatchAll() && hits > 0))
return ACCESS_GRANTED; return ACCESS_GRANTED;

View File

@ -1,39 +1,54 @@
package org.opencdmp.configurations; package org.opencdmp.configurations;
import gr.cite.commons.web.authz.configuration.AuthorizationConfiguration; import gr.cite.commons.web.authz.configuration.AuthorizationConfiguration;
import gr.cite.commons.web.authz.configuration.Permission;
import gr.cite.commons.web.authz.configuration.PermissionPolicyContextImpl; import gr.cite.commons.web.authz.configuration.PermissionPolicyContextImpl;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import org.opencdmp.authorization.CustomPermissionAttributesProperties;
import org.opencdmp.authorization.PlanRole;
import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.XmlHandlingService;
import org.opencdmp.commons.enums.IsActive; import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.enums.PlanUserRole;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity; import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.data.PlanStatusEntity; import org.opencdmp.data.PlanStatusEntity;
import org.opencdmp.event.DescriptionStatusTouchedEvent;
import org.opencdmp.event.PlanStatusTouchedEvent; import org.opencdmp.event.PlanStatusTouchedEvent;
import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.planstatus.PlanStatus; import org.opencdmp.model.planstatus.PlanStatus;
import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization; import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.query.PlanStatusQuery; import org.opencdmp.query.PlanStatusQuery;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
@Service @Service
public class OpencdmpPermissionPolicyContextImpl extends PermissionPolicyContextImpl { public class OpencdmpPermissionPolicyContextImpl extends PermissionPolicyContextImpl {
private final CustomPolicyService customPolicyService;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final XmlHandlingService xmlHandlingService; private final XmlHandlingService xmlHandlingService;
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OpencdmpPermissionPolicyContextImpl.class)); private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OpencdmpPermissionPolicyContextImpl.class));
public OpencdmpPermissionPolicyContextImpl(AuthorizationConfiguration authorizationConfiguration, QueryFactory queryFactory, XmlHandlingService xmlHandlingService) { public OpencdmpPermissionPolicyContextImpl(AuthorizationConfiguration authorizationConfiguration, CustomPolicyService customPolicyService, QueryFactory queryFactory, XmlHandlingService xmlHandlingService) {
super(authorizationConfiguration); super(authorizationConfiguration);
this.queryFactory = queryFactory; this.customPolicyService = customPolicyService;
this.xmlHandlingService = xmlHandlingService; this.queryFactory = queryFactory;
this.xmlHandlingService = xmlHandlingService;
}
@EventListener
public void handlePlanTouchedEvent(PlanStatusTouchedEvent event) {
this.refresh(true);
} }
@EventListener @EventListener
public void handleTenantTouchedEvent(PlanStatusTouchedEvent event) { public void handleDescriptionStatusTouchedEvent(DescriptionStatusTouchedEvent event) {
this.refresh(true); this.refresh(true);
} }
@ -42,21 +57,42 @@ public class OpencdmpPermissionPolicyContextImpl extends PermissionPolicyContext
if (!force && this.policies != null) return; if (!force && this.policies != null) return;
this.policies = this.authorizationConfiguration.getRawPolicies(); this.policies = this.authorizationConfiguration.getRawPolicies();
this.extendedClaims = this.authorizationConfiguration.getRawExtendedClaims(); this.extendedClaims = this.authorizationConfiguration.getRawExtendedClaims();
this.policies.putAll(this.buildPlanStatusPolicies()); this.policies.putAll(this.customPolicyService.buildPlanStatusPolicies());
this.policies.putAll(this.customPolicyService.buildDescriptionStatusPolicies());
logger.info("Authorization policies found: {}", this.policies.size()); logger.info("Authorization policies found: {}", this.policies.size());
this.reload(); this.reload();
} }
private HashMap<String, Permission> buildPlanStatusPolicies(){ public HashMap<String, CustomPermissionAttributesProperties.MyPermission> buildAffiliatedCustomPermissions() {
HashMap<String, gr.cite.commons.web.authz.configuration.Permission> policies = new HashMap<>(); HashMap<String, CustomPermissionAttributesProperties.MyPermission> affiliatedCustomPermissions = new HashMap<>();
List<PlanStatusEntity> entities = this.queryFactory.query(PlanStatusQuery.class).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(PlanStatus._id).ensure(PlanStatus._definition)); List<PlanStatusEntity> planStatusEntities = this.queryFactory.query(PlanStatusQuery.class).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(PlanStatus._id).ensure(PlanStatus._definition));
for (PlanStatusEntity entity : entities) { if (planStatusEntities != null) {
PlanStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(PlanStatusDefinitionEntity.class, entity.getDefinition()); for (PlanStatusEntity entity : planStatusEntities) {
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null) policies.put(this.getPlanStatusCanEditStatusPermission(entity.getId()), new gr.cite.commons.web.authz.configuration.Permission(new HashSet<>(definition.getAuthorization().getEdit().getRoles()), new ArrayList<>(), new HashSet<>(), definition.getAuthorization().getEdit().getAllowAnonymous(), definition.getAuthorization().getEdit().getAllowAuthenticated())); PlanStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(PlanStatusDefinitionEntity.class, entity.getDefinition());
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null && definition.getAuthorization().getEdit().getPlanRoles() != null){
for (PlanUserRole planUserRole: definition.getAuthorization().getEdit().getPlanRoles()) {
PlanRole planRole = new PlanRole(new HashSet<>(List.of(planUserRole)));
CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(planRole, null);
affiliatedCustomPermissions.put(this.customPolicyService.getPlanStatusCanEditStatusAffiliatedPermission(entity.getId(), planUserRole), myPermission);
}
}
}
} }
return policies;
} List<DescriptionStatusEntity> descriptionStatusEntities = this.queryFactory.query(DescriptionStatusQuery.class).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._definition));
private String getPlanStatusCanEditStatusPermission(UUID id){ if (descriptionStatusEntities != null) {
return "PlanStatus" + "_" + id + "_" + PlanStatusDefinitionAuthorization._edit; for (DescriptionStatusEntity entity : descriptionStatusEntities) {
DescriptionStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DescriptionStatusDefinitionEntity.class, entity.getDefinition());
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null && definition.getAuthorization().getEdit().getPlanRoles() != null){
for (PlanUserRole planUserRole: definition.getAuthorization().getEdit().getPlanRoles()) {
PlanRole planRole = new PlanRole(new HashSet<>(List.of(planUserRole)));
CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(planRole, null);
affiliatedCustomPermissions.put(this.customPolicyService.getDescriptionStatusCanEditStatusAffiliatedPermission(entity.getId(), planUserRole), myPermission);
}
}
}
}
return affiliatedCustomPermissions;
} }
} }

View File

@ -86,6 +86,12 @@ cache:
maximumSize: 500 maximumSize: 500
enableRecordStats: false enableRecordStats: false
expireAfterWriteSeconds: 20 expireAfterWriteSeconds: 20
- names: [ "customPolicyByTenant" ]
allowNullValues: true
initialCapacity: 100
maximumSize: 500
enableRecordStats: false
expireAfterWriteSeconds: 600
mapCaches: mapCaches:
userBySubjectId: userBySubjectId:
name: userBySubjectId name: userBySubjectId
@ -128,4 +134,7 @@ cache:
keyPattern: resolve_$keyhash$:v0 keyPattern: resolve_$keyhash$:v0
affiliation: affiliation:
name: affiliation name: affiliation
keyPattern: affiliation_$entity$_$user$_$tenant$_$type$:v0 keyPattern: affiliation_$entity$_$user$_$tenant$_$type$:v0
customPolicyByTenant:
name: customPolicyByTenant
keyPattern: custom_policy_by_tenant$tenant_code$:v0