description authz

This commit is contained in:
Efstratios Giannopoulos 2024-03-15 14:13:55 +02:00
parent c62a945a6a
commit 266c05ee80
24 changed files with 305 additions and 101 deletions

View File

@ -101,6 +101,7 @@ public final class Permission {
//Description //Description
public static String BrowseDescription = "BrowseDescription"; public static String BrowseDescription = "BrowseDescription";
public static String EditDescription = "EditDescription"; public static String EditDescription = "EditDescription";
public static String FinalizeDescription = "FinalizeDescription";
public static String DeleteDescription = "DeleteDescription"; public static String DeleteDescription = "DeleteDescription";
public static String CloneDescription = "CloneDescription"; public static String CloneDescription = "CloneDescription";

View File

@ -12,4 +12,8 @@ public interface AuthorizationContentResolver {
AffiliatedResource dmpAffiliation(UUID id); AffiliatedResource dmpAffiliation(UUID id);
Map<UUID, AffiliatedResource> dmpsAffiliation(List<UUID> ids); Map<UUID, AffiliatedResource> dmpsAffiliation(List<UUID> ids);
AffiliatedResource descriptionAffiliation(UUID id);
Map<UUID, AffiliatedResource> descriptionsAffiliation(List<UUID> ids);
} }

View File

@ -4,9 +4,12 @@ import eu.eudat.authorization.AffiliatedResource;
import eu.eudat.authorization.PermissionNameProvider; import eu.eudat.authorization.PermissionNameProvider;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpEntity; import eu.eudat.data.DmpEntity;
import eu.eudat.data.DmpUserEntity; import eu.eudat.data.DmpUserEntity;
import eu.eudat.model.Description;
import eu.eudat.model.DmpUser; import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpUserQuery; import eu.eudat.query.DmpUserQuery;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.fieldset.BaseFieldSet;
@ -14,6 +17,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope; import org.springframework.web.context.annotation.RequestScope;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
@Service @Service
@RequestScope @RequestScope
@ -60,6 +64,37 @@ public class AuthorizationContentResolverImpl implements AuthorizationContentRes
return affiliatedResources; return affiliatedResources;
} }
@Override
public AffiliatedResource descriptionAffiliation(UUID id) {
return this.descriptionsAffiliation(List.of(id)).getOrDefault(id, new AffiliatedResource());
}
@Override
public Map<UUID, AffiliatedResource> descriptionsAffiliation(List<UUID> ids){
UUID userId = this.userScope.getUserIdSafe();
Map<UUID, AffiliatedResource> affiliatedResources = new HashMap<>();
for (UUID id : ids){
affiliatedResources.put(id, new AffiliatedResource());
}
if (userId == null || !userScope.isSet()) return affiliatedResources;
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, DescriptionEntity.class.getSimpleName());
if (idsToResolve.isEmpty()) return affiliatedResources;
List<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).ids(ids).collectAs(new BaseFieldSet().ensure(Description._id).ensure(Description._dmp));
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).descriptionIds(ids).userIds(userId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._role).ensure(DmpUser._dmp));
Map<UUID, List<DmpUserEntity>> dmpUsersMap = dmpUsers.stream().collect(Collectors.groupingBy(DmpUserEntity::getDmpId));
for (DescriptionEntity description : descriptionEntities){
List<DmpUserEntity> dmpDescriptionUsers = dmpUsersMap.getOrDefault(description.getDmpId(), new ArrayList<>());
for (DmpUserEntity dmpUser : dmpDescriptionUsers) {
affiliatedResources.get(description.getId()).getDmpUserRoles().add(dmpUser.getRole());
}
}
this.ensureAffiliatedInCache(idsToResolve, userId, affiliatedResources, DescriptionEntity.class.getSimpleName());
return affiliatedResources;
}
private List<UUID> getAffiliatedFromCache(List<UUID> ids, UUID userId, Map<UUID, AffiliatedResource> affiliatedResources, String entityType){ private List<UUID> getAffiliatedFromCache(List<UUID> ids, UUID userId, Map<UUID, AffiliatedResource> affiliatedResources, String entityType){
List<UUID> idsToResolve = new ArrayList<>(); List<UUID> idsToResolve = new ArrayList<>();
for (UUID id : ids){ for (UUID id : ids){

View File

@ -71,6 +71,9 @@ public class Description {
public static final String _descriptionTemplate = "descriptionTemplate"; public static final String _descriptionTemplate = "descriptionTemplate";
private List<String> authorizationFlags;
public static final String _authorizationFlags = "authorizationFlags";
private Dmp dmp; private Dmp dmp;
public static final String _dmp = "dmp"; public static final String _dmp = "dmp";
@ -203,4 +206,12 @@ public class Description {
public void setDescriptionTemplate(DescriptionTemplate descriptionTemplate) { public void setDescriptionTemplate(DescriptionTemplate descriptionTemplate) {
this.descriptionTemplate = descriptionTemplate; this.descriptionTemplate = descriptionTemplate;
} }
public List<String> getAuthorizationFlags() {
return authorizationFlags;
}
public void setAuthorizationFlags(List<String> authorizationFlags) {
this.authorizationFlags = authorizationFlags;
}
} }

View File

@ -1,6 +1,8 @@
package eu.eudat.model.builder; package eu.eudat.model.builder;
import eu.eudat.authorization.AffiliatedResource;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.XmlHandlingService; import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.types.description.PropertyDefinitionEntity; import eu.eudat.commons.types.description.PropertyDefinitionEntity;
@ -8,10 +10,12 @@ import eu.eudat.commons.types.descriptiontemplate.DefinitionEntity;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.data.DescriptionEntity; import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DescriptionTemplateEntity; import eu.eudat.data.DescriptionTemplateEntity;
import eu.eudat.data.DmpEntity;
import eu.eudat.data.UserRoleEntity; import eu.eudat.data.UserRoleEntity;
import eu.eudat.model.*; import eu.eudat.model.*;
import eu.eudat.model.builder.descriptionpropertiesdefinition.PropertyDefinitionBuilder; import eu.eudat.model.builder.descriptionpropertiesdefinition.PropertyDefinitionBuilder;
import eu.eudat.query.*; import eu.eudat.query.*;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
@ -37,6 +41,8 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
private final BuilderFactory builderFactory; private final BuilderFactory builderFactory;
private final JsonHandlingService jsonHandlingService; private final JsonHandlingService jsonHandlingService;
private final XmlHandlingService xmlHandlingService; private final XmlHandlingService xmlHandlingService;
private final AuthorizationService authorizationService;
private final AuthorizationContentResolver authorizationContentResolver;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -44,12 +50,14 @@ 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) { BuilderFactory builderFactory, JsonHandlingService jsonHandlingService, XmlHandlingService xmlHandlingService, AuthorizationService authorizationService, AuthorizationContentResolver authorizationContentResolver) {
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;
this.jsonHandlingService = jsonHandlingService; this.jsonHandlingService = jsonHandlingService;
this.xmlHandlingService = xmlHandlingService; this.xmlHandlingService = xmlHandlingService;
this.authorizationService = authorizationService;
this.authorizationContentResolver = authorizationContentResolver;
} }
public DescriptionBuilder authorize(EnumSet<AuthorizationFlags> values) { public DescriptionBuilder authorize(EnumSet<AuthorizationFlags> values) {
@ -86,6 +94,9 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
Map<UUID, DefinitionEntity> definitionEntityMap = !definitionPropertiesFields.isEmpty() ? this.collectDescriptionTemplateDefinitions(data) : null; Map<UUID, DefinitionEntity> definitionEntityMap = !definitionPropertiesFields.isEmpty() ? this.collectDescriptionTemplateDefinitions(data) : null;
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()));
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();
@ -108,6 +119,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
PropertyDefinitionEntity propertyDefinition = this.jsonHandlingService.fromJsonSafe(PropertyDefinitionEntity.class, d.getProperties()); PropertyDefinitionEntity propertyDefinition = this.jsonHandlingService.fromJsonSafe(PropertyDefinitionEntity.class, d.getProperties());
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)));
models.add(m); models.add(m);
} }

