status authz changes

This commit is contained in:
CITE\amentis 2024-10-01 13:13:29 +03:00
parent fcacf93024
commit 6ebe3228ae
33 changed files with 483 additions and 190 deletions

View File

@ -7,12 +7,16 @@ public class DescriptionStatusTouchedEvent {
public DescriptionStatusTouchedEvent() { public DescriptionStatusTouchedEvent() {
} }
public DescriptionStatusTouchedEvent(UUID id) { public DescriptionStatusTouchedEvent(UUID id, String tenantCode) {
this.id = id; this.id = id;
this.tenantCode = tenantCode;
} }
private UUID id; private UUID id;
private String tenantCode;
public UUID getId() { public UUID getId() {
return id; return id;
} }
@ -21,4 +25,11 @@ public class DescriptionStatusTouchedEvent {
this.id = id; this.id = id;
} }
public String getTenantCode() {
return tenantCode;
}
public void setTenantCode(String tenantCode) {
this.tenantCode = tenantCode;
}
} }

View File

@ -7,12 +7,15 @@ public class PlanStatusTouchedEvent {
public PlanStatusTouchedEvent() { public PlanStatusTouchedEvent() {
} }
public PlanStatusTouchedEvent(UUID id) { public PlanStatusTouchedEvent(UUID id, String tenantCode) {
this.id = id; this.id = id;
this.tenantCode = tenantCode;
} }
private UUID id; private UUID id;
private String tenantCode;
public UUID getId() { public UUID getId() {
return id; return id;
} }
@ -21,4 +24,11 @@ public class PlanStatusTouchedEvent {
this.id = id; this.id = id;
} }
public String getTenantCode() {
return tenantCode;
}
public void setTenantCode(String tenantCode) {
this.tenantCode = tenantCode;
}
} }

View File

@ -13,11 +13,13 @@ import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationContentResolver; import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import org.opencdmp.commons.JsonHandlingService; import org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.XmlHandlingService;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.types.description.PropertyDefinitionEntity; import org.opencdmp.commons.types.description.PropertyDefinitionEntity;
import org.opencdmp.commons.types.descriptiontemplate.DefinitionEntity; import org.opencdmp.commons.types.descriptiontemplate.DefinitionEntity;
import org.opencdmp.convention.ConventionService; import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.DescriptionEntity; import org.opencdmp.data.DescriptionEntity;
import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.data.DescriptionTemplateEntity; import org.opencdmp.data.DescriptionTemplateEntity;
import org.opencdmp.model.DescriptionTag; import org.opencdmp.model.DescriptionTag;
import org.opencdmp.model.PlanDescriptionTemplate; import org.opencdmp.model.PlanDescriptionTemplate;
@ -34,8 +36,10 @@ import org.opencdmp.model.descriptionreference.DescriptionReference;
import org.opencdmp.model.descriptionstatus.DescriptionStatus; import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.descriptiontemplate.DescriptionTemplate; import org.opencdmp.model.descriptiontemplate.DescriptionTemplate;
import org.opencdmp.model.plan.Plan; import org.opencdmp.model.plan.Plan;
import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization;
import org.opencdmp.model.user.User; import org.opencdmp.model.user.User;
import org.opencdmp.query.*; import org.opencdmp.query.*;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@ -57,6 +61,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
private final AuthorizationContentResolver authorizationContentResolver; private final AuthorizationContentResolver authorizationContentResolver;
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final CustomPolicyService customPolicyService;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -64,7 +69,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
public DescriptionBuilder( public DescriptionBuilder(
ConventionService conventionService, ConventionService conventionService,
QueryFactory queryFactory, QueryFactory queryFactory,
BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService, AuthorizationService authorizationService, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope) { BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService, AuthorizationService authorizationService, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope, CustomPolicyService customPolicyService) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(DescriptionBuilder.class))); super(conventionService, new LoggerService(LoggerFactory.getLogger(DescriptionBuilder.class)));
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
@ -73,6 +78,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.authorizationContentResolver = authorizationContentResolver; this.authorizationContentResolver = authorizationContentResolver;
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.customPolicyService = customPolicyService;
} }
public DescriptionBuilder authorize(EnumSet<AuthorizationFlags> values) { public DescriptionBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -90,6 +96,9 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Description._status)); FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Description._status));
Map<UUID, DescriptionStatus> statusItemsMap = this.collectDescriptionStatuses(statusFields, data); Map<UUID, DescriptionStatus> statusItemsMap = this.collectDescriptionStatuses(statusFields, data);
FieldSet availableStatusesFields = fields.extractPrefixed(this.asPrefix(Description._availableStatuses));
Map<UUID, List<DescriptionStatus>> avaialbleStatusesItemsMap = this.collectAvailableDescriptionStatuses(availableStatusesFields, data);
FieldSet planDescriptionTemplateFields = fields.extractPrefixed(this.asPrefix(Description._planDescriptionTemplate)); FieldSet planDescriptionTemplateFields = fields.extractPrefixed(this.asPrefix(Description._planDescriptionTemplate));
Map<UUID, PlanDescriptionTemplate> planDescriptionTemplateItemsMap = this.collectPlanDescriptionTemplates(planDescriptionTemplateFields, data); Map<UUID, PlanDescriptionTemplate> planDescriptionTemplateItemsMap = this.collectPlanDescriptionTemplates(planDescriptionTemplateFields, data);
@ -115,6 +124,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Description._authorizationFlags, this.authorizationContentResolver.getPermissionNames()); Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Description._authorizationFlags, this.authorizationContentResolver.getPermissionNames());
Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.descriptionsAffiliation(data.stream().map(DescriptionEntity::getId).collect(Collectors.toList())); Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.descriptionsAffiliation(data.stream().map(DescriptionEntity::getId).collect(Collectors.toList()));
FieldSet statusAuthorizationFlags = fields.extractPrefixed(this.asPrefix(Description._statusAuthorizationFlags));
List<Description> models = new ArrayList<>(); List<Description> models = new ArrayList<>();
for (DescriptionEntity d : data) { for (DescriptionEntity d : data) {
Description m = new Description(); Description m = new Description();
@ -122,6 +132,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
if (fields.hasField(this.asIndexer(Description._tenantId))) m.setTenantId(d.getTenantId()); if (fields.hasField(this.asIndexer(Description._tenantId))) m.setTenantId(d.getTenantId());
if (fields.hasField(this.asIndexer(Description._label))) m.setLabel(d.getLabel()); if (fields.hasField(this.asIndexer(Description._label))) m.setLabel(d.getLabel());
if (!statusFields.isEmpty() && statusItemsMap != null && statusItemsMap.containsKey(d.getStatusId())) m.setStatus(statusItemsMap.get(d.getStatusId())); if (!statusFields.isEmpty() && statusItemsMap != null && statusItemsMap.containsKey(d.getStatusId())) m.setStatus(statusItemsMap.get(d.getStatusId()));
if (avaialbleStatusesItemsMap != null && !avaialbleStatusesItemsMap.isEmpty() && avaialbleStatusesItemsMap.containsKey(d.getId())) m.setAvailableStatuses(avaialbleStatusesItemsMap.get(d.getId()));
if (fields.hasField(this.asIndexer(Description._description))) m.setDescription(d.getDescription()); if (fields.hasField(this.asIndexer(Description._description))) m.setDescription(d.getDescription());
if (fields.hasField(this.asIndexer(Description._createdAt))) m.setCreatedAt(d.getCreatedAt()); if (fields.hasField(this.asIndexer(Description._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(Description._updatedAt))) m.setUpdatedAt(d.getUpdatedAt()); if (fields.hasField(this.asIndexer(Description._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
@ -140,6 +151,9 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
m.setProperties(this.builderFactory.builder(PropertyDefinitionBuilder.class).withDefinition(definitionEntityMap != null ? definitionEntityMap.getOrDefault(d.getDescriptionTemplateId(), null) : null).authorize(this.authorize).build(definitionPropertiesFields, propertyDefinition)); m.setProperties(this.builderFactory.builder(PropertyDefinitionBuilder.class).withDefinition(definitionEntityMap != null ? definitionEntityMap.getOrDefault(d.getDescriptionTemplateId(), null) : null).authorize(this.authorize).build(definitionPropertiesFields, propertyDefinition));
} }
if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null))); if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null)));
if (!statusAuthorizationFlags.isEmpty() && !this.conventionService.isListNullOrEmpty(m.getAvailableStatuses())) {
m.setStatusAuthorizationFlags(this.evaluateStatusAuthorizationFlags(this.authorizationService, statusAuthorizationFlags, d));
}
models.add(m); models.add(m);
} }
@ -178,6 +192,20 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
return itemMap; return itemMap;
} }
private Map<UUID, List<DescriptionStatus>> collectAvailableDescriptionStatuses(FieldSet fields, List<DescriptionEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", DescriptionStatus.class.getSimpleName());
Map<UUID, List<DescriptionStatus>> itemMap = new HashMap<>();
FieldSet fieldSet = new BaseFieldSet(fields.getFields()).ensure(DescriptionStatus._id);
for (DescriptionEntity entity: data) {
List<DescriptionStatusEntity> statusEntities = this.queryFactory.query(DescriptionStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActive(IsActive.Active).authorizedStatus(true, entity.getId()).collectAs(fieldSet);
itemMap.put(entity.getId(), this.builderFactory.builder(DescriptionStatusBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, statusEntities));
}
return itemMap;
}
private Map<UUID, User> collectUsers(FieldSet fields, List<DescriptionEntity> data) throws MyApplicationException { private Map<UUID, User> collectUsers(FieldSet fields, List<DescriptionEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) if (fields.isEmpty() || data.isEmpty())
return null; return null;
@ -350,4 +378,24 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
return itemMap; return itemMap;
} }
private List<String> evaluateStatusAuthorizationFlags(AuthorizationService authorizationService, FieldSet statusAuthorizationFlags, DescriptionEntity description) {
List<String> allowed = new ArrayList<>();
if (statusAuthorizationFlags == null) return allowed;
if (authorizationService == null) return allowed;
if (description == null) return allowed;
String editPermission = this.customPolicyService.getDescriptionStatusCanEditStatusPermission(description.getStatusId());
for (String permission : statusAuthorizationFlags.getFields()) {
if (statusAuthorizationFlags.hasField(this.asIndexer(PlanStatusDefinitionAuthorization._edit))) {
Boolean isAllowed = authorizationService.authorize(editPermission);
if (!isAllowed) {
isAllowed = this.authorizationService.authorizeAtLeastOne(List.of(this.authorizationContentResolver.planAffiliation(description.getPlanId())), editPermission);
}
if (isAllowed) allowed.add(permission);
}
}
return allowed;
}
} }

View File

