Merge branch 'dmp-refactoring' of https://code-repo.d4science.org/MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Sofia Papacharalampous 2024-03-19 17:26:08 +02:00
commit 28ae763eb8
30 changed files with 322 additions and 141 deletions

View File

@ -56,7 +56,8 @@ public class AuditableAction {
public static final EventId Description_UploadFieldFiles = new EventId(6007, "Description_UploadFieldFiles"); public static final EventId Description_UploadFieldFiles = new EventId(6007, "Description_UploadFieldFiles");
public static final EventId Description_GetFieldFile = new EventId(6008, "Description_GetFieldFile"); public static final EventId Description_GetFieldFile = new EventId(6008, "Description_GetFieldFile");
public static final EventId Description_Validate = new EventId(6009, "Description_Validate"); public static final EventId Description_Validate = new EventId(6009, "Description_Validate");
public static final EventId Description_GetDescriptionSectionPermissions = new EventId(6010, "Description_GetDescriptionSectionPermissions");
public static final EventId Reference_Query = new EventId(7000, "Reference_Query"); public static final EventId Reference_Query = new EventId(7000, "Reference_Query");
public static final EventId Reference_Lookup = new EventId(7001, "Reference_Lookup"); public static final EventId Reference_Lookup = new EventId(7001, "Reference_Lookup");

View File

@ -74,7 +74,6 @@ public final class Permission {
public static String DeleteDmp = "DeleteDmp"; public static String DeleteDmp = "DeleteDmp";
public static String CloneDmp = "CloneDmp"; public static String CloneDmp = "CloneDmp";
public static String CreateNewVersionDmp = "CreateNewVersionDmp"; public static String CreateNewVersionDmp = "CreateNewVersionDmp";
public static String ExportDmp = "ExportDmp";
public static String FinalizeDmp = "FinalizeDmp"; public static String FinalizeDmp = "FinalizeDmp";
public static String UndoFinalizeDmp = "UndoFinalizeDmp"; public static String UndoFinalizeDmp = "UndoFinalizeDmp";
public static String AssignDmpUsers = "AssignDmpUsers"; public static String AssignDmpUsers = "AssignDmpUsers";

View File

@ -16,4 +16,8 @@ public interface AuthorizationContentResolver {
AffiliatedResource descriptionAffiliation(UUID id); AffiliatedResource descriptionAffiliation(UUID id);
Map<UUID, AffiliatedResource> descriptionsAffiliation(List<UUID> ids); Map<UUID, AffiliatedResource> descriptionsAffiliation(List<UUID> ids);
AffiliatedResource descriptionsAffiliationBySection(UUID dmpId, UUID sectionId);
Map<UUID, AffiliatedResource> descriptionsAffiliationBySections(UUID dmpId, List<UUID> sectionIds);
} }

View File

@ -5,11 +5,14 @@ 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.DescriptionEntity;
import eu.eudat.data.DmpDescriptionTemplateEntity;
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.Description;
import eu.eudat.model.DmpDescriptionTemplate;
import eu.eudat.model.DmpUser; import eu.eudat.model.DmpUser;
import eu.eudat.query.DescriptionQuery; import eu.eudat.query.DescriptionQuery;
import eu.eudat.query.DmpDescriptionTemplateQuery;
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;
@ -54,7 +57,7 @@ public class AuthorizationContentResolverImpl implements AuthorizationContentRes
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, DmpEntity.class.getSimpleName()); List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, DmpEntity.class.getSimpleName());
if (idsToResolve.isEmpty()) return affiliatedResources; if (idsToResolve.isEmpty()) return affiliatedResources;
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(ids).userIds(userId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._role).ensure(DmpUser._dmp)); List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(ids).sectionIsEmpty(true).userIds(userId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._role).ensure(DmpUser._dmp));
for (DmpUserEntity dmpUser : dmpUsers){ for (DmpUserEntity dmpUser : dmpUsers){
affiliatedResources.get(dmpUser.getDmpId()).getDmpUserRoles().add(dmpUser.getRole()); affiliatedResources.get(dmpUser.getDmpId()).getDmpUserRoles().add(dmpUser.getRole());
@ -80,14 +83,23 @@ public class AuthorizationContentResolverImpl implements AuthorizationContentRes
List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, DescriptionEntity.class.getSimpleName()); List<UUID> idsToResolve = this.getAffiliatedFromCache(ids, userId, affiliatedResources, DescriptionEntity.class.getSimpleName());
if (idsToResolve.isEmpty()) return affiliatedResources; 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<DescriptionEntity> descriptionEntities = this.queryFactory.query(DescriptionQuery.class).ids(ids).collectAs(new BaseFieldSet().ensure(Description._id).ensure(Description._dmpDescriptionTemplate).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)); List<DmpDescriptionTemplateEntity> dmpDescriptionTemplateEntities = this.queryFactory.query(DmpDescriptionTemplateQuery.class).ids(descriptionEntities.stream().map(DescriptionEntity::getDmpDescriptionTemplateId).distinct().toList()).collectAs(new BaseFieldSet().ensure(DmpDescriptionTemplate._id).ensure(DmpDescriptionTemplate._sectionId));
Map<UUID, DmpDescriptionTemplateEntity> dmpDescriptionTemplateEntityMap = dmpDescriptionTemplateEntities == null ? new HashMap<>() : dmpDescriptionTemplateEntities.stream().collect(Collectors.toMap(DmpDescriptionTemplateEntity::getId, x-> x));
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).descriptionIds(ids).userIds(userId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._role).ensure(DmpUser._sectionId).ensure(DmpUser._dmp));
Map<UUID, List<DmpUserEntity>> dmpUsersMap = dmpUsers.stream().collect(Collectors.groupingBy(DmpUserEntity::getDmpId)); Map<UUID, List<DmpUserEntity>> dmpUsersMap = dmpUsers.stream().collect(Collectors.groupingBy(DmpUserEntity::getDmpId));
for (DescriptionEntity description : descriptionEntities){ for (DescriptionEntity description : descriptionEntities){
List<DmpUserEntity> dmpDescriptionUsers = dmpUsersMap.getOrDefault(description.getDmpId(), new ArrayList<>()); List<DmpUserEntity> dmpDescriptionUsers = dmpUsersMap.getOrDefault(description.getDmpId(), new ArrayList<>());
for (DmpUserEntity dmpUser : dmpDescriptionUsers) { for (DmpUserEntity dmpUser : dmpDescriptionUsers) {
affiliatedResources.get(description.getId()).getDmpUserRoles().add(dmpUser.getRole()); if (dmpUser.getSectionId() == null) affiliatedResources.get(description.getId()).getDmpUserRoles().add(dmpUser.getRole());
else {
DmpDescriptionTemplateEntity dmpDescriptionTemplateEntity = dmpDescriptionTemplateEntityMap.getOrDefault(description.getDmpDescriptionTemplateId(), null);
if (dmpDescriptionTemplateEntity != null && dmpUser.getSectionId().equals(dmpDescriptionTemplateEntity.getSectionId())){
affiliatedResources.get(description.getId()).getDmpUserRoles().add(dmpUser.getRole());
}
}
} }
} }
@ -95,6 +107,37 @@ public class AuthorizationContentResolverImpl implements AuthorizationContentRes
return affiliatedResources; return affiliatedResources;
} }
@Override
public AffiliatedResource descriptionsAffiliationBySection(UUID dmpId, UUID sectionId){
return this.descriptionsAffiliationBySections(dmpId, List.of(sectionId)).getOrDefault(sectionId, new AffiliatedResource());
}
@Override
public Map<UUID, AffiliatedResource> descriptionsAffiliationBySections(UUID dmpId, List<UUID> sectionIds){
UUID userId = this.userScope.getUserIdSafe();
Map<UUID, AffiliatedResource> affiliatedResources = new HashMap<>();
for (UUID id : sectionIds){
affiliatedResources.put(id, new AffiliatedResource());
}
if (userId == null || !userScope.isSet()) return affiliatedResources;
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).dmpIds(dmpId).userIds(userId).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(DmpUser._role).ensure(DmpUser._sectionId).ensure(DmpUser._dmp));
for (UUID sectionId : sectionIds.stream().distinct().toList()){
List<DmpUserEntity> dmpSectionUsers = dmpUsers.stream().filter(x-> x.getSectionId() == null || x.getSectionId().equals(sectionId)).toList();
for (DmpUserEntity dmpUser : dmpSectionUsers) {
if (dmpUser.getSectionId() == null) affiliatedResources.get(sectionId).getDmpUserRoles().add(dmpUser.getRole());
else {
if (dmpUser.getSectionId().equals(sectionId)){
affiliatedResources.get(sectionId).getDmpUserRoles().add(dmpUser.getRole());
}
}
}
}
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