View File

@ -89,7 +89,6 @@ public class DmpBuilder extends BaseBuilder<Dmp, DmpEntity> {
Map<UUID, List<DmpDescriptionTemplate>> dmpDescriptionTemplatesMap = this.collectDmpDescriptionTemplates(dmpDescriptionTemplatesFields, data); Map<UUID, List<DmpDescriptionTemplate>> dmpDescriptionTemplatesMap = this.collectDmpDescriptionTemplates(dmpDescriptionTemplatesFields, data);
Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Dmp._authorizationFlags, this.authorizationContentResolver.getPermissionNames()); Set<String> authorizationFlags = this.extractAuthorizationFlags(fields, Dmp._authorizationFlags, this.authorizationContentResolver.getPermissionNames());
Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.dmpsAffiliation(data.stream().map(DmpEntity::getId).collect(Collectors.toList())); Map<UUID, AffiliatedResource> affiliatedResourceMap = authorizationFlags == null || authorizationFlags.isEmpty() ? null : this.authorizationContentResolver.dmpsAffiliation(data.stream().map(DmpEntity::getId).collect(Collectors.toList()));
FieldSet propertiesFields = fields.extractPrefixed(this.asPrefix(Dmp._properties)); FieldSet propertiesFields = fields.extractPrefixed(this.asPrefix(Dmp._properties));
@ -121,7 +120,7 @@ public class DmpBuilder extends BaseBuilder<Dmp, DmpEntity> {
DmpPropertiesEntity propertyDefinition = this.jsonHandlingService.fromJsonSafe(DmpPropertiesEntity.class, d.getProperties()); DmpPropertiesEntity propertyDefinition = this.jsonHandlingService.fromJsonSafe(DmpPropertiesEntity.class, d.getProperties());
m.setProperties(this.builderFactory.builder(DmpPropertiesBuilder.class).authorize(this.authorize).build(propertiesFields, propertyDefinition)); m.setProperties(this.builderFactory.builder(DmpPropertiesBuilder.class).authorize(this.authorize).build(propertiesFields, propertyDefinition));
} }
if (authorizationFlags != 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)));
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));

View File

@ -39,7 +39,7 @@ public class TagCensor extends BaseCensor {
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; return;
this.authService.authorizeForce(Permission.BrowseTag); this.authService.authorizeForce(Permission.BrowseTag, Permission.DeferredAffiliation);
FieldSet createdByFields = fields.extractPrefixed(this.asIndexerPrefix(Tag._createdBy)); FieldSet createdByFields = fields.extractPrefixed(this.asIndexerPrefix(Tag._createdBy));
this.censorFactory.censor(UserCensor.class).censor(createdByFields, userId); this.censorFactory.censor(UserCensor.class).censor(createdByFields, userId);
} }

View File

@ -314,6 +314,7 @@ public class DescriptionQuery extends QueryBase<DescriptionEntity> {
else if (item.prefix(Description._descriptionTemplate) || item.prefix(PublicDescription._descriptionTemplate)) return DescriptionEntity._descriptionTemplateId; else if (item.prefix(Description._descriptionTemplate) || item.prefix(PublicDescription._descriptionTemplate)) return DescriptionEntity._descriptionTemplateId;
else if (item.match(Description._descriptionTemplate) || item.match(PublicDescription._descriptionTemplate)) return DescriptionEntity._descriptionTemplateId; else if (item.match(Description._descriptionTemplate) || item.match(PublicDescription._descriptionTemplate)) return DescriptionEntity._descriptionTemplateId;
else if (item.prefix(Description._dmp)) return DescriptionEntity._dmpId; else if (item.prefix(Description._dmp)) return DescriptionEntity._dmpId;
else if (item.match(Description._dmp)) return DescriptionEntity._dmpId;
else return null; else return null;
} }

View File