@ -14,12 +14,14 @@ import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationCont
import org.opencdmp.commons.JsonHandlingService; import org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.XmlHandlingService; import org.opencdmp.commons.XmlHandlingService;
import org.opencdmp.commons.enums.EntityType; import org.opencdmp.commons.enums.EntityType;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.scope.tenant.TenantScope; import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.types.plan.PlanPropertiesEntity; import org.opencdmp.commons.types.plan.PlanPropertiesEntity;
import org.opencdmp.commons.types.planblueprint.DefinitionEntity; import org.opencdmp.commons.types.planblueprint.DefinitionEntity;
import org.opencdmp.convention.ConventionService; import org.opencdmp.convention.ConventionService;
import org.opencdmp.data.PlanBlueprintEntity; import org.opencdmp.data.PlanBlueprintEntity;
import org.opencdmp.data.PlanEntity; import org.opencdmp.data.PlanEntity;
import org.opencdmp.data.PlanStatusEntity;
import org.opencdmp.model.PlanDescriptionTemplate; import org.opencdmp.model.PlanDescriptionTemplate;
import org.opencdmp.model.PlanUser; import org.opencdmp.model.PlanUser;
import org.opencdmp.model.EntityDoi; import org.opencdmp.model.EntityDoi;
@ -33,8 +35,10 @@ import org.opencdmp.model.plan.Plan;
import org.opencdmp.model.planblueprint.PlanBlueprint; import org.opencdmp.model.planblueprint.PlanBlueprint;
import org.opencdmp.model.planreference.PlanReference; import org.opencdmp.model.planreference.PlanReference;
import org.opencdmp.model.planstatus.PlanStatus; import org.opencdmp.model.planstatus.PlanStatus;
import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization;
import org.opencdmp.model.user.User; import org.opencdmp.model.user.User;
import org.opencdmp.query.*; import org.opencdmp.query.*;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@ -56,13 +60,14 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
private final AuthorizationContentResolver authorizationContentResolver; private final AuthorizationContentResolver authorizationContentResolver;
private final TenantScope tenantScope; private final TenantScope tenantScope;
private final CustomPolicyService customPolicyService;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@Autowired @Autowired
public PlanBuilder(ConventionService conventionService, public PlanBuilder(ConventionService conventionService,
QueryFactory queryFactory, QueryFactory queryFactory,
BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService, AuthorizationService authorizationService, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope) { BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService, AuthorizationService authorizationService, AuthorizationContentResolver authorizationContentResolver, TenantScope tenantScope, CustomPolicyService customPolicyService) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(PlanBuilder.class))); super(conventionService, new LoggerService(LoggerFactory.getLogger(PlanBuilder.class)));
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
@ -71,6 +76,7 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.authorizationContentResolver = authorizationContentResolver; this.authorizationContentResolver = authorizationContentResolver;
this.tenantScope = tenantScope; this.tenantScope = tenantScope;
this.customPolicyService = customPolicyService;
} }
public PlanBuilder authorize(EnumSet<AuthorizationFlags> values) { public PlanBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -90,6 +96,9 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Plan._status)); FieldSet statusFields = fields.extractPrefixed(this.asPrefix(Plan._status));
Map<UUID, PlanStatus> statusItemsMap = this.collectPlanStatuses(statusFields, data); Map<UUID, PlanStatus> statusItemsMap = this.collectPlanStatuses(statusFields, data);
FieldSet availableStatusesFields = fields.extractPrefixed(this.asPrefix(Plan._availableStatuses));
Map<UUID, List<PlanStatus>> avaialbleStatusesItemsMap = this.collectAvailablePlanStatuses(availableStatusesFields, data);
FieldSet entityDoisFields = fields.extractPrefixed(this.asPrefix(Plan._entityDois)); FieldSet entityDoisFields = fields.extractPrefixed(this.asPrefix(Plan._entityDois));
Map<UUID, List<EntityDoi>> entityDoisMap = this.collectEntityDois(entityDoisFields, data); Map<UUID, List<EntityDoi>> entityDoisMap = this.collectEntityDois(entityDoisFields, data);
@ -120,6 +129,7 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Plan._authorizationFlags, this.authorizationContentResolver.getPermissionNames()); Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Plan._authorizationFlags, this.authorizationContentResolver.getPermissionNames());
Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.plansAffiliation(data.stream().map(PlanEntity::getId).collect(Collectors.toList())); Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.plansAffiliation(data.stream().map(PlanEntity::getId).collect(Collectors.toList()));
FieldSet statusAuthorizationFlags = fields.extractPrefixed(this.asPrefix(Plan._statusAuthorizationFlags));
for (PlanEntity d : data) { for (PlanEntity d : data) {
Plan m = new Plan(); Plan m = new Plan();
if (fields.hasField(this.asIndexer(Plan._id))) m.setId(d.getId()); if (fields.hasField(this.asIndexer(Plan._id))) m.setId(d.getId());
@ -127,6 +137,7 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
if (fields.hasField(this.asIndexer(Plan._label))) m.setLabel(d.getLabel()); if (fields.hasField(this.asIndexer(Plan._label))) m.setLabel(d.getLabel());
if (fields.hasField(this.asIndexer(Plan._version))) m.setVersion(d.getVersion()); if (fields.hasField(this.asIndexer(Plan._version))) m.setVersion(d.getVersion());
if (!statusFields.isEmpty() && statusItemsMap != null && statusItemsMap.containsKey(d.getStatusId())) m.setStatus(statusItemsMap.get(d.getStatusId())); if (!statusFields.isEmpty() && statusItemsMap != null && statusItemsMap.containsKey(d.getStatusId())) m.setStatus(statusItemsMap.get(d.getStatusId()));
if (avaialbleStatusesItemsMap != null && !avaialbleStatusesItemsMap.isEmpty() && avaialbleStatusesItemsMap.containsKey(d.getId())) m.setAvailableStatuses(avaialbleStatusesItemsMap.get(d.getId()));
if (fields.hasField(this.asIndexer(Plan._groupId))) m.setGroupId(d.getGroupId()); if (fields.hasField(this.asIndexer(Plan._groupId))) m.setGroupId(d.getGroupId());
if (fields.hasField(this.asIndexer(Plan._description))) m.setDescription(d.getDescription()); if (fields.hasField(this.asIndexer(Plan._description))) m.setDescription(d.getDescription());
if (fields.hasField(this.asIndexer(Plan._createdAt))) m.setCreatedAt(d.getCreatedAt()); if (fields.hasField(this.asIndexer(Plan._createdAt))) m.setCreatedAt(d.getCreatedAt());
@ -155,6 +166,9 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
m.setProperties(this.builderFactory.builder(PlanPropertiesBuilder.class).withDefinition(definitionEntityMap != null ? definitionEntityMap.getOrDefault(d.getBlueprintId(), null) : null).authorize(this.authorize).build(planPropertiesFields, propertyDefinition)); m.setProperties(this.builderFactory.builder(PlanPropertiesBuilder.class).withDefinition(definitionEntityMap != null ? definitionEntityMap.getOrDefault(d.getBlueprintId(), null) : null).authorize(this.authorize).build(planPropertiesFields, propertyDefinition));
} }
if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null))); if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null)));
if (!statusAuthorizationFlags.isEmpty() && !this.conventionService.isListNullOrEmpty(m.getAvailableStatuses())) {
m.setStatusAuthorizationFlags(this.evaluateStatusAuthorizationFlags(this.authorizationService, statusAuthorizationFlags, d));
}
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));
@ -192,6 +206,20 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
return itemMap; return itemMap;
} }
private Map<UUID, List<PlanStatus>> collectAvailablePlanStatuses(FieldSet fields, List<PlanEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", PlanStatus.class.getSimpleName());
Map<UUID, List<PlanStatus>> itemMap = new HashMap<>();
FieldSet fieldSet = new BaseFieldSet(fields.getFields()).ensure(PlanStatus._id);
for (PlanEntity entity: data) {
List<PlanStatusEntity> statusEntities = this.queryFactory.query(PlanStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActives(IsActive.Active).authorizedStatus(true, entity.getId()).collectAs(fieldSet);
itemMap.put(entity.getId(), this.builderFactory.builder(PlanStatusBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, statusEntities));
}
return itemMap;
}
private Map<UUID, List<PlanReference>> collectPlanReferences(FieldSet fields, List<PlanEntity> data) throws MyApplicationException { private Map<UUID, List<PlanReference>> collectPlanReferences(FieldSet fields, List<PlanEntity> data) throws MyApplicationException {
if (fields.isEmpty() || data.isEmpty()) return null; if (fields.isEmpty() || data.isEmpty()) return null;
this.logger.debug("checking related - {}", PlanReference.class.getSimpleName()); this.logger.debug("checking related - {}", PlanReference.class.getSimpleName());
@ -377,4 +405,24 @@ public class PlanBuilder extends BaseBuilder<Plan, PlanEntity> {
return itemMap; return itemMap;
} }
private List<String> evaluateStatusAuthorizationFlags(AuthorizationService authorizationService, FieldSet statusAuthorizationFlags, PlanEntity plan) {
List<String> allowed = new ArrayList<>();
if (statusAuthorizationFlags == null) return allowed;
if (authorizationService == null) return allowed;
if (plan == null) return allowed;
String editPermission = this.customPolicyService.getPlanStatusCanEditStatusPermission(plan.getStatusId());
for (String permission : statusAuthorizationFlags.getFields()) {
if (statusAuthorizationFlags.hasField(this.asIndexer(PlanStatusDefinitionAuthorization._edit))) {
Boolean isAllowed = authorizationService.authorize(editPermission);
if (!isAllowed) {
isAllowed = this.authorizationService.authorizeAtLeastOne(List.of(this.authorizationContentResolver.planAffiliation(plan.getId())), editPermission);
}
if (isAllowed) allowed.add(permission);
}
}
return allowed;
}
} }

View File

@ -86,6 +86,12 @@ public class Description {
public static final String _plan = "plan"; public static final String _plan = "plan";
private List<DescriptionStatus> availableStatuses;
public static final String _availableStatuses = "availableStatuses";
private List<String> statusAuthorizationFlags;
public static final String _statusAuthorizationFlags = "statusAuthorizationFlags";
private Boolean belongsToCurrentTenant; private Boolean belongsToCurrentTenant;
public static final String _belongsToCurrentTenant = "belongsToCurrentTenant"; public static final String _belongsToCurrentTenant = "belongsToCurrentTenant";
@ -231,6 +237,22 @@ public class Description {
this.authorizationFlags = authorizationFlags; this.authorizationFlags = authorizationFlags;
} }
public List<DescriptionStatus> getAvailableStatuses() {
return availableStatuses;
}
public void setAvailableStatuses(List<DescriptionStatus> availableStatuses) {
this.availableStatuses = availableStatuses;
}
public List<String> getStatusAuthorizationFlags() {
return statusAuthorizationFlags;
}
public void setStatusAuthorizationFlags(List<String> statusAuthorizationFlags) {
this.statusAuthorizationFlags = statusAuthorizationFlags;
}
public Boolean getBelongsToCurrentTenant() { public Boolean getBelongsToCurrentTenant() {
return belongsToCurrentTenant; return belongsToCurrentTenant;
} }

View File

@ -97,8 +97,11 @@ public class Plan {
private List<Plan> otherPlanVersions; private List<Plan> otherPlanVersions;
public static final String _otherPlanVersions = "otherPlanVersions"; public static final String _otherPlanVersions = "otherPlanVersions";
private List<PlanStatus> availableTransitions; private List<PlanStatus> availableStatuses;
public static final String _availableTransitions = "availableTransitions"; public static final String _availableStatuses = "availableStatuses";
private List<String> statusAuthorizationFlags;
public static final String _statusAuthorizationFlags = "statusAuthorizationFlags";
private Boolean belongsToCurrentTenant; private Boolean belongsToCurrentTenant;
public static final String _belongsToCurrentTenant = "belongsToCurrentTenant"; public static final String _belongsToCurrentTenant = "belongsToCurrentTenant";
@ -321,11 +324,19 @@ public class Plan {
this.otherPlanVersions = otherPlanVersions; this.otherPlanVersions = otherPlanVersions;
} }
public List<PlanStatus> getAvailableTransitions() { public List<PlanStatus> getAvailableStatuses() {
return availableTransitions; return availableStatuses;
} }
public void setAvailableTransitions(List<PlanStatus> availableTransitions) { public void setAvailableStatuses(List<PlanStatus> availableStatuses) {
this.availableTransitions = availableTransitions; this.availableStatuses = availableStatuses;
}
public List<String> getStatusAuthorizationFlags() {
return statusAuthorizationFlags;
}
public void setStatusAuthorizationFlags(List<String> statusAuthorizationFlags) {
this.statusAuthorizationFlags = statusAuthorizationFlags;
} }
} }

View File

