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
public static String BrowseDescription = "BrowseDescription";
public static String EditDescription = "EditDescription";
public static String FinalizeDescription = "FinalizeDescription";
public static String DeleteDescription = "DeleteDescription";
public static String CloneDescription = "CloneDescription";

View File

@ -12,4 +12,8 @@ public interface AuthorizationContentResolver {
AffiliatedResource dmpAffiliation(UUID id);
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.commons.enums.IsActive;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DescriptionEntity;
import eu.eudat.data.DmpEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.model.Description;
import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpUserQuery;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
@ -14,6 +17,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequestScope
@ -60,6 +64,37 @@ public class AuthorizationContentResolverImpl implements AuthorizationContentRes
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){
List<UUID> idsToResolve = new ArrayList<>();
for (UUID id : ids){

View File

@ -71,6 +71,9 @@ public class Description {
public static final String _descriptionTemplate = "descriptionTemplate";
private List<String> authorizationFlags;
public static final String _authorizationFlags = "authorizationFlags";
private Dmp dmp;
public static final String _dmp = "dmp";
@ -203,4 +206,12 @@ public class Description {
public void setDescriptionTemplate(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;
import eu.eudat.authorization.AffiliatedResource;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.XmlHandlingService;
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.data.DescriptionEntity;
import eu.eudat.data.DescriptionTemplateEntity;
import eu.eudat.data.DmpEntity;
import eu.eudat.data.UserRoleEntity;
import eu.eudat.model.*;
import eu.eudat.model.builder.descriptionpropertiesdefinition.PropertyDefinitionBuilder;
import eu.eudat.query.*;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
@ -37,6 +41,8 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
private final BuilderFactory builderFactory;
private final JsonHandlingService jsonHandlingService;
private final XmlHandlingService xmlHandlingService;
private final AuthorizationService authorizationService;
private final AuthorizationContentResolver authorizationContentResolver;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -44,12 +50,14 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
public DescriptionBuilder(
ConventionService conventionService,
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)));
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.jsonHandlingService = jsonHandlingService;
this.xmlHandlingService = xmlHandlingService;
this.authorizationService = authorizationService;
this.authorizationContentResolver = authorizationContentResolver;
}
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;
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<>();
for (DescriptionEntity d : data) {
Description m = new Description();
@ -108,6 +119,7 @@ public class DescriptionBuilder extends BaseBuilder<Description, DescriptionEnti
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));
}
if (affiliatedResourceMap != null && !authorizationFlags.isEmpty()) m.setAuthorizationFlags(this.evaluateAuthorizationFlags(this.authorizationService, authorizationFlags, affiliatedResourceMap.getOrDefault(d.getId(), null)));
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);
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()));
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());
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);
}
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())
return;
this.authService.authorizeForce(Permission.BrowseTag);
this.authService.authorizeForce(Permission.BrowseTag, Permission.DeferredAffiliation);
FieldSet createdByFields = fields.extractPrefixed(this.asIndexerPrefix(Tag._createdBy));
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.match(Description._descriptionTemplate) || item.match(PublicDescription._descriptionTemplate)) return DescriptionEntity._descriptionTemplateId;
else if (item.prefix(Description._dmp)) return DescriptionEntity._dmpId;
else if (item.match(Description._dmp)) return DescriptionEntity._dmpId;
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.IsActive;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.DmpReferenceEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.data.ReferenceEntity;
import eu.eudat.data.*;
import eu.eudat.model.DmpUser;
import eu.eudat.model.PublicDmpUser;
import eu.eudat.query.utils.BuildSubQueryInput;
@ -19,6 +17,7 @@ import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@ -36,6 +35,8 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
private Collection<UUID> dmpIds;
private Collection<UUID> descriptionIds;
private Collection<UUID> userIds;
private Collection<DmpUserRole> userRoles;
@ -89,6 +90,21 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
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) {
this.userRoles = List.of(value);
return this;
@ -161,7 +177,7 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
@Override
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
@ -208,6 +224,19 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
inClause.value(item);
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) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpUserEntity._userId));
for (UUID item : this.userIds)