@ -5,9 +5,7 @@ import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.DmpUserRole; import eu.eudat.commons.enums.DmpUserRole;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DmpReferenceEntity; import eu.eudat.data.*;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.data.ReferenceEntity;
import eu.eudat.model.DmpUser; import eu.eudat.model.DmpUser;
import eu.eudat.model.PublicDmpUser; import eu.eudat.model.PublicDmpUser;
import eu.eudat.query.utils.BuildSubQueryInput; import eu.eudat.query.utils.BuildSubQueryInput;
@ -19,6 +17,7 @@ import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple; import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -36,6 +35,8 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
private Collection<UUID> dmpIds; private Collection<UUID> dmpIds;
private Collection<UUID> descriptionIds;
private Collection<UUID> userIds; private Collection<UUID> userIds;
private Collection<DmpUserRole> userRoles; private Collection<DmpUserRole> userRoles;
@ -89,6 +90,21 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
return this; return this;
} }
public DmpUserQuery descriptionIds(UUID value) {
this.descriptionIds = List.of(value);
return this;
}
public DmpUserQuery descriptionIds(UUID... value) {
this.descriptionIds = Arrays.asList(value);
return this;
}
public DmpUserQuery descriptionIds(Collection<UUID> values) {
this.descriptionIds = values;
return this;
}
public DmpUserQuery userRoles(DmpUserRole value) { public DmpUserQuery userRoles(DmpUserRole value) {
this.userRoles = List.of(value); this.userRoles = List.of(value);
return this; return this;
@ -161,7 +177,7 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
@Override @Override
protected Boolean isFalseQuery() { protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.dmpIds) || this.isEmpty(this.userIds); return this.isEmpty(this.ids) || this.isEmpty(this.dmpIds) || this.isEmpty(this.descriptionIds) || this.isEmpty(this.userIds);
} }
@Override @Override
@ -208,6 +224,19 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
inClause.value(item); inClause.value(item);
predicates.add(inClause); predicates.add(inClause);
} }
if (this.descriptionIds != null) {
Subquery<UUID> descriptionSubquery = queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(
new BuildSubQueryInput.Builder<>(DescriptionEntity.class, UUID.class, queryContext)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(DescriptionEntity._dmpId))
.filterFunc((subQueryRoot, cb) -> {
CriteriaBuilder.In<UUID> inClause = cb.in(subQueryRoot.get(DmpUserEntity._id));
for (UUID item : this.descriptionIds)
inClause.value(item);
return inClause;
})
));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpUserEntity._dmpId)).value(descriptionSubquery));
}
if (this.userIds != null) { if (this.userIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpUserEntity._userId)); CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpUserEntity._userId));
for (UUID item : this.userIds) for (UUID item : this.userIds)

View File