@ -13,6 +13,7 @@ import org.opencdmp.data.DescriptionStatusEntity;
import org.opencdmp.data.TenantEntityManager; import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.model.descriptionstatus.DescriptionStatus; import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.query.utils.QueryUtilsService; import org.opencdmp.query.utils.QueryUtilsService;
import org.opencdmp.service.descriptionstatus.DescriptionStatusService;
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;
@ -34,8 +35,14 @@ public class DescriptionStatusQuery extends QueryBase<DescriptionStatusEntity> {
private Collection<UUID> excludeIds; private Collection<UUID> excludeIds;
private Boolean authorizedStatus;
private UUID descriptionId;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final DescriptionStatusService descriptionStatusService;
public DescriptionStatusQuery like(String value) { public DescriptionStatusQuery like(String value) {
this.like = value; this.like = value;
return this; return this;
@ -111,6 +118,12 @@ public class DescriptionStatusQuery extends QueryBase<DescriptionStatusEntity> {
return this; return this;
} }
public DescriptionStatusQuery authorizedStatus(Boolean authorizedStatus, UUID descriptionId) {
this.authorizedStatus = authorizedStatus;
this.descriptionId = descriptionId;
return this;
}
public DescriptionStatusQuery authorize(EnumSet<AuthorizationFlags> values) { public DescriptionStatusQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values; this.authorize = values;
return this; return this;
@ -120,7 +133,8 @@ public class DescriptionStatusQuery extends QueryBase<DescriptionStatusEntity> {
private final TenantEntityManager entityManager; private final TenantEntityManager entityManager;
public DescriptionStatusQuery( public DescriptionStatusQuery(
QueryUtilsService queryUtilsService, TenantEntityManager entityManager) { DescriptionStatusService descriptionStatusService, QueryUtilsService queryUtilsService, TenantEntityManager entityManager) {
this.descriptionStatusService = descriptionStatusService;
this.queryUtilsService = queryUtilsService; this.queryUtilsService = queryUtilsService;
this.entityManager = entityManager; this.entityManager = entityManager;
} }
@ -172,6 +186,15 @@ public class DescriptionStatusQuery extends QueryBase<DescriptionStatusEntity> {
} }
predicates.add(notInClause.not()); predicates.add(notInClause.not());
} }
if (this.authorizedStatus != null && this.authorizedStatus && this.descriptionId != null) {
List<UUID> notAvailableStatusIds = this.descriptionStatusService.getAuthorizedNotAvailableStatusIds(this.descriptionId);
if (!notAvailableStatusIds.isEmpty()) {
CriteriaBuilder.In<UUID> notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionStatusEntity._id));
for (UUID id : notAvailableStatusIds)
notInClause.value(id);
predicates.add(notInClause.not());
}
}
if (!predicates.isEmpty()) { if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray); return queryContext.CriteriaBuilder.and(predicatesArray);

View File

@ -13,6 +13,7 @@ import org.opencdmp.data.PlanStatusEntity;
import org.opencdmp.data.TenantEntityManager; import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.model.planstatus.PlanStatus; import org.opencdmp.model.planstatus.PlanStatus;
import org.opencdmp.query.utils.QueryUtilsService; import org.opencdmp.query.utils.QueryUtilsService;
import org.opencdmp.service.planstatus.PlanStatusService;
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;
@ -34,8 +35,14 @@ public class PlanStatusQuery extends QueryBase<PlanStatusEntity> {
private Collection<UUID> excludedIds; private Collection<UUID> excludedIds;
private Boolean authorizedStatus;
private UUID planId;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final PlanStatusService planStatusService;
public PlanStatusQuery like(String value) { public PlanStatusQuery like(String value) {
this.like = value; this.like = value;
return this; return this;
@ -116,11 +123,18 @@ public class PlanStatusQuery extends QueryBase<PlanStatusEntity> {
return this; return this;
} }
public PlanStatusQuery authorizedStatus(Boolean authorizedStatus, UUID planId) {
this.authorizedStatus = authorizedStatus;
this.planId = planId;
return this;
}
private final QueryUtilsService queryUtilsService; private final QueryUtilsService queryUtilsService;
private final TenantEntityManager entityManager; private final TenantEntityManager entityManager;
public PlanStatusQuery( public PlanStatusQuery(
QueryUtilsService queryUtilsService, TenantEntityManager tenantEntityManager) { PlanStatusService planStatusService, QueryUtilsService queryUtilsService, TenantEntityManager tenantEntityManager) {
this.planStatusService = planStatusService;
this.queryUtilsService = queryUtilsService; this.queryUtilsService = queryUtilsService;
this.entityManager = tenantEntityManager; this.entityManager = tenantEntityManager;
} }
@ -171,6 +185,15 @@ public class PlanStatusQuery extends QueryBase<PlanStatusEntity> {
notInClause.value(item); notInClause.value(item);
predicates.add(notInClause.not()); predicates.add(notInClause.not());
} }
if (this.authorizedStatus != null && this.authorizedStatus && this.planId != null) {
List<UUID> notAvailableStatusIds = this.planStatusService.getAuthorizedNotAvailableStatusIds(this.planId);
if (!notAvailableStatusIds.isEmpty()) {
CriteriaBuilder.In<UUID> notInClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(PlanStatusEntity._id));
for (UUID id : notAvailableStatusIds)
notInClause.value(id);
predicates.add(notInClause.not());
}
}
if (!predicates.isEmpty()) { if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray); return queryContext.CriteriaBuilder.and(predicatesArray);

View File