@ -33,7 +33,7 @@ public class DmpAssociatedUserCensor extends BaseCensor {
logger.debug(new DataLogEntry("censoring fields", fields)); logger.debug(new DataLogEntry("censoring fields", fields));
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; return;
this.authService.authorizeAtLeastOneForce(userId != null ? List.of(new OwnedResource(userId)) : null, Permission.BrowseDmpAssociatedUser); this.authService.authorizeAtLeastOneForce(userId != null ? List.of(new OwnedResource(userId)) : null, Permission.BrowseDmpAssociatedUser, Permission.DeferredAffiliation);
} }
} }

View File

@ -32,6 +32,10 @@ public class Section {
public final static String _prefillingSources = "prefillingSources"; public final static String _prefillingSources = "prefillingSources";
private List<PrefillingSource> prefillingSources; private List<PrefillingSource> prefillingSources;
public final static String _canCreate = "canCreate";
private Boolean canCreate;
public UUID getId() { public UUID getId() {
return id; return id;
} }
@ -95,6 +99,14 @@ public class Section {
public void setPrefillingSources(List<PrefillingSource> prefillingSources) { public void setPrefillingSources(List<PrefillingSource> prefillingSources) {
this.prefillingSources = prefillingSources; this.prefillingSources = prefillingSources;
} }
public Boolean getCanCreate() {
return canCreate;
}
public void setCanCreate(Boolean canCreate) {
this.canCreate = canCreate;
}
} }

View File

@ -0,0 +1,89 @@
package eu.eudat.model.persist;
import eu.eudat.commons.validation.BaseValidator;
import eu.eudat.convention.ConventionService;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import gr.cite.tools.validation.specification.Specification;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class DescriptionSectionPermissionResolver {
private UUID dmpId;
public static final String _dmpId = "dmpId";
private List<UUID> sectionIds;
public static final String _sectionIds = "sectionIds";
private List<String> permissions;
public static final String _permissions = "permissions";
public UUID getDmpId() {
return dmpId;
}
public void setDmpId(UUID dmpId) {
this.dmpId = dmpId;
}
public List<UUID> getSectionIds() {
return sectionIds;
}
public void setSectionIds(List<UUID> sectionIds) {
this.sectionIds = sectionIds;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
@Component(DescriptionSectionPermissionResolverPersistValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class DescriptionSectionPermissionResolverPersistValidator extends BaseValidator<DescriptionSectionPermissionResolver> {
public static final String ValidatorName = "DescriptionSectionPermissionResolverPersistValidator";
private final MessageSource messageSource;
protected DescriptionSectionPermissionResolverPersistValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource) {
super(conventionService, errors);
this.messageSource = messageSource;
}
@Override
protected Class<DescriptionSectionPermissionResolver> modelClass() {
return DescriptionSectionPermissionResolver.class;
}
@Override
protected List<Specification> specifications(DescriptionSectionPermissionResolver item) {
return Arrays.asList(
this.spec()
.must(() -> this.isValidGuid(item.getDmpId()))
.failOn(DescriptionSectionPermissionResolver._dmpId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionSectionPermissionResolver._dmpId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> item.getPermissions() != null && !item.getPermissions().isEmpty())
.failOn(DescriptionSectionPermissionResolver._permissions).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionSectionPermissionResolver._permissions}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> item.getSectionIds() != null && !item.getSectionIds().isEmpty())
.failOn(DescriptionSectionPermissionResolver._sectionIds).failWith(messageSource.getMessage("Validation_Required", new Object[]{DescriptionSectionPermissionResolver._sectionIds}, LocaleContextHolder.getLocale()))
);
}
}
}