@ -1,9 +1,17 @@
package eu.eudat.query; package eu.eudat.query;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.IsActive; import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DescriptionTagEntity;
import eu.eudat.data.DmpReferenceEntity;
import eu.eudat.data.ReferenceEntity;
import eu.eudat.data.TagEntity; import eu.eudat.data.TagEntity;
import eu.eudat.model.Tag; import eu.eudat.model.Tag;
import eu.eudat.query.utils.BuildSubQueryInput;
import eu.eudat.query.utils.QueryUtilsService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver; import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase; import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext; import gr.cite.tools.data.query.QueryContext;
@ -35,7 +43,14 @@ public class TagQuery extends QueryBase<TagEntity> {
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public TagQuery() { private final UserScope userScope;
private final AuthorizationService authService;
private final QueryUtilsService queryUtilsService;
public TagQuery(UserScope userScope, AuthorizationService authService, QueryUtilsService queryUtilsService) {
this.userScope = userScope;
this.authService = authService;
this.queryUtilsService = queryUtilsService;
} }
public TagQuery like(String value) { public TagQuery like(String value) {
@ -153,6 +168,37 @@ public class TagQuery extends QueryBase<TagEntity> {
protected Class<TagEntity> entityClass() { protected Class<TagEntity> entityClass() {
return TagEntity.class; return TagEntity.class;
} }
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseTag)) return null;
UUID userId;
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe();
else userId = null;
List<Predicate> predicates = new ArrayList<>();
if (userId != null || usePublic ) {
predicates.add(queryContext.CriteriaBuilder.or(
queryContext.CriteriaBuilder.isNull(queryContext.Root.get(TagEntity._createdById)),
userId != null ? queryContext.CriteriaBuilder.equal(queryContext.Root.get(TagEntity._createdById), userId) : queryContext.CriteriaBuilder.or(), //Creates a false query
queryContext.CriteriaBuilder.in(queryContext.Root.get(TagEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(DescriptionTagEntity.class, UUID.class)
.query(queryContext.Query)
.criteriaBuilder(queryContext.CriteriaBuilder)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(DescriptionTagEntity._tagId))
.filterFunc((subQueryRoot, cb) ->
cb.in(subQueryRoot.get(DescriptionTagEntity._descriptionId)).value(queryUtilsService.buildDescriptionAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, userId, usePublic))
)
))) //Creates a false query
));
}
if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override @Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) { protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {

View File

@ -2,6 +2,7 @@ package eu.eudat.service.description;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import eu.eudat.commons.JsonHandlingService; import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.XmlHandlingService; import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.enums.*; import eu.eudat.commons.enums.*;
@ -96,6 +97,7 @@ public class DescriptionServiceImpl implements DescriptionService {
private final StorageFileProperties storageFileConfig; private final StorageFileProperties storageFileConfig;
private final StorageFileService storageFileService; private final StorageFileService storageFileService;
private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler; private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver;
@Autowired @Autowired
public DescriptionServiceImpl( public DescriptionServiceImpl(
@ -110,7 +112,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory, QueryFactory queryFactory,
JsonHandlingService jsonHandlingService, JsonHandlingService jsonHandlingService,
UserScope userScope, UserScope userScope,
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler) { XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory; this.deleterFactory = deleterFactory;
@ -131,6 +133,7 @@ public class DescriptionServiceImpl implements DescriptionService {
this.storageFileConfig = storageFileConfig; this.storageFileConfig = storageFileConfig;
this.storageFileService = storageFileService; this.storageFileService = storageFileService;
this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler; this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver;
} }
//region Persist //region Persist
@ -139,9 +142,10 @@ public class DescriptionServiceImpl implements DescriptionService {
public Description persist(DescriptionPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException { public Description persist(DescriptionPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException {
logger.debug(new MapLogEntry("persisting data description").And("model", model).And("fields", fields)); logger.debug(new MapLogEntry("persisting data description").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditDescription);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
if (isUpdate) this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.EditDescription);
else this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getDmpId())), Permission.EditDescription);
DescriptionEntity data; DescriptionEntity data;
if (isUpdate) { if (isUpdate) {
@ -336,13 +340,14 @@ public class DescriptionServiceImpl implements DescriptionService {
public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException { public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException {
logger.debug(new MapLogEntry("persisting data dmp").And("model", model).And("fields", fields)); logger.debug(new MapLogEntry("persisting data dmp").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditDescription); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.EditDescription);
DescriptionEntity data = this.entityManager.find(DescriptionEntity.class, model.getId()); DescriptionEntity data = this.entityManager.find(DescriptionEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
if (!data.getStatus().equals(model.getStatus())){ if (!data.getStatus().equals(model.getStatus())){
if (data.getStatus().equals(DescriptionStatus.Finalized)){ if (data.getStatus().equals(DescriptionStatus.Finalized)){
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.FinalizeDescription);
DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, data.getDmpId()); DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, data.getDmpId());
if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{data.getDmpId(), DmpEntity.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (dmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{data.getDmpId(), DmpEntity.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if(!dmpEntity.getStatus().equals(DmpStatus.Draft)) throw new MyValidationException(this.errors.getDmpIsFinalized().getCode(), this.errors.getDmpIsFinalized().getMessage()); if(!dmpEntity.getStatus().equals(DmpStatus.Draft)) throw new MyValidationException(this.errors.getDmpIsFinalized().getCode(), this.errors.getDmpIsFinalized().getMessage());
@ -650,7 +655,7 @@ public class DescriptionServiceImpl implements DescriptionService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException { public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
logger.debug("deleting description: {}", id); logger.debug("deleting description: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteDescription); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(id)), Permission.DeleteDescription);
this.deleterFactory.deleter(DescriptionDeleter.class).deleteAndSaveByIds(List.of(id), false); this.deleterFactory.deleter(DescriptionDeleter.class).deleteAndSaveByIds(List.of(id), false);
} }
@ -663,7 +668,7 @@ public class DescriptionServiceImpl implements DescriptionService {
public void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException { public void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException {
logger.debug("cloning description: {} with description: {}", descriptionId, dmpId); logger.debug("cloning description: {} with description: {}", descriptionId, dmpId);
this.authorizationService.authorizeForce(Permission.CloneDescription); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(descriptionId)), Permission.CloneDescription);
DescriptionEntity existing = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).isActive(IsActive.Active).first(); DescriptionEntity existing = this.queryFactory.query(DescriptionQuery.class).ids(descriptionId).isActive(IsActive.Active).first();
@ -743,7 +748,8 @@ public class DescriptionServiceImpl implements DescriptionService {
@Override @Override
public StorageFile uploadFieldFile(DescriptionFieldFilePersist model, MultipartFile file, FieldSet fields) throws IOException { public StorageFile uploadFieldFile(DescriptionFieldFilePersist model, MultipartFile file, FieldSet fields) throws IOException {
this.authorizationService.authorizeForce(Permission.EditDescription); //this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(descriptionId)), Permission.CloneDescription);
this.authorizationService.authorizeForce(Permission.EditDescription);//TODO: Missing Description or dmp for authz
DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).ids(model.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).first(); DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).ids(model.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).first();
if (descriptionTemplate == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (descriptionTemplate == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
@ -783,7 +789,8 @@ public class DescriptionServiceImpl implements DescriptionService {
@Override @Override
public StorageFileEntity getFieldFile(UUID descriptionId, UUID storageFileId) { public StorageFileEntity getFieldFile(UUID descriptionId, UUID storageFileId) {
this.authorizationService.authorizeForce(Permission.BrowseDescription); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(descriptionId)), Permission.BrowseDescription);
DescriptionEntity descriptionEntity = this.queryFactory.query(DescriptionQuery.class).isActive(IsActive.Active).ids(descriptionId).first(); DescriptionEntity descriptionEntity = this.queryFactory.query(DescriptionQuery.class).isActive(IsActive.Active).ids(descriptionId).first();
if (descriptionEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (descriptionEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));

View File

@ -132,18 +132,48 @@ permissions:
BrowseDescription: BrowseDescription:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
EditDescription: EditDescription:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
FinalizeDescription:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteDescription: DeleteDescription:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -151,11 +181,16 @@ permissions:
CloneDescription: CloneDescription:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
# Tag # Tag
BrowseTag: BrowseTag:
roles: roles:

View File

@ -10,8 +10,10 @@ export enum AppPermission {
DeleteDmpBlueprint = "DeleteDmpBlueprint", DeleteDmpBlueprint = "DeleteDmpBlueprint",
//Description //Description
NewDescription = "NewDescription",
BrowseDescription = "BrowseDescription", BrowseDescription = "BrowseDescription",
EditDescription = "EditDescription", EditDescription = "EditDescription",
FinalizeDescription = "FinalizeDescription",
DeleteDescription= "DeleteDescription", DeleteDescription= "DeleteDescription",
//Dmp //Dmp

View File

@ -6,6 +6,7 @@ import { Dmp, DmpDescriptionTemplate, PublicDmp } from "../dmp/dmp";
import { Reference, ReferencePersist } from "../reference/reference"; import { Reference, ReferencePersist } from "../reference/reference";
import { Tag } from "../tag/tag"; import { Tag } from "../tag/tag";
import { User } from "../user/user"; import { User } from "../user/user";
import { AppPermission } from "@app/core/common/enum/permission.enum";
export interface Description extends BaseEntity { export interface Description extends BaseEntity {
label?: string; label?: string;
@ -19,6 +20,7 @@ export interface Description extends BaseEntity {
descriptionTemplate?: DescriptionTemplate; descriptionTemplate?: DescriptionTemplate;
dmpDescriptionTemplate?: DmpDescriptionTemplate; dmpDescriptionTemplate?: DmpDescriptionTemplate;
dmp?: Dmp; dmp?: Dmp;
authorizationFlags?: AppPermission[];
} }

View File

@ -78,24 +78,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
step: number = 0; step: number = 0;
protected get canDelete(): boolean {
return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDescription);
}
protected get canSave(): boolean {
return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditDescription);
}
protected get canFinalize(): boolean {
return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditDescription);
}
private hasPermission(permission: AppPermission): boolean {
return this.authService.hasPermission(permission) || this.editorModel?.permissions?.includes(permission);
}
constructor( constructor(
// BaseFormEditor injected dependencies // BaseFormEditor injected dependencies
protected dialog: MatDialog, protected dialog: MatDialog,
@ -606,7 +588,9 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
} }
buildForm() { buildForm() {
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.authService.hasPermission(AppPermission.EditDescription)); const canedit = this.isNew ? this.authService.hasPermission(AppPermission.NewDescription) :
this.item.authorizationFlags?.some(x => x === AppPermission.EditDescription) || this.item.dmp.authorizationFlags?.some(x => x === AppPermission.EditDmp) ||this.authService.hasPermission(AppPermission.EditDescription);
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !canedit);
//this.visibilityRulesService.buildVisibilityRules(this.visibilityRulesService.getVisibilityRulesFromDescriptionTempalte(this.item.descriptionTemplate), this.formGroup); //this.visibilityRulesService.buildVisibilityRules(this.visibilityRulesService.getVisibilityRulesFromDescriptionTempalte(this.item.descriptionTemplate), this.formGroup);
// this.selectedSystemFields = this.selectedSystemFieldDisabled(); // this.selectedSystemFields = this.selectedSystemFieldDisabled();

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 { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplate, DescriptionTemplateBaseFieldData, DescriptionTemplateDefinition, DescriptionTemplateExternalDatasetData, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateMultiplicity, DescriptionTemplatePage, DescriptionTemplateReferenceTypeData, DescriptionTemplateRule, DescriptionTemplateSection, DescriptionTemplateSelectData, DescriptionTemplateSelectOption, DescriptionTemplateUploadData, DescriptionTemplateUploadOption } from '@app/core/model/description-template/description-template'; import { DescriptionTemplate, DescriptionTemplateBaseFieldData, DescriptionTemplateDefinition, DescriptionTemplateExternalDatasetData, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateMultiplicity, DescriptionTemplatePage, DescriptionTemplateReferenceTypeData, DescriptionTemplateRule, DescriptionTemplateSection, DescriptionTemplateSelectData, DescriptionTemplateSelectOption, DescriptionTemplateUploadData, DescriptionTemplateUploadOption } from '@app/core/model/description-template/description-template';
import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionTag } from '@app/core/model/description/description'; import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionTag } from '@app/core/model/description/description';
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
@ -50,6 +51,10 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
nameof<Description>(x => x.description), nameof<Description>(x => x.description),
nameof<Description>(x => x.status), nameof<Description>(x => x.status),
[nameof<Description>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.DeleteDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.FinalizeDescription].join('.'),
[nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.id)].join('.'), [nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.id)].join('.'),
[nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'), [nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
@ -124,6 +129,8 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.status)].join('.'), (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.status)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.isActive)].join('.'), (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.isActive)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.id)].join('.'), (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.isActive)].join('.'), (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.isActive)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition)].join('.'), (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition)].join('.'),

View File

@ -21,10 +21,11 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}), })
authContext: { // ,
permissions: [AppPermission.EditDescription] // authContext: {
} // permissions: [AppPermission.EditDescription]
// }
} }
}, },
{ {
@ -55,10 +56,11 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}), })
authContext: { // ,
permissions: [AppPermission.EditDescription] // authContext: {
} // permissions: [AppPermission.EditDescription]
// }
} }
}, },
{ {
@ -72,10 +74,11 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION' title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}), })
authContext: { // ,
permissions: [AppPermission.EditDescription] // authContext: {
} // permissions: [AppPermission.EditDescription]
// }
} }
}, },