@ -4,7 +4,10 @@ package org.opencdmp.service.custompolicy;
import gr.cite.tools.cache.CacheService; import gr.cite.tools.cache.CacheService;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity; import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity; import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
import org.opencdmp.event.DescriptionStatusTouchedEvent;
import org.opencdmp.event.PlanStatusTouchedEvent;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.HashMap; import java.util.HashMap;
@ -54,6 +57,16 @@ public class CustomPolicyCacheService extends CacheService<CustomPolicyCacheServ
} }
} }
@EventListener
public void handlePlanTouchedEvent(PlanStatusTouchedEvent event) {
this.evict(this.buildKey(event.getTenantCode()));
}
@EventListener
public void handleDescriptionStatusTouchedEvent(DescriptionStatusTouchedEvent event) {
this.evict(this.buildKey(event.getTenantCode()));
}
@Autowired @Autowired
public CustomPolicyCacheService(CustomPolicyCacheOptions options) { public CustomPolicyCacheService(CustomPolicyCacheOptions options) {
super(options); super(options);

View File

@ -2,7 +2,6 @@ package org.opencdmp.service.custompolicy;
import gr.cite.commons.web.authz.configuration.Permission; import gr.cite.commons.web.authz.configuration.Permission;
import org.opencdmp.commons.enums.PlanUserRole;
import java.util.HashMap; import java.util.HashMap;
import java.util.UUID; import java.util.UUID;
@ -17,7 +16,4 @@ public interface CustomPolicyService {
String getDescriptionStatusCanEditStatusPermission(UUID id); String getDescriptionStatusCanEditStatusPermission(UUID id);
String getPlanStatusCanEditStatusAffiliatedPermission(UUID id, PlanUserRole planUserRole);
String getDescriptionStatusCanEditStatusAffiliatedPermission(UUID id, PlanUserRole planUserRole);
} }

View File

@ -6,7 +6,6 @@ import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
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.scope.tenant.TenantScope; import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity; import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity; import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
@ -44,7 +43,7 @@ public class CustomPolicyServiceImpl implements CustomPolicyService{
HashMap<String, Permission> policies = new HashMap<>(); HashMap<String, Permission> policies = new HashMap<>();
String tenantCode = null; String tenantCode = null;
try { try {
tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : ""; tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : this.tenantScope.getDefaultTenantCode();
} catch (InvalidApplicationException e) { } catch (InvalidApplicationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -84,7 +83,7 @@ public class CustomPolicyServiceImpl implements CustomPolicyService{
HashMap<String, Permission> policies = new HashMap<>(); HashMap<String, Permission> policies = new HashMap<>();
String tenantCode = null; String tenantCode = null;
try { try {
tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : ""; tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : this.tenantScope.getDefaultTenantCode();
} catch (InvalidApplicationException e) { } catch (InvalidApplicationException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -121,21 +120,11 @@ public class CustomPolicyServiceImpl implements CustomPolicyService{
@Override @Override
public String getPlanStatusCanEditStatusPermission(UUID id){ public String getPlanStatusCanEditStatusPermission(UUID id){
return "PlanStatus" + "_" + id + "_" + PlanStatusDefinitionAuthorization._edit; return ("PlanStatus" + "_" + id + "_" + PlanStatusDefinitionAuthorization._edit).toLowerCase();
} }
@Override @Override
public String getDescriptionStatusCanEditStatusPermission(UUID id){ public String getDescriptionStatusCanEditStatusPermission(UUID id){
return "DescriptionStatus" + "_" + id + "_" + DescriptionStatusDefinitionAuthorization._edit; return ("DescriptionStatus" + "_" + id + "_" + DescriptionStatusDefinitionAuthorization._edit).toLowerCase();
}
@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

@ -508,8 +508,7 @@ public class DescriptionServiceImpl implements DescriptionService {
try { try {
this.authorizationService.authorizeForce(this.customPolicyService.getDescriptionStatusCanEditStatusPermission(model.getStatusId())); this.authorizationService.authorizeForce(this.customPolicyService.getDescriptionStatusCanEditStatusPermission(model.getStatusId()));
} catch (Exception e) { } 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.getDescriptionStatusCanEditStatusPermission(model.getStatusId()));
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());

View File

@ -19,5 +19,5 @@ public interface DescriptionStatusService {
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
List<DescriptionStatus> getAvailableTransitionStatuses(UUID descriptionId) throws InvalidApplicationException; List<UUID> getAuthorizedNotAvailableStatusIds(UUID descriptionId);
} }

View File

@ -15,8 +15,10 @@ import gr.cite.tools.logging.MapLogEntry;
import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.JAXBException;
import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.Permission; import org.opencdmp.authorization.Permission;
import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationContentResolver;
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.scope.tenant.TenantScope;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionAuthorizationEntity; import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionAuthorizationEntity;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionAuthorizationItemEntity; import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionAuthorizationItemEntity;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity; import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity;
@ -25,19 +27,20 @@ import org.opencdmp.commons.types.descriptionworkflow.DescriptionWorkflowDefinit
import org.opencdmp.convention.ConventionService; 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.PlanStatusEntity;
import org.opencdmp.data.TenantEntityManager; import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.event.DescriptionStatusTouchedEvent; import org.opencdmp.event.DescriptionStatusTouchedEvent;
import org.opencdmp.event.EventBroker; 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.descriptionstatus.DescriptionStatus; import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionAuthorizationItemPersist; import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionAuthorizationItemPersist;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionAuthorizationPersist; import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionAuthorizationPersist;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionPersist; import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusDefinitionPersist;
import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusPersist; import org.opencdmp.model.persist.descriptionstatus.DescriptionStatusPersist;
import org.opencdmp.query.DescriptionStatusQuery; import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.query.PlanStatusQuery;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService; import org.opencdmp.service.descriptionworkflow.DescriptionWorkflowService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -64,11 +67,15 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
private final ConventionService conventionService; private final ConventionService conventionService;
private final MessageSource messageSource; private final MessageSource messageSource;
private final XmlHandlingService xmlHandlingService; private final XmlHandlingService xmlHandlingService;
private final QueryFactory queryFactory;
private final DescriptionWorkflowService descriptionWorkflowService;
private final EventBroker eventBroker; private final EventBroker eventBroker;
private final TenantScope tenantScope;
private final DescriptionWorkflowService descriptionWorkflowService;
private final CustomPolicyService customPolicyService;
private final AuthorizationService authorizationService;
private final AuthorizationContentResolver authorizationContentResolver;
private final QueryFactory queryFactory;
public DescriptionStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authService, TenantEntityManager entityManager, ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService, QueryFactory queryFactory, DescriptionWorkflowService descriptionWorkflowService, EventBroker eventBroker) { public DescriptionStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authService, TenantEntityManager entityManager, ConventionService conventionService, MessageSource messageSource, XmlHandlingService xmlHandlingService, EventBroker eventBroker, TenantScope tenantScope, DescriptionWorkflowService descriptionWorkflowService, CustomPolicyService customPolicyService, AuthorizationService authorizationService, AuthorizationContentResolver authorizationContentResolver, QueryFactory queryFactory) {
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -77,9 +84,13 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
this.conventionService = conventionService; this.conventionService = conventionService;
this.messageSource = messageSource; this.messageSource = messageSource;
this.xmlHandlingService = xmlHandlingService; this.xmlHandlingService = xmlHandlingService;
this.queryFactory = queryFactory;
this.descriptionWorkflowService = descriptionWorkflowService;
this.eventBroker = eventBroker; this.eventBroker = eventBroker;
this.tenantScope = tenantScope;
this.descriptionWorkflowService = descriptionWorkflowService;
this.customPolicyService = customPolicyService;
this.authorizationService = authorizationService;
this.authorizationContentResolver = authorizationContentResolver;
this.queryFactory = queryFactory;
} }
@ -117,7 +128,7 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
this.entityManager.flush(); this.entityManager.flush();
this.eventBroker.emit(new DescriptionStatusTouchedEvent(data.getId())); this.eventBroker.emit(new DescriptionStatusTouchedEvent(data.getId(), this.tenantScope.getTenantCode()));
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);
} }
@ -164,19 +175,44 @@ public class DescriptionStatusServiceImpl implements DescriptionStatusService {
return data; return data;
} }
public List<DescriptionStatus> getAvailableTransitionStatuses(UUID descriptionId) throws InvalidApplicationException { @Override
DescriptionWorkflowDefinitionEntity definition = this.descriptionWorkflowService.getWorkFlowDefinition(); public List<UUID> getAuthorizedNotAvailableStatusIds(UUID descriptionId) {
DescriptionEntity description = this.entityManager.find(DescriptionEntity.class, descriptionId); List<UUID> notAuthorizedStatusIds = new ArrayList<>();
if (description == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); DescriptionWorkflowDefinitionEntity definition;
DescriptionEntity description;
try {
definition = this.descriptionWorkflowService.getWorkFlowDefinition();
description = this.entityManager.find(DescriptionEntity.class, descriptionId, true);
if (description == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, DescriptionEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
notAuthorizedStatusIds.add(description.getStatusId());
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
List<DescriptionWorkflowDefinitionTransitionEntity> availableTransitions = definition.getStatusTransitions().stream().filter(x -> x.getFromStatusId().equals(description.getStatusId())).collect(Collectors.toList()); List<DescriptionWorkflowDefinitionTransitionEntity> availableTransitions = definition.getStatusTransitions().stream().filter(x -> x.getFromStatusId().equals(description.getStatusId())).collect(Collectors.toList());
if (!this.conventionService.isListNullOrEmpty(availableTransitions)){
DescriptionStatusQuery query = this.queryFactory.query(DescriptionStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActive(IsActive.Active).ids(availableTransitions.stream().map(DescriptionWorkflowDefinitionTransitionEntity::getToStatusId).distinct().toList()); if (!this.conventionService.isListNullOrEmpty(availableTransitions)) {
FieldSet fieldSet = new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._name).ensure(DescriptionStatus._action).ensure(DescriptionStatus._internalStatus); List<UUID> availableStatusIds = availableTransitions.stream().map(DescriptionWorkflowDefinitionTransitionEntity::getToStatusId).collect(Collectors.toList());
return this.builderFactory.builder(DescriptionStatusBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, query.collectAs(fieldSet)); for (UUID statusId: availableStatusIds) {
// add status id with no permission
String editPermission = this.customPolicyService.getDescriptionStatusCanEditStatusPermission(statusId);
Boolean isAllowed = this.authorizationService.authorize(editPermission);
if (!isAllowed) {
isAllowed = this.authorizationService.authorizeAtLeastOne(List.of(this.authorizationContentResolver.planAffiliation(description.getPlanId())), editPermission);
}
if (!isAllowed) notAuthorizedStatusIds.add(statusId);
} }
return new ArrayList<>(); // add status ids that not included in workflow
List<DescriptionStatusEntity> statusEntities = this.queryFactory.query(DescriptionStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActive(IsActive.Active).excludeIds(notAuthorizedStatusIds).collectAs(new BaseFieldSet().ensure(DescriptionStatus._id));
if (!this.conventionService.isListNullOrEmpty(statusEntities)) {
for (DescriptionStatusEntity status: statusEntities) {
if (!availableStatusIds.contains(status.getId())) notAuthorizedStatusIds.add(status.getId());
}
}
}
return notAuthorizedStatusIds;
} }
} }

View File

@ -1617,8 +1617,7 @@ public class PlanServiceImpl implements PlanService {
try { try {
this.authorizationService.authorizeForce(this.customPolicyService.getPlanStatusCanEditStatusPermission(newStatusId)); this.authorizationService.authorizeForce(this.customPolicyService.getPlanStatusCanEditStatusPermission(newStatusId));
} catch (Exception e) { } 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.getPlanStatusCanEditStatusPermission(newStatusId));
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");

View File

@ -1,6 +1,5 @@
package org.opencdmp.service.planstatus; package org.opencdmp.service.planstatus;
import gr.cite.commons.web.authz.configuration.Permission;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException; import gr.cite.tools.exception.MyNotFoundException;
@ -11,7 +10,6 @@ import org.opencdmp.model.persist.planstatus.PlanStatusPersist;
import org.opencdmp.model.planstatus.PlanStatus; import org.opencdmp.model.planstatus.PlanStatus;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -20,5 +18,5 @@ public interface PlanStatusService {
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException;
List<PlanStatus> getAvailableTransitionStatuses(UUID planId) throws InvalidApplicationException; List<UUID> getAuthorizedNotAvailableStatusIds(UUID planId);
} }

View File

@ -1,6 +1,5 @@
package org.opencdmp.service.planstatus; package org.opencdmp.service.planstatus;
import gr.cite.commons.web.authz.configuration.PermissionPolicyContext;
import gr.cite.commons.web.authz.service.AuthorizationService; 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.deleter.DeleterFactory; import gr.cite.tools.data.deleter.DeleterFactory;
@ -17,8 +16,10 @@ import jakarta.xml.bind.JAXBException;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.opencdmp.authorization.AuthorizationFlags; import org.opencdmp.authorization.AuthorizationFlags;
import org.opencdmp.authorization.Permission; import org.opencdmp.authorization.Permission;
import org.opencdmp.authorization.authorizationcontentresolver.AuthorizationContentResolver;
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.scope.tenant.TenantScope;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionAuthorizationEntity; import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionAuthorizationEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionAuthorizationItemEntity; import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionAuthorizationItemEntity;
import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity; import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity;
@ -31,7 +32,6 @@ import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.errorcode.ErrorThesaurusProperties; import org.opencdmp.errorcode.ErrorThesaurusProperties;
import org.opencdmp.event.EventBroker; import org.opencdmp.event.EventBroker;
import org.opencdmp.event.PlanStatusTouchedEvent; import org.opencdmp.event.PlanStatusTouchedEvent;
import org.opencdmp.event.PlanTouchedEvent;
import org.opencdmp.model.builder.planstatus.PlanStatusBuilder; import org.opencdmp.model.builder.planstatus.PlanStatusBuilder;
import org.opencdmp.model.deleter.PlanStatusDeleter; import org.opencdmp.model.deleter.PlanStatusDeleter;
import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationItemPersist; import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationItemPersist;
@ -39,8 +39,8 @@ import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationPe
import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionPersist; import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionPersist;
import org.opencdmp.model.persist.planstatus.PlanStatusPersist; import org.opencdmp.model.persist.planstatus.PlanStatusPersist;
import org.opencdmp.model.planstatus.PlanStatus; import org.opencdmp.model.planstatus.PlanStatus;
import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization;
import org.opencdmp.query.PlanStatusQuery; import org.opencdmp.query.PlanStatusQuery;
import org.opencdmp.service.custompolicy.CustomPolicyService;
import org.opencdmp.service.planworkflow.PlanWorkflowService; import org.opencdmp.service.planworkflow.PlanWorkflowService;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -66,10 +66,14 @@ public class PlanStatusServiceImpl implements PlanStatusService {
private final TenantEntityManager entityManager; private final TenantEntityManager entityManager;
private final MessageSource messageSource; private final MessageSource messageSource;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final QueryFactory queryFactory;
private final PlanWorkflowService planWorkflowService;
private final EventBroker eventBroker; private final EventBroker eventBroker;
public PlanStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authorizationService, ConventionService conventionService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors, EventBroker eventBroker, QueryFactory queryFactory, PlanWorkflowService planWorkflowService) { private final TenantScope tenantScope;
private final PlanWorkflowService planWorkflowService;
private final CustomPolicyService customPolicyService;
private final AuthorizationContentResolver authorizationContentResolver;
private final QueryFactory queryFactory;
public PlanStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authorizationService, ConventionService conventionService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors, EventBroker eventBroker, TenantScope tenantScope, PlanWorkflowService planWorkflowService, CustomPolicyService customPolicyService, AuthorizationContentResolver authorizationContentResolver, QueryFactory queryFactory) {
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -79,9 +83,12 @@ public class PlanStatusServiceImpl implements PlanStatusService {
this.entityManager = entityManager; this.entityManager = entityManager;
this.messageSource = messageSource; this.messageSource = messageSource;
this.errors = errors; this.errors = errors;
this.queryFactory = queryFactory;
this.planWorkflowService = planWorkflowService;
this.eventBroker = eventBroker; this.eventBroker = eventBroker;
this.tenantScope = tenantScope;
this.planWorkflowService = planWorkflowService;
this.customPolicyService = customPolicyService;
this.authorizationContentResolver = authorizationContentResolver;
this.queryFactory = queryFactory;
} }
@Override @Override
@ -118,7 +125,7 @@ public class PlanStatusServiceImpl implements PlanStatusService {
this.entityManager.flush(); this.entityManager.flush();
this.eventBroker.emit(new PlanStatusTouchedEvent(data.getId())); this.eventBroker.emit(new PlanStatusTouchedEvent(data.getId(), this.tenantScope.getTenantCode()));
return this.builderFactory.builder(PlanStatusBuilder.class).build(BaseFieldSet.build(fields, PlanStatus._id), data); return this.builderFactory.builder(PlanStatusBuilder.class).build(BaseFieldSet.build(fields, PlanStatus._id), data);
} }
@ -171,19 +178,44 @@ public class PlanStatusServiceImpl implements PlanStatusService {
return data; return data;
} }
public List<PlanStatus> getAvailableTransitionStatuses(UUID planId) throws InvalidApplicationException { @Override
PlanWorkflowDefinitionEntity definition = this.planWorkflowService.getWorkFlowDefinition(); public List<UUID> getAuthorizedNotAvailableStatusIds(UUID planId) {
PlanEntity plan = this.entityManager.find(PlanEntity.class, planId); List<UUID> notAuthorizedStatusIds = new ArrayList<>();
PlanWorkflowDefinitionEntity definition;
PlanEntity plan;
try {
definition = this.planWorkflowService.getWorkFlowDefinition();
plan = this.entityManager.find(PlanEntity.class, planId, true);
if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, PlanEntity.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (plan == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{planId, PlanEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
notAuthorizedStatusIds.add(plan.getStatusId());
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
List<PlanWorkflowDefinitionTransitionEntity> availableTransitions = definition.getStatusTransitions().stream().filter(x -> x.getFromStatusId().equals(plan.getStatusId())).collect(Collectors.toList()); List<PlanWorkflowDefinitionTransitionEntity> availableTransitions = definition.getStatusTransitions().stream().filter(x -> x.getFromStatusId().equals(plan.getStatusId())).collect(Collectors.toList());
if (!this.conventionService.isListNullOrEmpty(availableTransitions)){
PlanStatusQuery query = this.queryFactory.query(PlanStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActives(IsActive.Active).ids(availableTransitions.stream().map(PlanWorkflowDefinitionTransitionEntity::getToStatusId).distinct().toList()); if (!this.conventionService.isListNullOrEmpty(availableTransitions)) {
FieldSet fieldSet = new BaseFieldSet().ensure(PlanStatus._id).ensure(PlanStatus._name).ensure(PlanStatus._action).ensure(PlanStatus._internalStatus); List<UUID> availableStatusIds = availableTransitions.stream().map(PlanWorkflowDefinitionTransitionEntity::getToStatusId).collect(Collectors.toList());
return this.builderFactory.builder(PlanStatusBuilder.class).authorize(AuthorizationFlags.AllExceptPublic).build(fieldSet, query.collectAs(fieldSet)); for (UUID statusId: availableStatusIds) {
// add status id with no permission
String editPermission = this.customPolicyService.getPlanStatusCanEditStatusPermission(statusId);
Boolean isAllowed = this.authorizationService.authorize(editPermission);
if (!isAllowed) {
isAllowed = this.authorizationService.authorizeAtLeastOne(List.of(this.authorizationContentResolver.planAffiliation(plan.getId())), editPermission);
}
if (!isAllowed) notAuthorizedStatusIds.add(statusId);
} }
return new ArrayList<>(); // add status ids that not included in workflow
List<PlanStatusEntity> statusEntities = this.queryFactory.query(PlanStatusQuery.class).authorize(AuthorizationFlags.AllExceptPublic).isActives(IsActive.Active).excludedIds(notAuthorizedStatusIds).collectAs(new BaseFieldSet().ensure(PlanStatus._id));
if (!this.conventionService.isListNullOrEmpty(statusEntities)) {
for (PlanStatusEntity status: statusEntities) {
if (!availableStatusIds.contains(status.getId())) notAuthorizedStatusIds.add(status.getId());
}
}
}
return notAuthorizedStatusIds;
} }
} }

View File

@ -50,7 +50,7 @@ public class AffiliatedAuthorizationHandler extends AuthorizationHandler<Affilia
boolean hasDescriptionTemplatePermission = policy != null && this.hasPermission(policy.getDescriptionTemplate(), userDescriptionTemplateRoles); boolean hasDescriptionTemplatePermission = policy != null && this.hasPermission(policy.getDescriptionTemplate(), userDescriptionTemplateRoles);
boolean hasPlanCustomPermission = false; boolean hasPlanCustomPermission = false;
if (permission.startsWith("PlanStatus_") || permission.startsWith("DescriptionStatus_")) { if (permission.startsWith(("PlanStatus_").toLowerCase()) || permission.startsWith(("DescriptionStatus_").toLowerCase())) {
HashMap<String, CustomPermissionAttributesProperties. MyPermission> customPolicies = this.opencdmpPermissionPolicyContext.buildAffiliatedCustomPermissions(); HashMap<String, CustomPermissionAttributesProperties. MyPermission> customPolicies = this.opencdmpPermissionPolicyContext.buildAffiliatedCustomPermissions();
if (customPolicies == null || customPolicies.isEmpty()) return ACCESS_DENIED; if (customPolicies == null || customPolicies.isEmpty()) return ACCESS_DENIED;
CustomPermissionAttributesProperties.MyPermission customPolicy = customPolicies.get(permission); CustomPermissionAttributesProperties.MyPermission customPolicy = customPolicies.get(permission);

View File

@ -9,7 +9,7 @@ import org.opencdmp.authorization.CustomPermissionAttributesProperties;
import org.opencdmp.authorization.PlanRole; 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.scope.tenant.TenantScope;
import org.opencdmp.commons.types.descriptionstatus.DescriptionStatusDefinitionEntity; 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.DescriptionStatusEntity;
@ -20,11 +20,13 @@ import org.opencdmp.model.descriptionstatus.DescriptionStatus;
import org.opencdmp.model.planstatus.PlanStatus; import org.opencdmp.model.planstatus.PlanStatus;
import org.opencdmp.query.DescriptionStatusQuery; import org.opencdmp.query.DescriptionStatusQuery;
import org.opencdmp.query.PlanStatusQuery; import org.opencdmp.query.PlanStatusQuery;
import org.opencdmp.service.custompolicy.CustomPolicyCacheService;
import org.opencdmp.service.custompolicy.CustomPolicyService; 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 javax.management.InvalidApplicationException;
import java.util.*; import java.util.*;
@ -33,13 +35,17 @@ public class OpencdmpPermissionPolicyContextImpl extends PermissionPolicyContext
private final CustomPolicyService customPolicyService; private final CustomPolicyService customPolicyService;
private final QueryFactory queryFactory; private final QueryFactory queryFactory;
private final XmlHandlingService xmlHandlingService; private final XmlHandlingService xmlHandlingService;
private final CustomPolicyCacheService customPolicyCacheService;
private final TenantScope tenantScope;
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, CustomPolicyService customPolicyService, QueryFactory queryFactory, XmlHandlingService xmlHandlingService) { public OpencdmpPermissionPolicyContextImpl(AuthorizationConfiguration authorizationConfiguration, CustomPolicyService customPolicyService, QueryFactory queryFactory, XmlHandlingService xmlHandlingService, CustomPolicyCacheService customPolicyCacheService, TenantScope tenantScope) {
super(authorizationConfiguration); super(authorizationConfiguration);
this.customPolicyService = customPolicyService; this.customPolicyService = customPolicyService;
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.xmlHandlingService = xmlHandlingService; this.xmlHandlingService = xmlHandlingService;
this.customPolicyCacheService = customPolicyCacheService;
this.tenantScope = tenantScope;
} }
@EventListener @EventListener
@ -65,34 +71,77 @@ public class OpencdmpPermissionPolicyContextImpl extends PermissionPolicyContext
public HashMap<String, CustomPermissionAttributesProperties.MyPermission> buildAffiliatedCustomPermissions() { public HashMap<String, CustomPermissionAttributesProperties.MyPermission> buildAffiliatedCustomPermissions() {
HashMap<String, CustomPermissionAttributesProperties.MyPermission> affiliatedCustomPermissions = new HashMap<>(); HashMap<String, CustomPermissionAttributesProperties.MyPermission> affiliatedCustomPermissions = new HashMap<>();
String tenantCode = null;
try {
tenantCode = this.tenantScope.isSet() && this.tenantScope.isMultitenant() ? this.tenantScope.getTenantCode() : this.tenantScope.getDefaultTenantCode();
} catch (InvalidApplicationException e) {
throw new RuntimeException(e);
}
CustomPolicyCacheService.CustomPolicyCacheValue cacheValue = this.customPolicyCacheService.lookup(this.customPolicyCacheService.buildKey(tenantCode));
this.buildAffiliatedPlanCustomPermissions(tenantCode, cacheValue, affiliatedCustomPermissions);
this.buildAffiliatedDescriptionCustomPermissions(tenantCode, cacheValue, affiliatedCustomPermissions);
return affiliatedCustomPermissions;
}
private void buildAffiliatedPlanCustomPermissions(String tenantCode, CustomPolicyCacheService.CustomPolicyCacheValue cacheValue, HashMap<String, CustomPermissionAttributesProperties.MyPermission> affiliatedCustomPermissions) {
if (cacheValue == null || cacheValue.getPlanStatusDefinitionMap() == null) {
Map<UUID, PlanStatusDefinitionEntity> definitionStatusMap = new HashMap<>();
List<PlanStatusEntity> planStatusEntities = 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));
if (planStatusEntities != null) { if (planStatusEntities != null) {
for (PlanStatusEntity entity : planStatusEntities) { for (PlanStatusEntity entity : planStatusEntities) {
PlanStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(PlanStatusDefinitionEntity.class, entity.getDefinition()); PlanStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(PlanStatusDefinitionEntity.class, entity.getDefinition());
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null && definition.getAuthorization().getEdit().getPlanRoles() != null){ if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null && definition.getAuthorization().getEdit().getPlanRoles() != null){
for (PlanUserRole planUserRole: definition.getAuthorization().getEdit().getPlanRoles()) { CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(new PlanRole(new HashSet<>(definition.getAuthorization().getEdit().getPlanRoles())), null);
PlanRole planRole = new PlanRole(new HashSet<>(List.of(planUserRole))); affiliatedCustomPermissions.put(this.customPolicyService.getPlanStatusCanEditStatusPermission(entity.getId()), myPermission);
CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(planRole, null); }
affiliatedCustomPermissions.put(this.customPolicyService.getPlanStatusCanEditStatusAffiliatedPermission(entity.getId(), planUserRole), myPermission); }
}
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) {
CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(new PlanRole(new HashSet<>(definition.getAuthorization().getEdit().getPlanRoles())), null);
affiliatedCustomPermissions.put(this.customPolicyService.getPlanStatusCanEditStatusPermission(statusId), myPermission);
} }
} }
} }
} }
private void buildAffiliatedDescriptionCustomPermissions(String tenantCode, CustomPolicyCacheService.CustomPolicyCacheValue cacheValue, HashMap<String, CustomPermissionAttributesProperties.MyPermission> affiliatedCustomPermissions) {
if (cacheValue == null || cacheValue.getDescriptionStatusDefinitionMap() == null) {
Map<UUID, DescriptionStatusDefinitionEntity> definitionStatusMap = new HashMap<>();
List<DescriptionStatusEntity> descriptionStatusEntities = this.queryFactory.query(DescriptionStatusQuery.class).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._definition)); List<DescriptionStatusEntity> descriptionStatusEntities = this.queryFactory.query(DescriptionStatusQuery.class).isActive(IsActive.Active).collectAs(new BaseFieldSet().ensure(DescriptionStatus._id).ensure(DescriptionStatus._definition));
if (descriptionStatusEntities != null) { if (descriptionStatusEntities != null) {
for (DescriptionStatusEntity entity : descriptionStatusEntities) { for (DescriptionStatusEntity entity : descriptionStatusEntities) {
DescriptionStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DescriptionStatusDefinitionEntity.class, entity.getDefinition()); DescriptionStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(DescriptionStatusDefinitionEntity.class, entity.getDefinition());
if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null && definition.getAuthorization().getEdit().getPlanRoles() != null){ if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null && definition.getAuthorization().getEdit().getPlanRoles() != null){
for (PlanUserRole planUserRole: definition.getAuthorization().getEdit().getPlanRoles()) { CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(new PlanRole(new HashSet<>(definition.getAuthorization().getEdit().getPlanRoles())), null);
PlanRole planRole = new PlanRole(new HashSet<>(List.of(planUserRole))); affiliatedCustomPermissions.put(this.customPolicyService.getDescriptionStatusCanEditStatusPermission(entity.getId()), myPermission);
CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(planRole, null);
affiliatedCustomPermissions.put(this.customPolicyService.getDescriptionStatusCanEditStatusAffiliatedPermission(entity.getId(), planUserRole), myPermission);
}
}
}
}
return affiliatedCustomPermissions; }
}
}
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) {
CustomPermissionAttributesProperties.MyPermission myPermission = new CustomPermissionAttributesProperties.MyPermission(new PlanRole(new HashSet<>(definition.getAuthorization().getEdit().getPlanRoles())), null);
affiliatedCustomPermissions.put(this.customPolicyService.getDescriptionStatusCanEditStatusPermission(statusId), myPermission);
}
}
}
} }
} }