View File

@ -118,3 +118,4 @@ public class LockPersist {
} }
} }

View File

@ -158,15 +158,7 @@ public class DescriptionReferenceQuery extends QueryBase<DescriptionReferenceEnt
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();
if (userId != null || usePublic ) { if (userId != null || usePublic ) {
UUID finalUserId = userId; predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionReferenceEntity._descriptionId)).value(queryUtilsService.buildDescriptionAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, userId, usePublic)));
Subquery<UUID> descriptionSubquery = queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(
new BuildSubQueryInput.Builder<>(DescriptionEntity.class, UUID.class, queryContext)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(DescriptionEntity._id))
.filterFunc((subQueryRoot, cb) ->
cb.in(subQueryRoot.get(DescriptionEntity._dmpDescriptionTemplateId)).value(queryUtilsService.buildDmpAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, finalUserId, usePublic))
)
));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionReferenceEntity._descriptionId)).value(descriptionSubquery));
} }
if (!predicates.isEmpty()) { if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);

View File

@ -144,37 +144,6 @@ public class DescriptionTagQuery extends QueryBase<DescriptionTagEntity> {
} }
@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.BrowseDescriptionTag)) return null;
UUID userId;
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe();
else userId = null;
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (userId != null || usePublic ) {
UUID finalUserId = userId;
Subquery<UUID> descriptionSubquery = queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(
new BuildSubQueryInput.Builder<>(DescriptionEntity.class, UUID.class, queryContext)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(DescriptionEntity._id))
.filterFunc((subQueryRoot, cb) ->
cb.in(subQueryRoot.get(DescriptionEntity._dmpDescriptionTemplateId)).value(queryUtilsService.buildDmpAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, finalUserId, usePublic))
)
));
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DescriptionTagEntity._descriptionId)).value(descriptionSubquery));
}
if (predicates.size() > 0) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override @Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) { protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();

View File

@ -42,6 +42,7 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
private Collection<DmpUserRole> userRoles; private Collection<DmpUserRole> userRoles;
private Collection<UUID> sectionIds; private Collection<UUID> sectionIds;
private Boolean sectionIsEmpty;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None); private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
@ -149,6 +150,11 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
this.sectionIds = values; this.sectionIds = values;
return this; return this;
} }
public DmpUserQuery sectionIsEmpty(Boolean sectionIsEmpty) {
this.sectionIsEmpty = sectionIsEmpty;
return this;
}
public DmpUserQuery authorize(EnumSet<AuthorizationFlags> values) { public DmpUserQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values; this.authorize = values;
@ -224,6 +230,10 @@ public class DmpUserQuery extends QueryBase<DmpUserEntity> {
inClause.value(item); inClause.value(item);
predicates.add(inClause); predicates.add(inClause);
} }
if (this.sectionIsEmpty != null){
if(this.sectionIsEmpty) predicates.add(queryContext.CriteriaBuilder.isNull(queryContext.Root.get(DmpUserEntity._sectionId)));
else predicates.add(queryContext.CriteriaBuilder.isNotNull(queryContext.Root.get(DmpUserEntity._sectionId)));
}
if (this.descriptionIds != null) { if (this.descriptionIds != null) {
Subquery<UUID> descriptionSubquery = queryUtilsService.buildSubQuery(new BuildSubQueryInput<>( Subquery<UUID> descriptionSubquery = queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(
new BuildSubQueryInput.Builder<>(DescriptionEntity.class, UUID.class, queryContext) new BuildSubQueryInput.Builder<>(DescriptionEntity.class, UUID.class, queryContext)

View File

@ -159,7 +159,7 @@ public class UserQuery extends QueryBase<UserEntity> {
UUID finalUserId = userId; UUID finalUserId = userId;
predicates.add(queryContext.CriteriaBuilder.or( predicates.add(queryContext.CriteriaBuilder.or(
userId != null ? queryContext.CriteriaBuilder.in(queryContext.Root.get(UserEntity._id)).value(userId) : queryContext.CriteriaBuilder.or(), //Creates a false query userId != null ? queryContext.CriteriaBuilder.in(queryContext.Root.get(UserEntity._id)).value(userId) : queryContext.CriteriaBuilder.or(), //Creates a false query
queryContext.CriteriaBuilder.in(queryContext.Root.get(ReferenceEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(DmpUserEntity.class, UUID.class) queryContext.CriteriaBuilder.in(queryContext.Root.get(UserEntity._id)).value(queryUtilsService.buildSubQuery(new BuildSubQueryInput<>(new BuildSubQueryInput.Builder<>(DmpUserEntity.class, UUID.class)
.query(queryContext.Query) .query(queryContext.Query)
.criteriaBuilder(queryContext.CriteriaBuilder) .criteriaBuilder(queryContext.CriteriaBuilder)
.keyPathFunc((subQueryRoot) -> subQueryRoot.get(DmpUserEntity._userId)) .keyPathFunc((subQueryRoot) -> subQueryRoot.get(DmpUserEntity._userId))

View File

@ -53,9 +53,13 @@ public class QueryUtilsServiceImpl implements QueryUtilsService {
usePublic ? cb.and( usePublic ? cb.and(
cb.equal(subQueryRoot.get(DescriptionEntity._status), DescriptionStatus.Finalized), cb.equal(subQueryRoot.get(DescriptionEntity._status), DescriptionStatus.Finalized),
cb.equal(subQueryRoot.get(DescriptionEntity._isActive), IsActive.Active), cb.equal(subQueryRoot.get(DescriptionEntity._isActive), IsActive.Active),
cb.in(subQueryRoot.get(DescriptionEntity._dmpId)).value(this.buildDmpAuthZSubQuery(query, criteriaBuilder, userId, usePublic)) cb.in(subQueryRoot.get(DescriptionEntity._dmpId)).value(this.buildPublicDmpAuthZSubQuery(query, criteriaBuilder, usePublic))
): cb.or(), //Creates a false query ): cb.or(), //Creates a false query
userId != null ? cb.equal(subQueryRoot.get(DescriptionEntity._createdById), userId) : cb.or() //Creates a false query userId != null ?
cb.or(
cb.equal(subQueryRoot.get(DescriptionEntity._createdById), userId),
cb.in(subQueryRoot.get(DescriptionEntity._dmpId)).value(this.buildDmpUserAuthZSubQuery(query, criteriaBuilder, userId))
) : cb.or() //Creates a false query
) )
) )
)); ));

View File

@ -6,6 +6,7 @@ import eu.eudat.model.DescriptionValidationResult;
import eu.eudat.model.StorageFile; import eu.eudat.model.StorageFile;
import eu.eudat.model.persist.DescriptionFieldFilePersist; import eu.eudat.model.persist.DescriptionFieldFilePersist;
import eu.eudat.model.persist.DescriptionPersist; import eu.eudat.model.persist.DescriptionPersist;
import eu.eudat.model.persist.DescriptionSectionPermissionResolver;
import eu.eudat.model.persist.DescriptionStatusPersist; import eu.eudat.model.persist.DescriptionStatusPersist;
import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyForbiddenException;
@ -18,11 +19,14 @@ import org.springframework.web.multipart.MultipartFile;
import javax.management.InvalidApplicationException; import javax.management.InvalidApplicationException;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
public interface DescriptionService { public interface DescriptionService {
Description persist(DescriptionPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException; Map<UUID, List<String>> getDescriptionSectionPermissions(DescriptionSectionPermissionResolver model);
Description persist(DescriptionPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, IOException;
Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException; Description persistStatus(DescriptionStatusPersist model, FieldSet fields) throws IOException;
void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException; void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException;

View File

@ -1,5 +1,6 @@
package eu.eudat.service.description; package eu.eudat.service.description;
import eu.eudat.authorization.AffiliatedResource;
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.authorization.authorizationcontentresolver.AuthorizationContentResolver;
@ -26,7 +27,6 @@ import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler; import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import eu.eudat.model.*; import eu.eudat.model.*;
import eu.eudat.model.builder.DescriptionBuilder; import eu.eudat.model.builder.DescriptionBuilder;
import eu.eudat.model.builder.DescriptionTemplateBuilder;
import eu.eudat.model.deleter.DescriptionDeleter; import eu.eudat.model.deleter.DescriptionDeleter;
import eu.eudat.model.deleter.DescriptionReferenceDeleter; import eu.eudat.model.deleter.DescriptionReferenceDeleter;
import eu.eudat.model.deleter.DescriptionTagDeleter; import eu.eudat.model.deleter.DescriptionTagDeleter;
@ -50,7 +50,6 @@ import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService; import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.ValidationResult;
import gr.cite.tools.validation.ValidatorFactory; import gr.cite.tools.validation.ValidatorFactory;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
@ -138,6 +137,24 @@ public class DescriptionServiceImpl implements DescriptionService {
this.authorizationContentResolver = authorizationContentResolver; this.authorizationContentResolver = authorizationContentResolver;
} }
@Override
public Map<UUID, List<String>> getDescriptionSectionPermissions(DescriptionSectionPermissionResolver model) {
logger.debug(new MapLogEntry("get description section permissions").And("model", model));
Map<UUID, List<String>> response = new HashMap<>();
Map<UUID, AffiliatedResource> affiliatedResourceMap = this.authorizationContentResolver.descriptionsAffiliationBySections(model.getDmpId(), model.getSectionIds());
for (UUID sectionId : model.getSectionIds().stream().distinct().toList()){
AffiliatedResource affiliatedResource = affiliatedResourceMap.getOrDefault(sectionId, null);
response.put(sectionId, new ArrayList<>());
for (String permission : model.getPermissions()){
if (this.authorizationService.authorizeAtLeastOne(List.of(affiliatedResource), permission)) response.get(sectionId).add(permission);
}
}
return response;
}
//region Persist //region Persist
@Override @Override
@ -145,9 +162,12 @@ public class DescriptionServiceImpl implements DescriptionService {
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));
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);
DmpDescriptionTemplateEntity dmpDescriptionTemplate = this.entityManager.find(DmpDescriptionTemplateEntity.class, model.getDmpDescriptionTemplateId());
if (dmpDescriptionTemplate == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDmpDescriptionTemplateId(), DmpDescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (isUpdate) this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionAffiliation(model.getId())), Permission.EditDescription);
else this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.descriptionsAffiliationBySection(model.getDmpId(), dmpDescriptionTemplate.getSectionId())), Permission.EditDescription);
DescriptionEntity data; DescriptionEntity data;
if (isUpdate) { if (isUpdate) {
@ -167,9 +187,6 @@ public class DescriptionServiceImpl implements DescriptionService {
data.setDmpDescriptionTemplateId(model.getDmpDescriptionTemplateId()); data.setDmpDescriptionTemplateId(model.getDmpDescriptionTemplateId());
} }
DmpDescriptionTemplateEntity dmpDescriptionTemplate = this.entityManager.find(DmpDescriptionTemplateEntity.class, data.getDmpDescriptionTemplateId());
if (dmpDescriptionTemplate == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{data.getDmpDescriptionTemplateId(), DmpDescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
DescriptionTemplateEntity descriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, model.getDescriptionTemplateId()); DescriptionTemplateEntity descriptionTemplateEntity = this.entityManager.find(DescriptionTemplateEntity.class, model.getDescriptionTemplateId());
if (descriptionTemplateEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (descriptionTemplateEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
@ -267,11 +284,14 @@ public class DescriptionServiceImpl implements DescriptionService {
switch (status) { switch (status) {
case Draft: case Draft:
event.setNotificationType(notificationProperties.getDescriptionModifiedType()); event.setNotificationType(notificationProperties.getDescriptionModifiedType());
break;
case Finalized: case Finalized:
event.setNotificationType(notificationProperties.getDescriptionFinalisedType()); event.setNotificationType(notificationProperties.getDescriptionFinalisedType());
break;
default: default:
throw new MyApplicationException("Unsupported Description Status."); throw new MyApplicationException("Unsupported Description Status.");
} }
return event;
} }
// public List<eu.eudat.commons.types.descriptiontemplate.FieldEntity> getFieldById(String id){ // public List<eu.eudat.commons.types.descriptiontemplate.FieldEntity> getFieldById(String id){

View File

@ -32,7 +32,7 @@ public interface DmpService {
Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException; Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException;
List<DmpUser> assignUsers(UUID dmp, List<DmpUserPersist> model, FieldSet fields) throws InvalidApplicationException, IOException; List<DmpUser> assignUsers(UUID dmp, List<DmpUserPersist> model, FieldSet fields, boolean disableDelete) throws InvalidApplicationException, IOException;
Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException; Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException;
ResponseEntity<byte[]> export(UUID id, String transformerId, String exportType) throws InvalidApplicationException, IOException; ResponseEntity<byte[]> export(UUID id, String transformerId, String exportType) throws InvalidApplicationException, IOException;

View File

@ -492,7 +492,7 @@ public class DmpServiceImpl implements DmpService {
} }
@Override @Override
public List<DmpUser> assignUsers(UUID dmpId, List<DmpUserPersist> model, FieldSet fieldSet) throws InvalidApplicationException, IOException { public List<DmpUser> assignUsers(UUID dmpId, List<DmpUserPersist> model, FieldSet fieldSet, boolean disableDelete) throws InvalidApplicationException, IOException {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(dmpId)), Permission.AssignDmpUsers); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(dmpId)), Permission.AssignDmpUsers);
DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, dmpId); DmpEntity dmpEntity = this.entityManager.find(DmpEntity.class, dmpId);
@ -522,7 +522,7 @@ public class DmpServiceImpl implements DmpService {
} }
List<DmpUserEntity> toDelete = existingUsers.stream().filter(x-> updatedCreatedIds.stream().noneMatch(y-> y.equals(x.getUserId()))).collect(Collectors.toList()); List<DmpUserEntity> toDelete = existingUsers.stream().filter(x-> updatedCreatedIds.stream().noneMatch(y-> y.equals(x.getUserId()))).collect(Collectors.toList());
if (!toDelete.isEmpty()) this.deleterFactory.deleter(DmpUserDeleter.class).delete(toDelete); if (!toDelete.isEmpty() && !disableDelete) this.deleterFactory.deleter(DmpUserDeleter.class).delete(toDelete);
this.entityManager.flush(); this.entityManager.flush();
@ -916,7 +916,7 @@ public class DmpServiceImpl implements DmpService {
} }
} }
if(!usersToAssign.isEmpty()) this.assignUsers(id, usersToAssign, null); if(!usersToAssign.isEmpty()) this.assignUsers(id, usersToAssign, null, true);
} }
private void sendDmpInvitationExistingUser(UUID userId, DmpEntity dmp, DmpUserRole role) throws InvalidApplicationException { private void sendDmpInvitationExistingUser(UUID userId, DmpEntity dmp, DmpUserRole role) throws InvalidApplicationException {

View File

@ -1,5 +1,6 @@
package eu.eudat.service.lock; package eu.eudat.service.lock;
import eu.eudat.authorization.AffiliatedResource;
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.authorization.authorizationcontentresolver.AuthorizationContentResolver;
@ -80,7 +81,9 @@ public class LockServiceImpl implements LockService {
public Lock persist(LockPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException { public Lock persist(LockPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields)); logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields));
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getTarget())), Permission.EditLock); AffiliatedResource affiliatedResourceDmp = this.authorizationContentResolver.dmpAffiliation(model.getTarget());
AffiliatedResource affiliatedResourceDescription = this.authorizationContentResolver.descriptionAffiliation(model.getTarget());
this.authorizationService.authorizeAtLeastOneForce(List.of(affiliatedResourceDmp, affiliatedResourceDescription), Permission.EditLock);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
@ -179,7 +182,9 @@ public class LockServiceImpl implements LockService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting : {}", id); logger.debug("deleting : {}", id);
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.DeleteLock); AffiliatedResource affiliatedResourceDmp = this.authorizationContentResolver.dmpAffiliation(id);
AffiliatedResource affiliatedResourceDescription = this.authorizationContentResolver.descriptionAffiliation(id);
this.authorizationService.authorizeAtLeastOneForce(List.of(affiliatedResourceDmp, affiliatedResourceDescription), Permission.DeleteLock);
this.deleterFactory.deleter(LockDeleter.class).deleteAndSaveByIds(List.of(id)); this.deleterFactory.deleter(LockDeleter.class).deleteAndSaveByIds(List.of(id));
} }