View File

@ -8,6 +8,7 @@ import { MatSort } from '@angular/material/sort';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status'; import { DescriptionStatus } from '@app/core/common/enum/description-status';
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 { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template'; import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description } from '@app/core/model/description/description'; import { Description } from '@app/core/model/description/description';
@ -196,6 +197,11 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
nameof<Description>(x => x.label), nameof<Description>(x => x.label),
nameof<Description>(x => x.status), nameof<Description>(x => x.status),
nameof<Description>(x => x.updatedAt), nameof<Description>(x => x.updatedAt),
[nameof<Description>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.DeleteDescription].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('.'),

View File

@ -8,8 +8,8 @@
<div *ngIf="description.status === descriptionStatusEnum.Finalized" class="col-auto description-title">{{description.label}}</div> <div *ngIf="description.status === descriptionStatusEnum.Finalized" class="col-auto description-title">{{description.label}}</div>
<div *ngIf="description.status === descriptionStatusEnum.Draft" class="col-auto description-title-draft">{{description.label}}</div> <div *ngIf="description.status === descriptionStatusEnum.Draft" class="col-auto description-title-draft">{{description.label}}</div>
<div class="description-subtitle"> <div class="description-subtitle">
<span *ngIf="isUserDescriptionRelated()" class="col-auto">{{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }}</span> <span *ngIf="canEdit" class="col-auto">{{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }}</span>
<span *ngIf="isUserDescriptionRelated()">.</span> <span *ngIf="canEdit">.</span>
<span class="col-auto" *ngIf="description.status === descriptionStatusEnum.Finalized && description.dmp.accessType === dmpAccessTypeEnum.Public"><span class="material-icons icon-align">public</span>{{'DESCRIPTION-LISTING.STATES.PUBLIC' | translate}}</span> <span class="col-auto" *ngIf="description.status === descriptionStatusEnum.Finalized && description.dmp.accessType === dmpAccessTypeEnum.Public"><span class="material-icons icon-align">public</span>{{'DESCRIPTION-LISTING.STATES.PUBLIC' | translate}}</span>
<span *ngIf="description.status === descriptionStatusEnum.Finalized && description.dmp.accessType != dmpAccessTypeEnum.Public" class="col-auto"><span class="material-icons icon-align">done</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span> <span *ngIf="description.status === descriptionStatusEnum.Finalized && description.dmp.accessType != dmpAccessTypeEnum.Public" class="col-auto"><span class="material-icons icon-align">done</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span>
<span *ngIf="description.status === descriptionStatusEnum.Draft" class=" col-auto draft"><span class="material-icons icon-align">create</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span> <span *ngIf="description.status === descriptionStatusEnum.Draft" class=" col-auto draft"><span class="material-icons icon-align">create</span>{{ enumUtils.toDescriptionStatusString(description.status) }}</span>
@ -27,13 +27,13 @@
<a class="col-auto border-right pointer" *ngIf="fileTransformerService.availableFormats && fileTransformerService.availableFormats.length > 0" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DESCRIPTION-LISTING.ACTIONS.EXPORT' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="fileTransformerService.availableFormats && fileTransformerService.availableFormats.length > 0" [matMenuTriggerFor]="exportMenu"><span class="material-icons icon-align pr-2">open_in_new</span>{{'DESCRIPTION-LISTING.ACTIONS.EXPORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isUserOwner" (click)="openShareDialog(description.dmp.id, description.dmp.label)"><span class="material-icons icon-align pr-2">group_add</span>{{'DESCRIPTION-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="isUserOwner" (click)="openShareDialog(description.dmp.id, description.dmp.label)"><span class="material-icons icon-align pr-2">group_add</span>{{'DESCRIPTION-LISTING.ACTIONS.INVITE-SHORT' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="copyToDmp(description)"><span class="material-icons icon-align pr-2">file_copy</span>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}}</a> <a class="col-auto border-right pointer" *ngIf="isAuthenticated()" (click)="copyToDmp(description)"><span class="material-icons icon-align pr-2">file_copy</span>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}}</a>
<a class="col-auto border-right pointer" *ngIf="isAuthenticated() && isUserDescriptionRelated()" (click)="deleteClicked(description.id)"><span class="material-icons icon-align pr-2">delete</span>{{ 'DESCRIPTION-LISTING.ACTIONS.DELETE' | translate }}</a> <a class="col-auto border-right pointer" *ngIf="canDelete" (click)="deleteClicked(description.id)"><span class="material-icons icon-align pr-2">delete</span>{{ 'DESCRIPTION-LISTING.ACTIONS.DELETE' | translate }}</a>
</div> </div>
<mat-menu #actionsMenu="matMenu"> <mat-menu #actionsMenu="matMenu">
<button *ngIf="isAuthenticated()" mat-menu-item (click)="copyToDmp(description)" class="menu-item"> <button *ngIf="isAuthenticated()" mat-menu-item (click)="copyToDmp(description)" class="menu-item">
<mat-icon>file_copy</mat-icon>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}} <mat-icon>file_copy</mat-icon>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}}
</button> </button>
<button *ngIf="isUserDescriptionRelated()" mat-menu-item (click)="deleteClicked(description.id)" class="menu-item"> <button *ngIf="canDelete" mat-menu-item (click)="deleteClicked(description.id)" class="menu-item">
<mat-icon>delete</mat-icon>{{ 'DESCRIPTION-LISTING.ACTIONS.DELETE' | translate }} <mat-icon>delete</mat-icon>{{ 'DESCRIPTION-LISTING.ACTIONS.DELETE' | translate }}
</button> </button>
</mat-menu> </mat-menu>