View File

@ -8,6 +8,7 @@ import { Tag, TagPersist } from "../tag/tag";
import { User } from "../user/user"; import { User } from "../user/user";
import { AppPermission } from "@app/core/common/enum/permission.enum"; import { AppPermission } from "@app/core/common/enum/permission.enum";
import { DescriptionStatus, DescriptionStatusDefinition } from "../description-status/description-status"; import { DescriptionStatus, DescriptionStatusDefinition } from "../description-status/description-status";
import { DescriptionStatusPermission } from "@app/core/common/enum/description-status-permission.enum";
export interface Description extends BaseDescription { export interface Description extends BaseDescription {
label?: string; label?: string;
@ -21,7 +22,9 @@ export interface Description extends BaseDescription {
descriptionTemplate?: DescriptionTemplate; descriptionTemplate?: DescriptionTemplate;
planDescriptionTemplate?: PlanDescriptionTemplate; planDescriptionTemplate?: PlanDescriptionTemplate;
plan?: Plan; plan?: Plan;
availableStatuses?: DescriptionStatus[];
authorizationFlags?: AppPermission[]; authorizationFlags?: AppPermission[];
statusAuthorizationFlags?: DescriptionStatusPermission[];
} }

View File

@ -15,6 +15,7 @@ import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum';
import { EntityType } from '@app/core/common/enum/entity-type'; import { EntityType } from '@app/core/common/enum/entity-type';
import { PlanStatus } from '../plan-status/plan-status'; import { PlanStatus } from '../plan-status/plan-status';
import { PlanStatusPermission } from '@app/core/common/enum/plan-status-permission.enum';
export interface BasePlan extends BaseEntity { export interface BasePlan extends BaseEntity {
label?: string; label?: string;
@ -41,7 +42,9 @@ export interface Plan extends BasePlan {
descriptions?: Description[]; descriptions?: Description[];
planDescriptionTemplates?: PlanDescriptionTemplate[]; planDescriptionTemplates?: PlanDescriptionTemplate[];
otherPlanVersions?: Plan[]; otherPlanVersions?: Plan[];
availableStatuses?: PlanStatus[];
authorizationFlags?: AppPermission[]; authorizationFlags?: AppPermission[];
statusAuthorizationFlags?: PlanStatusPermission[];
} }
export interface PublicPlan extends BasePlan { export interface PublicPlan extends BasePlan {

View File

@ -58,15 +58,6 @@ export class DescriptionStatusService {
catchError((error: any) => throwError(() => error))); catchError((error: any) => throwError(() => error)));
} }
getAvailableTransitions(descriptionId: Guid, reqFields: string[] = []): Observable<Array<DescriptionStatus>> {
const url = `${this.apiBase}/available-transitions/${descriptionId}`;
const options = { params: { f: reqFields } };
return this.http
.get<Array<DescriptionStatus>>(url, options).pipe(
catchError((error: any) => throwError(() => error)));
}
// tslint:disable-next-line: member-ordering // tslint:disable-next-line: member-ordering
singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
initialItems: (data?: any) => this.query(this.buildAutocompleteLookup([IsActive.Active])).pipe(map(x => x.items)), initialItems: (data?: any) => this.query(this.buildAutocompleteLookup([IsActive.Active])).pipe(map(x => x.items)),

View File

@ -58,15 +58,6 @@ export class PlanStatusService {
catchError((error: any) => throwError(() => error))); catchError((error: any) => throwError(() => error)));
} }
getAvailableTransitions(planId: Guid, reqFields: string[] = []): Observable<Array<PlanStatus>> {
const url = `${this.apiBase}/available-transitions/${planId}`;
const options = { params: { f: reqFields } };
return this.http
.get<Array<PlanStatus>>(url, options).pipe(
catchError((error: any) => throwError(() => error)));
}
// tslint:disable-next-line: member-ordering // tslint:disable-next-line: member-ordering
singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = {
initialItems: (data?: any) => this.query(this.buildAutocompleteLookup([IsActive.Active])).pipe(map(x => x.items)), initialItems: (data?: any) => this.query(this.buildAutocompleteLookup([IsActive.Active])).pipe(map(x => x.items)),

View File

@ -67,8 +67,8 @@
<button [disabled]="saving" mat-menu-item (click)="saveAndClose()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CLOSE' | translate }}</button> <button [disabled]="saving" mat-menu-item (click)="saveAndClose()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CLOSE' | translate }}</button>
<button [disabled]="saving" mat-menu-item (click)="saveAndContinue()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CONTINUE' | translate }}</button> <button [disabled]="saving" mat-menu-item (click)="saveAndContinue()" type="button">{{ 'DESCRIPTION-EDITOR.ACTIONS.SAVE-AND-CONTINUE' | translate }}</button>
</mat-menu> </mat-menu>
<ng-container *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked && item.id && isNotFinalizedPlan()"> <ng-container *ngIf="canEditStatus && !isNew && item.availableStatuses && item.availableStatuses.length > 0 && !isLocked && item.id && isNotFinalizedPlan()">
<button *ngFor='let status of availableStatusesTransitions' [disabled]="saving" mat-button class="rounded-btn neutral mr-2" type="button" (click)="persistStatus(status)">{{ status.action?.length > 0 ? status.action : status.name }}</button> <button *ngFor='let status of item.availableStatuses' [disabled]="saving" mat-button class="rounded-btn neutral mr-2" type="button" (click)="persistStatus(status)">{{ status.action?.length > 0 ? status.action : status.name }}</button>
</ng-container> </ng-container>
<button [disabled]="saving" *ngIf="isLocked" mat-button disabled class="rounded-btn neutral cursor-default" type="button">{{ 'PLAN-OVERVIEW.LOCKED' | translate}}</button> <button [disabled]="saving" *ngIf="isLocked" mat-button disabled class="rounded-btn neutral cursor-default" type="button">{{ 'PLAN-OVERVIEW.LOCKED' | translate}}</button>
</div> </div>

View File

@ -45,9 +45,9 @@ import { DescriptionEditorEntityResolver } from './resolvers/description-editor-
import { ToCEntry } from './table-of-contents/models/toc-entry'; import { ToCEntry } from './table-of-contents/models/toc-entry';
import { TableOfContentsService } from './table-of-contents/services/table-of-contents-service'; import { TableOfContentsService } from './table-of-contents/services/table-of-contents-service';
import { TableOfContentsComponent } from './table-of-contents/table-of-contents.component'; import { TableOfContentsComponent } from './table-of-contents/table-of-contents.component';
import { DescriptionStatusService } from '@app/core/services/description-status/description-status.service';
import { DescriptionStatus } from '@app/core/model/description-status/description-status'; import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { DescriptionStatusAvailableActionType } from '@app/core/common/enum/description-status-available-action-type'; import { DescriptionStatusAvailableActionType } from '@app/core/common/enum/description-status-available-action-type';
import { DescriptionStatusPermission } from '@app/core/common/enum/description-status-permission.enum';
@Component({ @Component({
selector: 'app-description-editor-component', selector: 'app-description-editor-component',
@ -86,7 +86,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
private initialTemplateId: string = Guid.EMPTY; private initialTemplateId: string = Guid.EMPTY;
private permissionPerSection: Map<Guid, string[]>; private permissionPerSection: Map<Guid, string[]>;
availableStatusesTransitions: DescriptionStatus[];
oldStatusId: Guid; oldStatusId: Guid;
constructor( constructor(
@ -118,7 +117,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
private tableOfContentsService: TableOfContentsService, private tableOfContentsService: TableOfContentsService,
private descriptionFormService: DescriptionFormService, private descriptionFormService: DescriptionFormService,
private formAnnotationService: FormAnnotationService, private formAnnotationService: FormAnnotationService,
private descriptionStatusService: DescriptionStatusService
) { ) {
const descriptionLabel: string = route.snapshot.data['entity']?.label; const descriptionLabel: string = route.snapshot.data['entity']?.label;
if (descriptionLabel) { if (descriptionLabel) {
@ -198,7 +196,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
} }
if (this.route.snapshot.url[1] && this.route.snapshot.url[1].path == 'finalize' && !this.lockStatus && !this.viewOnly) { if (this.route.snapshot.url[1] && this.route.snapshot.url[1].path == 'finalize' && !this.lockStatus && !this.viewOnly) {
setTimeout(() => { setTimeout(() => {
const finalizedStatus = this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) || null; const finalizedStatus = this.item.availableStatuses?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) || null;
if (finalizedStatus) this.finalize(finalizedStatus.id); if (finalizedStatus) this.finalize(finalizedStatus.id);
}, 0); }, 0);
} }
@ -225,7 +223,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
try { try {
this.editorModel = data ? new DescriptionEditorModel().fromModel(data, data.descriptionTemplate) : new DescriptionEditorModel(); this.editorModel = data ? new DescriptionEditorModel().fromModel(data, data.descriptionTemplate) : new DescriptionEditorModel();
if (data) { if (data) {
if (data.id) this.getAvailableStatuses(data.id);
if (data.status?.id) this.oldStatusId = data.status.id if (data.status?.id) this.oldStatusId = data.status.id
if (data.status?.definition?.availableActions?.filter(x => x === DescriptionStatusAvailableActionType.Export).length > 0) this.canExport = true; if (data.status?.definition?.availableActions?.filter(x => x === DescriptionStatusAvailableActionType.Export).length > 0) this.canExport = true;
} }
@ -270,15 +267,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
this.registerFormListeners(); this.registerFormListeners();
} }
getAvailableStatuses(id: Guid){
this.descriptionStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed))
.subscribe(
(statuses) => {
this.availableStatusesTransitions = statuses;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
); }
calculateMultiplicityRejectedPlanDescriptionTemplates(section: PlanBlueprintDefinitionSection, descriptions: Description[]): PlanDescriptionTemplate[] { calculateMultiplicityRejectedPlanDescriptionTemplates(section: PlanBlueprintDefinitionSection, descriptions: Description[]): PlanDescriptionTemplate[] {
if (section.descriptionTemplates?.length > 0) { if (section.descriptionTemplates?.length > 0) {
descriptions = descriptions?.filter(x => x?.planDescriptionTemplate?.sectionId === section.id) || []; descriptions = descriptions?.filter(x => x?.planDescriptionTemplate?.sectionId === section.id) || [];
@ -325,7 +313,7 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
persistEntity(onSuccess?: (response) => void): void { persistEntity(onSuccess?: (response) => void): void {
const formData = this.formService.getValue(this.formGroup.value) as DescriptionPersist; const formData = this.formService.getValue(this.formGroup.value) as DescriptionPersist;
const finalizedStatus = this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) || null; const finalizedStatus = this.item.availableStatuses?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) || null;
this.descriptionService.persist(formData) this.descriptionService.persist(formData)
.pipe(takeUntil(this._destroyed)).subscribe( .pipe(takeUntil(this._destroyed)).subscribe(
@ -649,6 +637,10 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
return errorsCount; return errorsCount;
} }
get canEditStatus(): boolean{
return this.item.statusAuthorizationFlags?.some(x => x.toLowerCase() === DescriptionStatusPermission.Edit.toLowerCase())
}
registerFormListeners() { registerFormListeners() {
this.formGroup.get('descriptionTemplateId').valueChanges this.formGroup.get('descriptionTemplateId').valueChanges

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { DescriptionStatusPermission } from '@app/core/common/enum/description-status-permission.enum';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionStatus, DescriptionStatusDefinition } from '@app/core/model/description-status/description-status'; import { DescriptionStatus, DescriptionStatusDefinition } from '@app/core/model/description-status/description-status';
@ -64,6 +65,8 @@ export class DescriptionEditorEntityResolver extends BaseEditorResolver {
[nameof<Description>(x => x.authorizationFlags), AppPermission.FinalizeDescription].join('.'), [nameof<Description>(x => x.authorizationFlags), AppPermission.FinalizeDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.AnnotateDescription].join('.'), [nameof<Description>(x => x.authorizationFlags), AppPermission.AnnotateDescription].join('.'),
[nameof<Description>(x => x.statusAuthorizationFlags), DescriptionStatusPermission.Edit].join('.'),
[nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.id)].join('.'), [nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.id)].join('.'),
[nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.sectionId)].join('.'), [nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.sectionId)].join('.'),
[nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.isActive)].join('.'), [nameof<Description>(x => x.planDescriptionTemplate), nameof<PlanDescriptionTemplate>(x => x.isActive)].join('.'),
@ -103,7 +106,12 @@ export class DescriptionEditorEntityResolver extends BaseEditorResolver {
nameof<Description>(x => x.createdAt), nameof<Description>(x => x.createdAt),
nameof<Description>(x => x.hash), nameof<Description>(x => x.hash),
nameof<Description>(x => x.isActive) nameof<Description>(x => x.isActive),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.action)].join('.'),
] ]
} }