View File

@ -1,9 +1,17 @@
package eu.eudat.query;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
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.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.QueryBase;
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);
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) {
@ -153,6 +168,37 @@ public class TagQuery extends QueryBase<TagEntity> {
protected Class<TagEntity> entityClass() {
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
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.Permission;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import eu.eudat.commons.JsonHandlingService;
import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.enums.*;
@ -96,6 +97,7 @@ public class DescriptionServiceImpl implements DescriptionService {
private final StorageFileProperties storageFileConfig;
private final StorageFileService storageFileService;
private final DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler;
private final AuthorizationContentResolver authorizationContentResolver;
@Autowired
public DescriptionServiceImpl(
@ -110,7 +112,7 @@ public class DescriptionServiceImpl implements DescriptionService {
QueryFactory queryFactory,
JsonHandlingService jsonHandlingService,
UserScope userScope,
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler) {
XmlHandlingService xmlHandlingService, NotifyIntegrationEventHandler eventHandler, NotificationProperties notificationProperties, FileTransformerService fileTransformerService, ElasticService elasticService, ValidatorFactory validatorFactory, StorageFileProperties storageFileConfig, StorageFileService storageFileService, DescriptionTouchedIntegrationEventHandler descriptionTouchedIntegrationEventHandler, AuthorizationContentResolver authorizationContentResolver) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
@ -131,6 +133,7 @@ public class DescriptionServiceImpl implements DescriptionService {
this.storageFileConfig = storageFileConfig;
this.storageFileService = storageFileService;
this.descriptionTouchedIntegrationEventHandler = descriptionTouchedIntegrationEventHandler;
this.authorizationContentResolver = authorizationContentResolver;
}
//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 {
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());
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;
if (isUpdate) {
@ -336,13 +340,14 @@ public class DescriptionServiceImpl implements DescriptionService {
public Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException {
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());
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 (!data.getStatus().equals(model.getStatus())){
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());
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());
@ -650,7 +655,7 @@ public class DescriptionServiceImpl implements DescriptionService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
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);
}
@ -663,7 +668,7 @@ public class DescriptionServiceImpl implements DescriptionService {
public void clone(UUID dmpId, UUID descriptionId) throws InvalidApplicationException, IOException {
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();
@ -743,7 +748,8 @@ public class DescriptionServiceImpl implements DescriptionService {
@Override
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();
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
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();
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:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
EditDescription:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
FinalizeDescription:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
DeleteDescription:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ]
clients: [ ]
allowAnonymous: false
@ -151,11 +181,16 @@ permissions:
CloneDescription:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
# Tag
BrowseTag:
roles:

View File

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

View File

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

View File

@ -78,24 +78,6 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
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(
// BaseFormEditor injected dependencies
protected dialog: MatDialog,
@ -606,7 +588,9 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
}
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.selectedSystemFields = this.selectedSystemFieldDisabled();

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
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 { 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';
@ -50,6 +51,10 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
nameof<Description>(x => x.description),
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.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.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.isActive)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition)].join('.'),

View File

@ -21,10 +21,11 @@ const routes: Routes = [
data: {
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}),
authContext: {
permissions: [AppPermission.EditDescription]
}
})
// ,
// authContext: {
// permissions: [AppPermission.EditDescription]
// }
}
},
{
@ -55,10 +56,11 @@ const routes: Routes = [
data: {
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION'
}),
authContext: {
permissions: [AppPermission.EditDescription]
}
})
// ,
// authContext: {
// permissions: [AppPermission.EditDescription]
// }
}
},
{
@ -72,10 +74,11 @@ const routes: Routes = [
data: {
...BreadcrumbService.generateRouteDataConfiguration({
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 { DescriptionStatus } from '@app/core/common/enum/description-status';
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 { DescriptionTemplate } from '@app/core/model/description-template/description-template';
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.status),
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.label)].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.Draft" class="col-auto description-title-draft">{{description.label}}</div>
<div class="description-subtitle">
<span *ngIf="isUserDescriptionRelated()" class="col-auto">{{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }}</span>
<span *ngIf="isUserDescriptionRelated()">.</span>
<span *ngIf="canEdit" class="col-auto">{{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }}</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 *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>
@ -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="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() && 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>
<mat-menu #actionsMenu="matMenu">
<button *ngIf="isAuthenticated()" mat-menu-item (click)="copyToDmp(description)" class="menu-item">
<mat-icon>file_copy</mat-icon>{{'DESCRIPTION-LISTING.ACTIONS.COPY-DESCRIPTION' | translate}}
</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 }}
</button>
</mat-menu>