View File

@ -26,6 +26,7 @@ import * as FileSaver from 'file-saver';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { DescriptionStatus } from '../../../../core/common/enum/description-status'; import { DescriptionStatus } from '../../../../core/common/enum/description-status';
import { DescriptionCopyDialogComponent } from '../../description-copy-dialog/description-copy-dialog.component'; import { DescriptionCopyDialogComponent } from '../../description-copy-dialog/description-copy-dialog.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@Component({ @Component({
selector: 'app-description-listing-item-component', selector: 'app-description-listing-item-component',
@ -44,6 +45,8 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
isUserOwner: boolean; isUserOwner: boolean;
descriptionStatusEnum = DescriptionStatus; descriptionStatusEnum = DescriptionStatus;
dmpAccessTypeEnum = DmpAccessType; dmpAccessTypeEnum = DmpAccessType;
canDelete: boolean = false;
canEdit: boolean = false;
constructor( constructor(
@ -80,6 +83,13 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
this.isDeleted = false; this.isDeleted = false;
this.setIsUserOwner(); this.setIsUserOwner();
} }
this.canDelete = this.authService.hasPermission(AppPermission.DeleteDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.DeleteDescription);
this.canEdit = this.authService.hasPermission(AppPermission.EditDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.EditDescription);
} }
setIsUserOwner() { setIsUserOwner() {
@ -240,11 +250,4 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
onDeleteCallbackError(error) { onDeleteCallbackError(error) {
this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error); this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error);
} }
isUserDescriptionRelated(): boolean {
const principalId: Guid = this.authService.userId();
return this.description.dmp.dmpUsers.some(x => (x.user.id === principalId));
}
} }

View File