View File

@ -134,8 +134,8 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="frame mb-3 pt-4 pl-4 pr-5 pb-3"> <div class="frame mb-3 pt-4 pl-4 pr-5 pb-3">
<ng-container *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked && isNotFinalizedPlan(description)"> <ng-container *ngIf="canEditStatus && description.availableStatuses && description.availableStatuses.length > 0 && !isLocked && isNotFinalizedPlan(description)">
<div *ngFor='let status of availableStatusesTransitions'> <div *ngFor='let status of description.availableStatuses'>
<div class="row align-items-center" (click)="persistStatus(status, description)"> <div class="row align-items-center" (click)="persistStatus(status, description)">
<div class="col-auto pr-0"> <div class="col-auto pr-0">
<button *ngIf="status.internalStatus === descriptionStatusEnum.Finalized && description.status?.internalStatus != descriptionStatusEnum.Finalized" mat-mini-fab class="finalize-btn"> <button *ngIf="status.internalStatus === descriptionStatusEnum.Finalized && description.status?.internalStatus != descriptionStatusEnum.Finalized" mat-mini-fab class="finalize-btn">

View File

@ -45,9 +45,9 @@ import { nameof } from 'ts-simple-nameof';
import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component'; import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service'; import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { DescriptionStatus, DescriptionStatusDefinition } from '@app/core/model/description-status/description-status'; import { DescriptionStatus, DescriptionStatusDefinition } from '@app/core/model/description-status/description-status';
import { DescriptionStatusService } from '@app/core/services/description-status/description-status.service';
import { PlanStatus } from '@app/core/model/plan-status/plan-status'; import { PlanStatus } from '@app/core/model/plan-status/plan-status';
import { DescriptionStatusAvailableActionType } from '@app/core/common/enum/description-status-available-action-type'; import { DescriptionStatusAvailableActionType } from '@app/core/common/enum/description-status-available-action-type';
import { DescriptionStatusPermission } from '@app/core/common/enum/description-status-permission.enum';
@Component({ @Component({
@ -77,7 +77,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
canFinalize = false; canFinalize = false;
canAnnotate = false; canAnnotate = false;
canInvitePlanUsers = false; canInvitePlanUsers = false;
availableStatusesTransitions: DescriptionStatus[];
get canAssignPlanUsers(): boolean { get canAssignPlanUsers(): boolean {
const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : []; const authorizationFlags = !this.isPublicView ? (this.description?.plan as Plan)?.authorizationFlags : [];
return (authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers)) && return (authorizationFlags?.some(x => x === AppPermission.InvitePlanUsers) || this.authentication.hasPermission(AppPermission.InvitePlanUsers)) &&
@ -111,7 +110,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
private breadcrumbService: BreadcrumbService, private breadcrumbService: BreadcrumbService,
private httpErrorHandlingService: HttpErrorHandlingService, private httpErrorHandlingService: HttpErrorHandlingService,
private userService: UserService, private userService: UserService,
private descriptionStatusService: DescriptionStatusService
) { ) {
super(); super();
} }
@ -141,7 +139,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label); this.breadcrumbService.addIdResolvedValue(data.id.toString(), data.label);
this.description = data; this.description = data;
this.getAvailableStatuses(this.description.id);
this.description.plan.planUsers = this.isActive || this.description.plan.isActive === IsActive.Active ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers; this.description.plan.planUsers = this.isActive || this.description.plan.isActive === IsActive.Active ? data.plan.planUsers.filter(x => x.isActive === IsActive.Active) : data.plan.planUsers;
this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]); this.researchers = this.referenceService.getReferencesForTypes(this.description?.plan?.planReferences, [this.referenceTypeService.getResearcherReferenceType()]);
this.checkLockStatus(this.description.id); this.checkLockStatus(this.description.id);
@ -228,17 +225,12 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
return this.description?.status?.definition?.availableActions?.filter(x => x === DescriptionStatusAvailableActionType.Export).length > 0; return this.description?.status?.definition?.availableActions?.filter(x => x === DescriptionStatusAvailableActionType.Export).length > 0;
} }
getAvailableStatuses(id: Guid){ get canEditStatus(): boolean{
this.descriptionStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed)) return (this.description as Description).statusAuthorizationFlags?.some(x => x.toLowerCase() === DescriptionStatusPermission.Edit.toLowerCase())
.subscribe( }
(statuses) => {
this.availableStatusesTransitions = statuses;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
); }
hasAvailableFinalizeStatus() { hasAvailableFinalizeStatus() {
return this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) != null; return (this.description as Description).availableStatuses?.find(x => x.internalStatus === DescriptionStatusEnum.Finalized) != null;
} }
checkLockStatus(id: Guid) { checkLockStatus(id: Guid) {
@ -524,7 +516,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
} }
hasReversableStatus(description: Description): boolean { hasReversableStatus(description: Description): boolean {
return description.plan.status.internalStatus == PlanStatusEnum.Draft && description?.status?.internalStatus == DescriptionStatusEnum.Finalized && this.canFinalize && this.availableStatusesTransitions?.find(x => x.internalStatus === DescriptionStatusEnum.Draft) != null return description.plan.status.internalStatus == PlanStatusEnum.Draft && description?.status?.internalStatus == DescriptionStatusEnum.Finalized && this.canFinalize && (this.description as Description).availableStatuses?.find(x => x.internalStatus === DescriptionStatusEnum.Draft) != null
} }
reverseFinalization(description: Description, statusId: Guid) { reverseFinalization(description: Description, statusId: Guid) {
@ -578,6 +570,8 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
[nameof<Description>(x => x.authorizationFlags), AppPermission.InvitePlanUsers].join('.'), [nameof<Description>(x => x.authorizationFlags), AppPermission.InvitePlanUsers].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.AnnotateDescription].join('.'), [nameof<Description>(x => x.authorizationFlags), AppPermission.AnnotateDescription].join('.'),
[nameof<Description>(x => x.statusAuthorizationFlags), DescriptionStatusPermission.Edit].join('.'),
[nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.id)].join('.'), [nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.id)].join('.'),
[nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.label)].join('.'), [nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.label)].join('.'),
[nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'), [nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.groupId)].join('.'),
@ -610,6 +604,11 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.planReferences), nameof<PlanReference>(x => x.reference), nameof<Reference>(x => x.source)].join('.'), [nameof<Description>(x => x.plan), nameof<Plan>(x => x.planReferences), nameof<PlanReference>(x => x.reference), nameof<Reference>(x => x.source)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.planReferences), nameof<PlanReference>(x => x.reference), nameof<Reference>(x => x.reference)].join('.'), [nameof<Description>(x => x.plan), nameof<Plan>(x => x.planReferences), nameof<PlanReference>(x => x.reference), nameof<Reference>(x => x.reference)].join('.'),
[nameof<Description>(x => x.plan), nameof<Plan>(x => x.planReferences), nameof<PlanReference>(x => x.isActive)].join('.'), [nameof<Description>(x => x.plan), nameof<Plan>(x => x.planReferences), nameof<PlanReference>(x => x.isActive)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.id)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.name)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.internalStatus)].join('.'),
[nameof<Description>(x => x.availableStatuses), nameof<DescriptionStatus>(x => x.action)].join('.'),
] ]
} }