View File

@ -26,6 +26,7 @@ import * as FileSaver from 'file-saver';
import { takeUntil } from 'rxjs/operators';
import { DescriptionStatus } from '../../../../core/common/enum/description-status';
import { DescriptionCopyDialogComponent } from '../../description-copy-dialog/description-copy-dialog.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@Component({
selector: 'app-description-listing-item-component',
@ -44,6 +45,8 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
isUserOwner: boolean;
descriptionStatusEnum = DescriptionStatus;
dmpAccessTypeEnum = DmpAccessType;
canDelete: boolean = false;
canEdit: boolean = false;
constructor(
@ -80,6 +83,13 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
this.isDeleted = false;
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() {
@ -240,11 +250,4 @@ export class DescriptionListingItemComponent extends BaseComponent implements On
onDeleteCallbackError(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>
</div>
<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">
{{ enumUtils.toDmpUserRolesString(dmpService.getCurrentUserRolesInDmp(description?.dmp?.dmpUsers)) }}
</p>
</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">
<mat-icon class="status-icon">public</mat-icon>
{{'DESCRIPTION-OVERVIEW.PUBLIC' | translate}}
@ -39,13 +39,13 @@
</div>
</div>
<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>
</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>
</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>
</button>
</div>
@ -92,7 +92,7 @@
</div>
<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 *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)">
<button mat-mini-fab class="finalize-btn">
<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>
</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 *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)">
<mat-icon>group_add</mat-icon>
{{'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 { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@Component({
@ -49,7 +50,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
isPublicView = true;
hasPublishButton: boolean = true;
// breadCrumbs: Observable<BreadcrumbItem[]> = observableOf();
isUserOwner: boolean;
expand = false;
lockStatus: Boolean;
descriptionStatusEnum = DescriptionStatus;
@ -57,6 +57,11 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
dmpStatusEnum = DmpStatus;
dmpUserRoleEnum = DmpUserRole;
canEdit = false;
canDelete = false;
canFinalize = false;
canInviteDmpUsers = false;
constructor(
private route: ActivatedRoute,
private router: Router,
@ -72,6 +77,7 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
public enumUtils: EnumUtils,
private matomoService: MatomoService,
private fileUtils: FileUtils,
private authService: AuthService,
public fileTransformerService: FileTransformerService,
private referenceTypeService: ReferenceTypeService,
private fb: UntypedFormBuilder,
@ -81,6 +87,10 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
ngOnInit() {
this.matomoService.trackPageView('Description Overview');
this.canDelete = false;
this.canEdit = false;
this.canFinalize = false;
this.canInviteDmpUsers = false;
// Gets description data using parameter id
this.route.params
.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.users = this.description.dmp.users;
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 = [];
// 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 });
// this.breadCrumbs = observableOf(breadCrumbs);
}, (error: any) => {
if (error.status === 404) {
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]));
}
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 {
if (this.isAuthenticated()) {
@ -186,11 +201,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
} 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) {
// TODO: add dialog
@ -439,6 +449,11 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
nameof<Description>(x => x.status),
nameof<Description>(x => x.updatedAt),
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.label)].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.accessType)].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.user.id)].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 {
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 {
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 {
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 {
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(

View File

@ -40,6 +40,7 @@ export class DmpEditorResolver extends BaseEditorResolver {
nameof<Dmp>(x => x.hash),
[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.fieldValue)].join('.'),