@ -12,12 +12,12 @@
<p class="col description-label p-0 ml-3 mb-0">{{ description.label }}</p> <p class="col description-label p-0 ml-3 mb-0">{{ description.label }}</p>
</div> </div>
<div class="row d-flex align-items-center mt-3 mb-4 label-txt"> <div class="row d-flex align-items-center mt-3 mb-4 label-txt">
<div *ngIf="isUserDescriptionRelated()" class="d-flex"> <div *ngIf="canEdit" class="d-flex">
<p class="ml-0 mb-0 label2-txt"> <p class="ml-0 mb-0 label2-txt">
{{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }} {{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }}
</p> </p>
</div> </div>
<span *ngIf="isUserDescriptionRelated()" class="ml-2 mr-2">.</span> <span *ngIf="canEdit" class="ml-2 mr-2">.</span>
<div *ngIf="description.status === descriptionStatusEnum.Finalized && description.dmp.accessType === dmpAccessTypeEnum.Public" class="d-flex flex-row"> <div *ngIf="description.status === descriptionStatusEnum.Finalized && description.dmp.accessType === dmpAccessTypeEnum.Public" class="d-flex flex-row">
<mat-icon class="status-icon">public</mat-icon> <mat-icon class="status-icon">public</mat-icon>
{{'DESCRIPTION-OVERVIEW.PUBLIC' | translate}} {{'DESCRIPTION-OVERVIEW.PUBLIC' | translate}}
@ -39,13 +39,13 @@
</div> </div>
</div> </div>
<div class="row mb-4 pb-3"> <div class="row mb-4 pb-3">
<button *ngIf="isDraftDescription(description) && !lockStatus" (click)="editClicked(description)" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.EDIT' | translate}}" matTooltipPosition="above"> <button *ngIf="canEdit && isDraftDescription(description) && !lockStatus" (click)="editClicked(description)" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.EDIT' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">create</mat-icon> <mat-icon class="mat-mini-fab-icon">create</mat-icon>
</button> </button>
<button *ngIf="isAuthenticated()" (click)="openCopyToDmpDialog()" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.CLONE' | translate}}" matTooltipPosition="above"> <button *ngIf="canEdit" (click)="openCopyToDmpDialog()" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.CLONE' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">content_copy</mat-icon> <mat-icon class="mat-mini-fab-icon">content_copy</mat-icon>
</button> </button>
<button *ngIf="isUserDescriptionRelated() && !lockStatus" (click)="deleteClicked()" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.DELETE' | translate}}" matTooltipPosition="above"> <button *ngIf="canDelete && !lockStatus" (click)="deleteClicked()" mat-mini-fab class="mr-3 actions-btn" matTooltip="{{'DESCRIPTION-OVERVIEW.ACTIONS.DELETE' | translate}}" matTooltipPosition="above">
<mat-icon class="mat-mini-fab-icon">delete</mat-icon> <mat-icon class="mat-mini-fab-icon">delete</mat-icon>
</button> </button>
</div> </div>
@ -92,7 +92,7 @@
</div> </div>
<div class="col-md-4 col-lg-4 p-0"> <div class="col-md-4 col-lg-4 p-0">
<div class="frame mb-3 pt-4 pl-3 pr-5 pb-1" *ngIf="isAuthenticated()"> <div class="frame mb-3 pt-4 pl-3 pr-5 pb-1" *ngIf="isAuthenticated()">
<div *ngIf="isDraftDescription(description) && !lockStatus"> <div *ngIf="canFinalize && isDraftDescription(description) && !lockStatus">
<div class="row ml-0 mr-0 pl-4 d-flex align-items-center" (click)="finalize(description)"> <div class="row ml-0 mr-0 pl-4 d-flex align-items-center" (click)="finalize(description)">
<button mat-mini-fab class="finalize-btn"> <button mat-mini-fab class="finalize-btn">
<mat-icon class="mat-mini-fab-icon check-icon">check</mat-icon> <mat-icon class="mat-mini-fab-icon check-icon">check</mat-icon>
@ -141,10 +141,10 @@
<p class="authors-role">{{ enumUtils.toDmpUserRoleString(dmpUser.role) }}</p> <p class="authors-role">{{ enumUtils.toDmpUserRoleString(dmpUser.role) }}</p>
</div> </div>
</div> </div>
<button *ngIf="isUserOwner && description.dmp?.status === dmpStatusEnum.Draft && dmpUser.role === dmpUserRoleEnum.Owner" (click)="removeUserFromDmp(dmpUser)" class="remove-btn">{{ 'DESCRIPTION-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}</button> <button *ngIf="canInviteDmpUsers && description.dmp?.status === dmpStatusEnum.Draft && dmpUser.role === dmpUserRoleEnum.Owner" (click)="removeUserFromDmp(dmpUser)" class="remove-btn">{{ 'DESCRIPTION-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}</button>
</div> </div>
</div> </div>
<div *ngIf="isUserOwner" class="row mt-3 mb-3 d-flex align-items-center justify-content-center"> <div *ngIf="canInviteDmpUsers" class="row mt-3 mb-3 d-flex align-items-center justify-content-center">
<button mat-raised-button class="invite-btn" (click)="openShareDialog(description.dmp.id, description.dmp.label)"> <button mat-raised-button class="invite-btn" (click)="openShareDialog(description.dmp.id, description.dmp.label)">
<mat-icon>group_add</mat-icon> <mat-icon>group_add</mat-icon>
{{'DESCRIPTION-OVERVIEW.ACTIONS.INVITE-SHORT' | translate}} {{'DESCRIPTION-OVERVIEW.ACTIONS.INVITE-SHORT' | translate}}

View File

@ -33,6 +33,7 @@ import { filter, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof'; 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 { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@Component({ @Component({
@ -49,7 +50,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
isPublicView = true; isPublicView = true;
hasPublishButton: boolean = true; hasPublishButton: boolean = true;
// breadCrumbs: Observable<BreadcrumbItem[]> = observableOf(); // breadCrumbs: Observable<BreadcrumbItem[]> = observableOf();
isUserOwner: boolean;
expand = false; expand = false;
lockStatus: Boolean; lockStatus: Boolean;
descriptionStatusEnum = DescriptionStatus; descriptionStatusEnum = DescriptionStatus;
@ -57,6 +57,11 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
dmpStatusEnum = DmpStatus; dmpStatusEnum = DmpStatus;
dmpUserRoleEnum = DmpUserRole; dmpUserRoleEnum = DmpUserRole;
canEdit = false;
canDelete = false;
canFinalize = false;
canInviteDmpUsers = false;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
@ -72,6 +77,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
public enumUtils: EnumUtils, public enumUtils: EnumUtils,
private matomoService: MatomoService, private matomoService: MatomoService,
private fileUtils: FileUtils, private fileUtils: FileUtils,
private authService: AuthService,
public fileTransformerService: FileTransformerService, public fileTransformerService: FileTransformerService,
private referenceTypeService: ReferenceTypeService, private referenceTypeService: ReferenceTypeService,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
@ -81,6 +87,10 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
ngOnInit() { ngOnInit() {
this.matomoService.trackPageView('Description Overview'); this.matomoService.trackPageView('Description Overview');
this.canDelete = false;
this.canEdit = false;
this.canFinalize = false;
this.canInviteDmpUsers = false;
// Gets description data using parameter id // Gets description data using parameter id
this.route.params this.route.params
.pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
@ -97,11 +107,22 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
this.researchers = this.referenceService.getReferencesForTypes(this.description?.dmp?.dmpReferences, [this.referenceTypeService.getResearcherReferenceType()]); this.researchers = this.referenceService.getReferencesForTypes(this.description?.dmp?.dmpReferences, [this.referenceTypeService.getResearcherReferenceType()]);
// this.users = this.description.dmp.users; // this.users = this.description.dmp.users;
this.checkLockStatus(this.description.id); this.checkLockStatus(this.description.id);
this.setIsUserOwner(); this.canDelete = this.authService.hasPermission(AppPermission.DeleteDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.DeleteDescription);
this.canEdit = this.authService.hasPermission(AppPermission.EditDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.EditDescription);
this.canFinalize = this.authService.hasPermission(AppPermission.FinalizeDescription) ||
this.description.authorizationFlags?.some(x => x === AppPermission.FinalizeDescription);
this.canInviteDmpUsers = this.authService.hasPermission(AppPermission.InviteDmpUsers) ||
this.description.dmp?.authorizationFlags?.some(x => x === AppPermission.InviteDmpUsers);
// const breadCrumbs = []; // const breadCrumbs = [];
// breadCrumbs.push({ parentComponentName: null, label: this.language.instant('NAV-BAR.MY-DESCRIPTION-DESCRIPTIONS'), url: "/descriptions" }); // breadCrumbs.push({ parentComponentName: null, label: this.language.instant('NAV-BAR.MY-DESCRIPTION-DESCRIPTIONS'), url: "/descriptions" });
// breadCrumbs.push({ parentComponentName: 'DescriptionListingComponent', label: this.description.label, url: '/descriptions/overview/' + this.description.id }); // breadCrumbs.push({ parentComponentName: 'DescriptionListingComponent', label: this.description.label, url: '/descriptions/overview/' + this.description.id });
// this.breadCrumbs = observableOf(breadCrumbs); // this.breadCrumbs = observableOf(breadCrumbs);
}, (error: any) => { }, (error: any) => {
if (error.status === 404) { if (error.status === 404) {
return this.onFetchingDeletedCallbackError('/descriptions/'); return this.onFetchingDeletedCallbackError('/descriptions/');
@ -172,12 +193,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
this.router.navigateByUrl('/reload', { skipLocationChange: true }).then(() => this.router.navigate([path])); this.router.navigateByUrl('/reload', { skipLocationChange: true }).then(() => this.router.navigate([path]));
} }
setIsUserOwner() {
if (this.description) {
const principalId: Guid = this.authentication.userId();
if (principalId) this.isUserOwner = !!this.description.dmp.dmpUsers.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id));
}
}
isUserAuthor(userId: Guid): boolean { isUserAuthor(userId: Guid): boolean {
if (this.isAuthenticated()) { if (this.isAuthenticated()) {
@ -186,11 +201,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
} else return false; } else return false;
} }
isUserDescriptionRelated(): boolean {
const principalId: Guid = this.authentication.userId();
return this.description.dmp.dmpUsers.some(x => (x.user.id === principalId));
}
openShareDialog(rowId: any, rowName: any) { openShareDialog(rowId: any, rowName: any) {
// TODO: add dialog // TODO: add dialog
@ -439,6 +449,11 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
nameof<Description>(x => x.status), nameof<Description>(x => x.status),
nameof<Description>(x => x.updatedAt), nameof<Description>(x => x.updatedAt),
nameof<Description>(x => x.hash), nameof<Description>(x => x.hash),
[nameof<Description>(x => x.authorizationFlags), AppPermission.EditDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.DeleteDescription].join('.'),
[nameof<Description>(x => x.authorizationFlags), AppPermission.FinalizeDescription].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('.'),
@ -449,6 +464,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.label)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.label)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.accessType)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.accessType)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.status)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.status)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.authorizationFlags),AppPermission.InviteDmpUsers].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
[nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.name)].join('.'), [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.name)].join('.'),