View File

@ -181,8 +181,8 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="frame mb-3 pt-4 pl-4 pr-5 pb-3"> <div class="frame mb-3 pt-4 pl-4 pr-5 pb-3">
<ng-container *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && !isLocked && plan.versionStatus != planVersionStatusEnum.Previous && hasDoi(plan) && plan.belongsToCurrentTenant != false"> <ng-container *ngIf="canEditStatus && plan.availableStatuses && plan.availableStatuses.length > 0 && !isLocked && plan.versionStatus != planVersionStatusEnum.Previous && hasDoi(plan) && plan.belongsToCurrentTenant != false">
<div *ngFor='let status of availableStatusesTransitions'> <div *ngFor='let status of plan.availableStatuses'>
<div class="row align-items-center" (click)="persistStatus(status)"> <div class="row align-items-center" (click)="persistStatus(status)">
<div class="col-auto pr-0"> <div class="col-auto pr-0">
<button *ngIf="status.internalStatus === descriptionStatusEnum.Finalized && plan.status?.internalStatus != planStatusEnum.Finalized" mat-mini-fab class="finalize-btn"> <button *ngIf="status.internalStatus === descriptionStatusEnum.Finalized && plan.status?.internalStatus != planStatusEnum.Finalized" mat-mini-fab class="finalize-btn">

View File

@ -52,8 +52,8 @@ import { NewVersionPlanDialogComponent } from '../new-version-dialog/plan-new-ve
import { RouterUtilsService } from '@app/core/services/router/router-utils.service'; import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { DescriptionStatus } from '@app/core/model/description-status/description-status'; import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { PlanStatus, PlanStatusDefinition } from '@app/core/model/plan-status/plan-status'; import { PlanStatus, PlanStatusDefinition } from '@app/core/model/plan-status/plan-status';
import { PlanStatusService } from '@app/core/services/plan/plan-status.service';
import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type'; import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type';
import { PlanStatusPermission } from '@app/core/common/enum/plan-status-permission.enum';
@Component({ @Component({
selector: 'app-plan-overview', selector: 'app-plan-overview',
@ -87,7 +87,6 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
authorFocus: string; authorFocus: string;
userName: string; userName: string;
availableStatusesTransitions: PlanStatus[];
constructor( constructor(
public routerUtils: RouterUtilsService, public routerUtils: RouterUtilsService,
@ -113,7 +112,6 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
private breadcrumbService: BreadcrumbService, private breadcrumbService: BreadcrumbService,
private httpErrorHandlingService: HttpErrorHandlingService, private httpErrorHandlingService: HttpErrorHandlingService,
private userService: UserService, private userService: UserService,
private pLanStatusService: PlanStatusService
) { ) {
super(); super();
} }
@ -136,7 +134,6 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label); this.breadcrumbService.addIdResolvedValue(data.id?.toString(), data.label);
this.plan = data; this.plan = data;
this.getAvailableStatuses(this.plan.id);
this.plan.planUsers = this.isActive ? data?.planUsers?.filter((x) => x.isActive === IsActive.Active) : data?.planUsers; this.plan.planUsers = this.isActive ? data?.planUsers?.filter((x) => x.isActive === IsActive.Active) : data?.planUsers;
this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null; this.plan.otherPlanVersions = data.otherPlanVersions?.filter(x => x.isActive === IsActive.Active) || null;
if (this.plan.descriptions && this.isActive) { if (this.plan.descriptions && this.isActive) {
@ -237,14 +234,9 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
return this.language.instant('PLAN-OVERVIEW.INFOS.UNAUTHORIZED-ORCID'); return this.language.instant('PLAN-OVERVIEW.INFOS.UNAUTHORIZED-ORCID');
} }
getAvailableStatuses(id: Guid){ get canEditStatus(): boolean{
this.pLanStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed)) return (this.plan as Plan).statusAuthorizationFlags ?.some(x => x.toLowerCase() === PlanStatusPermission.Edit.toLowerCase())
.subscribe( }
(statuses) => {
this.availableStatusesTransitions = statuses;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
); }
onFetchingDeletedCallbackError(redirectRoot: string) { onFetchingDeletedCallbackError(redirectRoot: string) {
this.router.navigate([this.routerUtils.generateUrl(redirectRoot)]); this.router.navigate([this.routerUtils.generateUrl(redirectRoot)]);
@ -691,6 +683,7 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
[nameof<Plan>(x => x.authorizationFlags), AppPermission.AssignPlanUsers].join('.'), [nameof<Plan>(x => x.authorizationFlags), AppPermission.AssignPlanUsers].join('.'),
[nameof<Plan>(x => x.authorizationFlags), AppPermission.EditPlan].join('.'), [nameof<Plan>(x => x.authorizationFlags), AppPermission.EditPlan].join('.'),
[nameof<Plan>(x => x.authorizationFlags), AppPermission.DepositPlan].join('.'), [nameof<Plan>(x => x.authorizationFlags), AppPermission.DepositPlan].join('.'),
[nameof<Plan>(x => x.statusAuthorizationFlags), PlanStatusPermission.Edit].join('.'),
[nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'), [nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'),
[nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'), [nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'),
[nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'), [nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),
@ -734,6 +727,11 @@ export class PlanOverviewComponent extends BaseComponent implements OnInit {
[nameof<Plan>(x => x.otherPlanVersions), nameof<Plan>(x => x.version)].join('.'), [nameof<Plan>(x => x.otherPlanVersions), nameof<Plan>(x => x.version)].join('.'),
[nameof<Plan>(x => x.otherPlanVersions), nameof<Plan>(x => x.isActive)].join('.'), [nameof<Plan>(x => x.otherPlanVersions), nameof<Plan>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.id)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.name)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.action)].join('.'),
nameof<Plan>(x => x.hash), nameof<Plan>(x => x.hash),
] ]
} }