View File

@ -10,6 +10,7 @@ import eu.eudat.data.StorageFileEntity;
import eu.eudat.model.*; import eu.eudat.model.*;
import eu.eudat.model.builder.PublicDescriptionBuilder; import eu.eudat.model.builder.PublicDescriptionBuilder;
import eu.eudat.model.persist.DescriptionFieldFilePersist; import eu.eudat.model.persist.DescriptionFieldFilePersist;
import eu.eudat.model.persist.DescriptionSectionPermissionResolver;
import eu.eudat.service.storage.StorageFileService; import eu.eudat.service.storage.StorageFileService;
import gr.cite.tools.validation.ValidationFilterAnnotation; import gr.cite.tools.validation.ValidationFilterAnnotation;
import eu.eudat.model.builder.DescriptionBuilder; import eu.eudat.model.builder.DescriptionBuilder;
@ -185,6 +186,19 @@ public class DescriptionController {
return persisted; return persisted;
} }
@PostMapping("get-description-section-permissions")
@ValidationFilterAnnotation(validator = DescriptionSectionPermissionResolver.DescriptionSectionPermissionResolverPersistValidator.ValidatorName, argumentName = "model")
public Map<UUID, List<String>> getDescriptionSectionPermissions(@RequestBody DescriptionSectionPermissionResolver model) {
logger.debug(new MapLogEntry("persisting" + Description.class.getSimpleName()).And("model", model));
Map<UUID, List<String>> persisted = this.descriptionService.getDescriptionSectionPermissions(model);
this.auditService.track(AuditableAction.Description_GetDescriptionSectionPermissions, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model)
));
return persisted;
}
@GetMapping("validate") @GetMapping("validate")
public List<DescriptionValidationResult> validate(@RequestParam(value="descriptionIds") List<UUID> descriptionIds) throws MyApplicationException, MyForbiddenException, MyNotFoundException { public List<DescriptionValidationResult> validate(@RequestParam(value="descriptionIds") List<UUID> descriptionIds) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("validating" + Description.class.getSimpleName()).And("descriptionIds", descriptionIds)); logger.debug(new MapLogEntry("validating" + Description.class.getSimpleName()).And("descriptionIds", descriptionIds));