View File

@ -82,20 +82,20 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
}; };
protected get canDelete(): boolean { protected get canDelete(): boolean {
return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDmp); return !this.isDeleted && !this.isNew && (this.hasPermission(this.authService.permissionEnum.DeleteDmp) || this.item?.authorizationFlags?.some(x => x === AppPermission.DeleteDmp));
} }
protected get canSave(): boolean { protected get canSave(): boolean {
return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditDmp); return !this.isDeleted && (this.hasPermission(this.authService.permissionEnum.EditDmp) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditDmp));
} }
protected get canFinalize(): boolean { protected get canFinalize(): boolean {
return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditDmp); return !this.isDeleted && (this.hasPermission(this.authService.permissionEnum.EditDmp) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditDmp));
} }
private hasPermission(permission: AppPermission): boolean { private hasPermission(permission: AppPermission): boolean {
return this.authService.hasPermission(permission) || this.editorModel?.permissions?.includes(permission); return this.authService.hasPermission(permission) || this.item?.authorizationFlags?.some(x => x === permission) || this.editorModel?.permissions?.includes(permission);
} }
constructor( constructor(

View File

@ -40,6 +40,7 @@ export class DmpEditorResolver extends BaseEditorResolver {
nameof<Dmp>(x => x.hash), nameof<Dmp>(x => x.hash),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'), [nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldId)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldId)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldValue)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldValue)].join('.'),