View File

@ -52,8 +52,8 @@
<button [disabled]="saving" mat-menu-item (click)="formSubmit()" type="button">{{ 'PLAN-EDITOR.ACTIONS.SAVE-AND-CONTINUE' | translate }}</button> <button [disabled]="saving" mat-menu-item (click)="formSubmit()" type="button">{{ 'PLAN-EDITOR.ACTIONS.SAVE-AND-CONTINUE' | translate }}</button>
</mat-menu> </mat-menu>
</div> </div>
<div *ngIf="availableStatusesTransitions && availableStatusesTransitions.length > 0 && item.versionStatus != planVersionStatusEnum.Previous &&!isLocked && !isNew && hasNotDoi()" class="col-auto d-flex align-items-center" [matTooltipDisabled]="formGroup.pristine" matTooltip="{{'PLAN-EDITOR.ACTIONS.FINALIZE.CAN-NOT-FINALIZE' | translate}}"> <div *ngIf="canEditStatus && !isNew && item.availableStatuses && item.availableStatuses.length > 0 && item.versionStatus != planVersionStatusEnum.Previous &&!isLocked && !isNew && hasNotDoi()" class="col-auto d-flex align-items-center" [matTooltipDisabled]="formGroup.pristine" matTooltip="{{'PLAN-EDITOR.ACTIONS.FINALIZE.CAN-NOT-FINALIZE' | translate}}">
<button *ngFor='let status of availableStatusesTransitions' [disabled]="saving || !formGroup.pristine" mat-button class="rounded-btn primary-inverted mr-2" type="button" (click)="persistStatus(status)">{{ status.action?.length > 0 ? status.action : status.name }}</button> <button *ngFor='let status of item.availableStatuses' [disabled]="saving || !formGroup.pristine" mat-button class="rounded-btn primary-inverted mr-2" type="button" (click)="persistStatus(status)">{{ status.action?.length > 0 ? status.action : status.name }}</button>
</div> </div>
<div *ngIf="isLocked" class="col-auto d-flex align-items-center"> <div *ngIf="isLocked" class="col-auto d-flex align-items-center">
<button class="col-auto d-flex align-items-center" [disabled]="saving" mat-button class="rounded-btn primary-inverted mr-2" type="button">{{ 'PLAN-EDITOR.ACTIONS.LOCKED' | translate}}</button> <button class="col-auto d-flex align-items-center" [disabled]="saving" mat-button class="rounded-btn primary-inverted mr-2" type="button">{{ 'PLAN-EDITOR.ACTIONS.LOCKED' | translate}}</button>

View File

@ -64,6 +64,7 @@ import { PlanStatusService } from '@app/core/services/plan/plan-status.service';
import { PlanStatus } from '@app/core/model/plan-status/plan-status'; import { PlanStatus } from '@app/core/model/plan-status/plan-status';
import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type'; import { PlanStatusAvailableActionType } from '@app/core/common/enum/plan-status-available-action-type';
import { PlanVersionStatus } from '@app/core/common/enum/plan-version-status'; import { PlanVersionStatus } from '@app/core/common/enum/plan-version-status';
import { PlanStatusPermission } from '@app/core/common/enum/plan-status-permission.enum';
@Component({ @Component({
selector: 'app-plan-editor', selector: 'app-plan-editor',
@ -102,8 +103,6 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
hoveredContact: number = -1; hoveredContact: number = -1;
availableStatusesTransitions: PlanStatus[];
singleAutocompleteBlueprintConfiguration: SingleAutoCompleteConfiguration = { singleAutocompleteBlueprintConfiguration: SingleAutoCompleteConfiguration = {
initialItems: (data?: any) => this.planBlueprintService.query(this.planBlueprintService.buildAutocompleteLookup(null, null, null, [PlanBlueprintStatus.Finalized])).pipe(map(x => x.items)), initialItems: (data?: any) => this.planBlueprintService.query(this.planBlueprintService.buildAutocompleteLookup(null, null, null, [PlanBlueprintStatus.Finalized])).pipe(map(x => x.items)),
filterFn: (searchQuery: string, data?: any) => this.planBlueprintService.query(this.planBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [PlanBlueprintStatus.Finalized])).pipe(map(x => x.items)), filterFn: (searchQuery: string, data?: any) => this.planBlueprintService.query(this.planBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [PlanBlueprintStatus.Finalized])).pipe(map(x => x.items)),
@ -166,6 +165,10 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
return !this.isDeleted && (this.isNew ? this.authService.hasPermission(AppPermission.NewPlan) : this.item.authorizationFlags?.some(x => x === AppPermission.EditPlan) || this.authService.hasPermission(AppPermission.EditPlan)); return !this.isDeleted && (this.isNew ? this.authService.hasPermission(AppPermission.NewPlan) : this.item.authorizationFlags?.some(x => x === AppPermission.EditPlan) || this.authService.hasPermission(AppPermission.EditPlan));
} }
get canEditStatus(): boolean{
return this.item.statusAuthorizationFlags?.some(x => x.toLowerCase() === PlanStatusPermission.Edit.toLowerCase())
}
protected canAnnotate(id: Guid): boolean { protected canAnnotate(id: Guid): boolean {
return !this.isDeleted && this.permissionPerSection && this.permissionPerSection[id.toString()] && this.permissionPerSection[id.toString()].some(x => x === AppPermission.AnnotatePlan); return !this.isDeleted && this.permissionPerSection && this.permissionPerSection[id.toString()] && this.permissionPerSection[id.toString()].some(x => x === AppPermission.AnnotatePlan);
} }
@ -274,7 +277,6 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
this.editorModel = data ? new PlanEditorModel().fromModel(data) : new PlanEditorModel(); this.editorModel = data ? new PlanEditorModel().fromModel(data) : new PlanEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false; this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
if (data) { if (data) {
if (data.id) this.getAvailableStatuses(data.id);
if (data.descriptions && !this.isDeleted) { if (data.descriptions && !this.isDeleted) {
if (data.status?.internalStatus == PlanStatusEnum.Finalized) { if (data.status?.internalStatus == PlanStatusEnum.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status.internalStatus === DescriptionStatusEnum.Finalized); data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status.internalStatus === DescriptionStatusEnum.Finalized);
@ -326,15 +328,6 @@ export class PlanEditorComponent extends BaseEditor<PlanEditorModel, Plan> imple
this.sectionToFieldsMap = this.prepareErrorIndication(); this.sectionToFieldsMap = this.prepareErrorIndication();
} }
getAvailableStatuses(id: Guid){
this.pLanStatusService.getAvailableTransitions(id).pipe(takeUntil(this._destroyed))
.subscribe(
(statuses) => {
this.availableStatusesTransitions = statuses;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error)
); }
prepareErrorIndication(): Map<string, PlanFieldIndicator> { prepareErrorIndication(): Map<string, PlanFieldIndicator> {
if (this.selectedBlueprint?.definition == null) return; if (this.selectedBlueprint?.definition == null) return;

View File

@ -17,6 +17,7 @@ import { nameof } from 'ts-simple-nameof';
import { EntityDoi } from '@app/core/model/entity-doi/entity-doi'; import { EntityDoi } from '@app/core/model/entity-doi/entity-doi';
import { DescriptionStatus } from '@app/core/model/description-status/description-status'; import { DescriptionStatus } from '@app/core/model/description-status/description-status';
import { PlanStatus, PlanStatusDefinition } from '@app/core/model/plan-status/plan-status'; import { PlanStatus, PlanStatusDefinition } from '@app/core/model/plan-status/plan-status';
import { PlanStatusPermission } from '@app/core/common/enum/plan-status-permission.enum';
@Injectable() @Injectable()
export class PlanEditorEntityResolver extends BaseEditorResolver { export class PlanEditorEntityResolver extends BaseEditorResolver {
@ -53,6 +54,8 @@ export class PlanEditorEntityResolver extends BaseEditorResolver {
[nameof<Plan>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'), [nameof<Plan>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'),
[nameof<Plan>(x => x.authorizationFlags), AppPermission.ExportPlan].join('.'), [nameof<Plan>(x => x.authorizationFlags), AppPermission.ExportPlan].join('.'),
[nameof<Plan>(x => x.statusAuthorizationFlags), PlanStatusPermission.Edit].join('.'),
[nameof<Plan>(x => x.properties), nameof<PlanProperties>(x => x.planBlueprintValues), nameof<PlanBlueprintValue>(x => x.fieldId)].join('.'), [nameof<Plan>(x => x.properties), nameof<PlanProperties>(x => x.planBlueprintValues), nameof<PlanBlueprintValue>(x => x.fieldId)].join('.'),
[nameof<Plan>(x => x.properties), nameof<PlanProperties>(x => x.planBlueprintValues), nameof<PlanBlueprintValue>(x => x.fieldValue)].join('.'), [nameof<Plan>(x => x.properties), nameof<PlanProperties>(x => x.planBlueprintValues), nameof<PlanBlueprintValue>(x => x.fieldValue)].join('.'),
[nameof<Plan>(x => x.properties), nameof<PlanProperties>(x => x.planBlueprintValues), nameof<PlanBlueprintValue>(x => x.dateValue)].join('.'), [nameof<Plan>(x => x.properties), nameof<PlanProperties>(x => x.planBlueprintValues), nameof<PlanBlueprintValue>(x => x.dateValue)].join('.'),
@ -100,6 +103,11 @@ export class PlanEditorEntityResolver extends BaseEditorResolver {
[nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'), [nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),
[nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.isActive)].join('.'), [nameof<Plan>(x => x.entityDois), nameof<EntityDoi>(x => x.isActive)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.id)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.name)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.internalStatus)].join('.'),
[nameof<Plan>(x => x.availableStatuses), nameof<PlanStatus>(x => x.action)].join('.'),
...PlanEditorEntityResolver.blueprintLookupFields(nameof<Plan>(x => x.blueprint)), ...PlanEditorEntityResolver.blueprintLookupFields(nameof<Plan>(x => x.blueprint)),
] ]