View File

@ -242,7 +242,7 @@ public class DmpController {
public QueryResult<DmpUser> assignUsers(@PathVariable("id") UUID id, @RequestBody List<DmpUserPersist> model, FieldSet fieldSet) throws InvalidApplicationException, IOException { public QueryResult<DmpUser> assignUsers(@PathVariable("id") UUID id, @RequestBody List<DmpUserPersist> model, FieldSet fieldSet) throws InvalidApplicationException, IOException {
logger.debug(new MapLogEntry("assigning users to dmp").And("model", model).And("fieldSet", fieldSet)); logger.debug(new MapLogEntry("assigning users to dmp").And("model", model).And("fieldSet", fieldSet));
List<DmpUser> persisted = this.dmpService.assignUsers(id, model, fieldSet); List<DmpUser> persisted = this.dmpService.assignUsers(id, model, fieldSet, false);
this.auditService.track(AuditableAction.Dmp_Assign_Users, Map.ofEntries( this.auditService.track(AuditableAction.Dmp_Assign_Users, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model), new AbstractMap.SimpleEntry<String, Object>("model", model),

View File

@ -1,6 +1,7 @@
package eu.eudat.controllers; package eu.eudat.controllers;
import eu.eudat.audit.AuditableAction; import eu.eudat.audit.AuditableAction;
import eu.eudat.authorization.AffiliatedResource;
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.authorization.authorizationcontentresolver.AuthorizationContentResolver;
@ -156,7 +157,7 @@ public class LockController {
@GetMapping("target/status/{id}") @GetMapping("target/status/{id}")
public Boolean getLocked(@PathVariable("id") UUID targetId) throws Exception { public Boolean getLocked(@PathVariable("id") UUID targetId) throws Exception {
logger.debug(new MapLogEntry("is locked" + Lock.class.getSimpleName()).And("targetId", targetId)); logger.debug(new MapLogEntry("is locked" + Lock.class.getSimpleName()).And("targetId", targetId));
this.authService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(targetId)), Permission.BrowseLock); this.authService.authorizeForce(Permission.BrowseLock);
Boolean isLocked = this.lockService.isLocked(targetId); Boolean isLocked = this.lockService.isLocked(targetId);
this.auditService.track(AuditableAction.Lock_IsLocked, Map.ofEntries( this.auditService.track(AuditableAction.Lock_IsLocked, Map.ofEntries(
@ -168,8 +169,9 @@ public class LockController {
@Transactional @Transactional
@DeleteMapping("target/unlock/{id}") @DeleteMapping("target/unlock/{id}")
public boolean unlock(@PathVariable("id") UUID targetId) throws Exception { public boolean unlock(@PathVariable("id") UUID targetId) throws Exception {
logger.debug(new MapLogEntry("unlock" + Lock.class.getSimpleName()).And("targetId", targetId)); AffiliatedResource affiliatedResourceDmp = this.authorizationContentResolver.dmpAffiliation(targetId);
this.authService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(targetId)), Permission.BrowseLock); AffiliatedResource affiliatedResourceDescription = this.authorizationContentResolver.descriptionAffiliation(targetId);
this.authService.authorizeAtLeastOneForce(List.of(affiliatedResourceDmp, affiliatedResourceDescription), Permission.EditLock);
this.lockService.unlock(targetId); this.lockService.unlock(targetId);
this.auditService.track(AuditableAction.Lock_UnLocked, Map.ofEntries( this.auditService.track(AuditableAction.Lock_UnLocked, Map.ofEntries(

View File

@ -147,9 +147,7 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor - DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
@ -159,9 +157,7 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor - DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
@ -171,9 +167,7 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor - DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -184,9 +178,7 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor - DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -241,6 +233,12 @@ permissions:
BrowseDmpAssociatedUser: BrowseDmpAssociatedUser:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -380,9 +378,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -393,9 +388,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -406,9 +398,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -419,22 +408,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ]
clients: [ ]
allowAnonymous: false
allowAuthenticated: false
ExportDmp:
roles:
- Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -445,9 +418,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -458,9 +428,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -471,9 +438,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -484,9 +448,6 @@ permissions:
dmp: dmp:
roles: roles:
- Owner - Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -806,12 +767,9 @@ permissions:
BrowseLock: BrowseLock:
roles: roles:
- Admin - Admin
dmp: - DescriptionTemplateEditor
roles: - Manager
- Owner - User
- User
- DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false

View File

@ -10,7 +10,6 @@ export enum AppPermission {
DeleteDmpBlueprint = "DeleteDmpBlueprint", DeleteDmpBlueprint = "DeleteDmpBlueprint",
//Description //Description
NewDescription = "NewDescription",
BrowseDescription = "BrowseDescription", BrowseDescription = "BrowseDescription",
EditDescription = "EditDescription", EditDescription = "EditDescription",
FinalizeDescription = "FinalizeDescription", FinalizeDescription = "FinalizeDescription",

View File

@ -144,3 +144,8 @@ export interface PublicDescriptionTemplate {
label: string; label: string;
description: string; description: string;
} }
export interface DescriptionSectionPermissionResolver {
dmpId: Guid;
sectionIds: Guid[];
permissions: string[];
}

View File

@ -1,7 +1,7 @@
import { HttpClient, HttpHeaders, HttpParamsOptions, HttpResponse } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParamsOptions, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { Description, DescriptionPersist, DescriptionStatusPersist, PublicDescription } from '@app/core/model/description/description'; import { Description, DescriptionPersist, DescriptionSectionPermissionResolver, DescriptionStatusPersist, PublicDescription } from '@app/core/model/description/description';
import { DescriptionLookup } from '@app/core/query/description.lookup'; import { DescriptionLookup } from '@app/core/query/description.lookup';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
@ -33,6 +33,11 @@ export class DescriptionService {
return this.http.post<QueryResult<Description>>(url, q).pipe(catchError((error: any) => throwError(error))); return this.http.post<QueryResult<Description>>(url, q).pipe(catchError((error: any) => throwError(error)));
} }
getDescriptionSectionPermissions(q: DescriptionSectionPermissionResolver): Observable<Map<Guid, string[]>> {
const url = `${this.apiBase}/get-description-section-permissions`;
return this.http.post<Map<Guid, string[]>>(url, q).pipe(catchError((error: any) => throwError(error)));
}
publicQuery(q: DescriptionLookup): Observable<QueryResult<PublicDescription>> { publicQuery(q: DescriptionLookup): Observable<QueryResult<PublicDescription>> {
const url = `${this.apiBase}/public/query`; const url = `${this.apiBase}/public/query`;
const params = new BaseHttpParams(); const params = new BaseHttpParams();

View File

@ -6,7 +6,7 @@ import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DmpStatus } from '@app/core/common/enum/dmp-status'; import { DmpStatus } from '@app/core/common/enum/dmp-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 { AppPermission } from '@app/core/common/enum/permission.enum';
import { Description, DescriptionPersist, DescriptionStatusPersist } from '@app/core/model/description/description'; import { Description, DescriptionPersist, DescriptionSectionPermissionResolver, DescriptionStatusPersist } from '@app/core/model/description/description';
import { AuthService } from '@app/core/services/auth/auth.service'; import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service'; import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
import { DescriptionService } from '@app/core/services/description/description.service'; import { DescriptionService } from '@app/core/services/description/description.service';
@ -588,18 +588,29 @@ export class DescriptionEditorComponent extends BaseEditor<DescriptionEditorMode
} }
buildForm() { buildForm() {
const canedit = this.isNew ? this.authService.hasPermission(AppPermission.NewDescription) : const descriptionSectionPermissionResolverModel: DescriptionSectionPermissionResolver = {
this.item.authorizationFlags?.some(x => x === AppPermission.EditDescription) || this.item.dmp.authorizationFlags?.some(x => x === AppPermission.EditDmp) ||this.authService.hasPermission(AppPermission.EditDescription); dmpId: this.item.dmp.id,
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !canedit); sectionIds: [this.item.dmpDescriptionTemplate.sectionId],
//this.visibilityRulesService.buildVisibilityRules(this.visibilityRulesService.getVisibilityRulesFromDescriptionTempalte(this.item.descriptionTemplate), this.formGroup); permissions: [AppPermission.EditDescription, AppPermission.DeleteDescription]
// this.selectedSystemFields = this.selectedSystemFieldDisabled();
this.descriptionEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
if (this.editorModel.status == DescriptionStatus.Finalized || this.isDeleted) {
this.formGroup.disable();
} }
this.descriptionService.getDescriptionSectionPermissions(descriptionSectionPermissionResolverModel)
.pipe(takeUntil(this._destroyed)).subscribe(
permissionPerSection => {
const canedit = permissionPerSection && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()] && permissionPerSection[this.item.dmpDescriptionTemplate.sectionId.toString()].some(x => x === 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();
this.descriptionEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
if (this.editorModel.status == DescriptionStatus.Finalized || this.isDeleted) {
this.formGroup.disable();
}
this.registerFormListeners();
},
error => this.onCallbackError(error)
);
this.registerFormListeners();
} }
refreshData(): void { refreshData(): void {

View File

@ -39,10 +39,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

@ -90,12 +90,12 @@
<li *ngFor="let description of descriptionsInSection(section.id); let descriptionIndex = index" (click)="editDescription(description.id, false)" class="active-dataset"> <li *ngFor="let description of descriptionsInSection(section.id); let descriptionIndex = index" (click)="editDescription(description.id, false)" class="active-dataset">
<div class="d-flex flex-direction-row"> <div class="d-flex flex-direction-row">
<div class="label" matTooltip="{{description.label}}">{{'DMP-EDITOR.DESCRIPTION' | translate}}: {{ description.label }}</div> <div class="label" matTooltip="{{description.label}}">{{'DMP-EDITOR.DESCRIPTION' | translate}}: {{ description.label }}</div>
<mat-icon *ngIf="description.status !== descriptionStatusEnum.Finalized" class="ml-2 mr-2 remove-dataset size-16" matTooltip="{{'DMP-EDITOR.ACTIONS.DELETE' | translate}}" (click)="$event.stopPropagation(); removeDescription(description.id)">close</mat-icon> <mat-icon *ngIf="description.status !== descriptionStatusEnum.Finalized && canDeleteSection(section.id)" class="ml-2 mr-2 remove-dataset size-16" matTooltip="{{'DMP-EDITOR.ACTIONS.DELETE' | translate}}" (click)="$event.stopPropagation(); removeDescription(description.id)">close</mat-icon>
<mat-icon *ngIf="description.status === descriptionStatusEnum.Finalized" class="ml-2 mr-2 status-icon check-icon size-16" matTooltip="{{'TYPES.DATASET-STATUS.FINALISED' | translate}}">check</mat-icon> <mat-icon *ngIf="description.status === descriptionStatusEnum.Finalized" class="ml-2 mr-2 status-icon check-icon size-16" matTooltip="{{'TYPES.DATASET-STATUS.FINALISED' | translate}}">check</mat-icon>
</div> </div>
</li> </li>
</ol> </ol>
<ul *ngIf="item.id && section.hasTemplates" class="add-dataset-option"> <ul *ngIf="item.id && section.hasTemplates && canEditSection(section.id)" class="add-dataset-option">
<li> <li>
<a class="add-dataset-action" [routerLink]="['/descriptions/edit/' + item.id + '/' + section.id]"> <a class="add-dataset-action" [routerLink]="['/descriptions/edit/' + item.id + '/' + section.id]">
<mat-icon>add</mat-icon>{{'DMP-EDITOR.ACTIONS.ADD-DESCRIPTION-IN-SECTION' | translate}} <mat-icon>add</mat-icon>{{'DMP-EDITOR.ACTIONS.ADD-DESCRIPTION-IN-SECTION' | translate}}

View File

@ -47,6 +47,8 @@ import { DmpEditorService } from './dmp-editor.service';
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role'; import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
import { DmpUserType } from '@app/core/common/enum/dmp-user-type'; import { DmpUserType } from '@app/core/common/enum/dmp-user-type';
import { DescriptionService } from '@app/core/services/description/description.service'; import { DescriptionService } from '@app/core/services/description/description.service';
import { DescriptionSectionPermissionResolver } from '@app/core/model/description/description';
import { UUID } from 'crypto';
@Component({ @Component({
selector: 'app-dmp-editor', selector: 'app-dmp-editor',
@ -73,6 +75,9 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
dmpUserTypeEnum = DmpUserType; dmpUserTypeEnum = DmpUserType;
dmpUserTypeEnumValues = this.enumUtils.getEnumValues<DmpUserType>(DmpUserType); dmpUserTypeEnumValues = this.enumUtils.getEnumValues<DmpUserType>(DmpUserType);
dmpUserRoleEnumValues = this.enumUtils.getEnumValues<DmpUserRole>(DmpUserRole); dmpUserRoleEnumValues = this.enumUtils.getEnumValues<DmpUserRole>(DmpUserRole);
permissionPerSection: Map<Guid, string[]>;
singleAutocompleteBlueprintConfiguration: SingleAutoCompleteConfiguration = { singleAutocompleteBlueprintConfiguration: SingleAutoCompleteConfiguration = {
initialItems: (data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)), initialItems: (data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)), filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
@ -94,6 +99,19 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
return !this.isDeleted && (this.hasPermission(this.authService.permissionEnum.EditDmp) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditDmp)); return !this.isDeleted && (this.hasPermission(this.authService.permissionEnum.EditDmp) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditDmp));
} }
protected canEditSection(id: Guid): boolean {
return !this.isDeleted && (this.hasPermission(this.authService.permissionEnum.EditDescription) || this.item?.authorizationFlags?.some(x => x === AppPermission.EditDescription) || (
this.permissionPerSection && this.permissionPerSection[id.toString()] && this.permissionPerSection[id.toString()].some(x => x === AppPermission.EditDescription)
));
}
protected canDeleteSection(id: Guid): boolean {
return !this.isDeleted && (this.hasPermission(this.authService.permissionEnum.DeleteDescription) || this.item?.authorizationFlags?.some(x => x === AppPermission.DeleteDescription) || (
this.permissionPerSection && this.permissionPerSection[id.toString()] && this.permissionPerSection[id.toString()].some(x => x === AppPermission.DeleteDescription)
));
}
private hasPermission(permission: AppPermission): boolean { private hasPermission(permission: AppPermission): boolean {
return this.authService.hasPermission(permission) || this.item?.authorizationFlags?.some(x => x === permission) || this.editorModel?.permissions?.includes(permission); return this.authService.hasPermission(permission) || this.item?.authorizationFlags?.some(x => x === permission) || this.editorModel?.permissions?.includes(permission);
@ -190,15 +208,29 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
if (data.descriptions) { if (data.descriptions) {
if (data.status == DmpStatus.Finalized) { if (data.status == DmpStatus.Finalized) {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized); data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else { } else {
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled); data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
} }
} }
this.item = data; this.item = data;
this.selectedBlueprint = data?.blueprint; this.selectedBlueprint = data?.blueprint;
this.isDeleted = data ? data.isActive === IsActive.Inactive : false; this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.buildForm();
const descriptionSectionPermissionResolverModel: DescriptionSectionPermissionResolver = {
dmpId: data.id,
sectionIds: data?.blueprint?.definition?.sections?.map(x => x.id),
permissions: [AppPermission.EditDescription, AppPermission.DeleteDescription]
}
this.descriptionService.getDescriptionSectionPermissions(descriptionSectionPermissionResolverModel)
.pipe(takeUntil(this._destroyed)).subscribe(
complete => {
this.permissionPerSection = complete,
this.buildForm();
},
error => this.onCallbackError(error)
);
} catch (error) { } catch (error) {
this.logger.error('Could not parse Dmp item: ' + data + error); this.logger.error('Could not parse Dmp item: ' + data + error);
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);

View File

@ -42,6 +42,7 @@ export class DmpEditorResolver extends BaseEditorResolver {
[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.authorizationFlags), AppPermission.DeleteDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDescription].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('.'),