dmp authz changes
This commit is contained in:
parent
e9cbf27295
commit
cef1e295f7
|
@ -33,7 +33,7 @@ public class DmpBlueprintValueCensor extends BaseCensor {
|
||||||
if (fields == null || fields.isEmpty())
|
if (fields == null || fields.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.authService.authorizeForce(Permission.BrowseDmp);
|
this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,8 @@ package eu.eudat.model.censorship.dmpproperties;
|
||||||
import eu.eudat.authorization.Permission;
|
import eu.eudat.authorization.Permission;
|
||||||
import eu.eudat.convention.ConventionService;
|
import eu.eudat.convention.ConventionService;
|
||||||
import eu.eudat.model.censorship.BaseCensor;
|
import eu.eudat.model.censorship.BaseCensor;
|
||||||
import eu.eudat.model.censorship.DmpCensor;
|
|
||||||
import eu.eudat.model.censorship.UserCensor;
|
import eu.eudat.model.censorship.UserCensor;
|
||||||
import eu.eudat.model.dmpproperties.DmpContact;
|
import eu.eudat.model.dmpproperties.DmpContact;
|
||||||
import eu.eudat.model.dmpproperties.DmpProperties;
|
|
||||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||||
import gr.cite.tools.data.censor.CensorFactory;
|
import gr.cite.tools.data.censor.CensorFactory;
|
||||||
import gr.cite.tools.fieldset.FieldSet;
|
import gr.cite.tools.fieldset.FieldSet;
|
||||||
|
@ -40,7 +38,7 @@ public class DmpContactCensor extends BaseCensor {
|
||||||
if (fields == null || fields.isEmpty())
|
if (fields == null || fields.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.authService.authorizeForce(Permission.BrowseDmp);
|
this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation);
|
||||||
FieldSet userFields = fields.extractPrefixed(this.asIndexerPrefix(DmpContact._user));
|
FieldSet userFields = fields.extractPrefixed(this.asIndexerPrefix(DmpContact._user));
|
||||||
this.censorFactory.censor(UserCensor.class).censor(userFields, userId);
|
this.censorFactory.censor(UserCensor.class).censor(userFields, userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class DmpPropertiesCensor extends BaseCensor {
|
||||||
if (fields == null || fields.isEmpty())
|
if (fields == null || fields.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.authService.authorizeForce(Permission.BrowseDmp);
|
this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation);
|
||||||
FieldSet dmpBlueprintValuesFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._dmpBlueprintValues));
|
FieldSet dmpBlueprintValuesFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._dmpBlueprintValues));
|
||||||
this.censorFactory.censor(DmpBlueprintValueCensor.class).censor(dmpBlueprintValuesFields, userId);
|
this.censorFactory.censor(DmpBlueprintValueCensor.class).censor(dmpBlueprintValuesFields, userId);
|
||||||
FieldSet contactsFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._contacts));
|
FieldSet contactsFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._contacts));
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class DmpReferenceDataCensor extends BaseCensor {
|
||||||
if (fields == null || fields.isEmpty())
|
if (fields == null || fields.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.authService.authorizeForce(Permission.BrowseDmpDescriptionTemplate);
|
this.authService.authorizeForce(Permission.BrowseDmpDescriptionTemplate, Permission.DeferredAffiliation);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import gr.cite.tools.data.query.QueryContext;
|
||||||
import jakarta.persistence.Tuple;
|
import jakarta.persistence.Tuple;
|
||||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import jakarta.persistence.criteria.Subquery;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -189,7 +190,7 @@ public class EntityDoiQuery extends QueryBase<EntityDoiEntity> {
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
List<Predicate> predicates = new ArrayList<>();
|
||||||
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
|
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
|
||||||
if (userId != null || usePublic) {
|
if (userId != null || usePublic) {
|
||||||
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(EntityDoiEntity._entityId)).value( queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpUserEntity._dmpId)).value(queryUtilsService.buildDmpAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, userId, usePublic))));
|
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(EntityDoiEntity._entityId)).value(queryUtilsService.buildDmpAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, userId, usePublic)));
|
||||||
}
|
}
|
||||||
if (!predicates.isEmpty()) {
|
if (!predicates.isEmpty()) {
|
||||||
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
|
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
|
||||||
|
|
|
@ -150,7 +150,7 @@ public class UserQuery extends QueryBase<UserEntity> {
|
||||||
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUser)) return null;
|
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUser)) return null;
|
||||||
UUID userId;
|
UUID userId;
|
||||||
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
|
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
|
||||||
if (this.authorize.contains(AuthorizationFlags.Public)) userId = this.userScope.getUserIdSafe();
|
if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe();
|
||||||
else userId = null;
|
else userId = null;
|
||||||
|
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
List<Predicate> predicates = new ArrayList<>();
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package eu.eudat.service.dmp;
|
package eu.eudat.service.dmp;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import eu.eudat.data.DmpEntity;
|
|
||||||
import eu.eudat.data.DmpUserEntity;
|
|
||||||
import eu.eudat.model.Dmp;
|
import eu.eudat.model.Dmp;
|
||||||
import eu.eudat.model.DmpUser;
|
import eu.eudat.model.DmpUser;
|
||||||
import eu.eudat.model.persist.*;
|
import eu.eudat.model.persist.*;
|
||||||
|
@ -36,7 +33,7 @@ public interface DmpService {
|
||||||
|
|
||||||
ResponseEntity<byte[]> export(UUID id, String transformerId, String exportType) throws InvalidApplicationException, IOException;
|
ResponseEntity<byte[]> export(UUID id, String transformerId, String exportType) throws InvalidApplicationException, IOException;
|
||||||
|
|
||||||
void inviteUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException;
|
void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException;
|
||||||
|
|
||||||
void dmpInvitationAccept(String token) throws InvalidApplicationException, IOException;
|
void dmpInvitationAccept(String token) throws InvalidApplicationException, IOException;
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,12 @@ public class DmpServiceImpl implements DmpService {
|
||||||
|
|
||||||
this.patchAndSaveTemplates(data.getId(), model.getDescriptionTemplates());
|
this.patchAndSaveTemplates(data.getId(), model.getDescriptionTemplates());
|
||||||
|
|
||||||
|
if (!isUpdate || userScope.isSet()) {
|
||||||
|
this.addOwner(data);
|
||||||
|
if (model.getUsers() == null) model.setUsers(new ArrayList<>());
|
||||||
|
if (model.getUsers().stream().noneMatch(x-> x.getUser() != null && x.getUser().equals(this.userScope.getUserIdSafe()) && DmpUserRole.Owner.equals(x.getRole()))) model.getUsers().add(this.createOwnerPersist());
|
||||||
|
}
|
||||||
|
|
||||||
this.eventBroker.emit(new DmpTouchedEvent(data.getId()));
|
this.eventBroker.emit(new DmpTouchedEvent(data.getId()));
|
||||||
|
|
||||||
this.dmpTouchedIntegrationEventHandler.handle(DmpTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
|
this.dmpTouchedIntegrationEventHandler.handle(DmpTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
|
||||||
|
@ -200,10 +206,7 @@ public class DmpServiceImpl implements DmpService {
|
||||||
this.sendNotification(data);
|
this.sendNotification(data);
|
||||||
|
|
||||||
if (!this.conventionService.isListNullOrEmpty(model.getUsers())){
|
if (!this.conventionService.isListNullOrEmpty(model.getUsers())){
|
||||||
this.inviteUsers(data.getId(), model.getUsers());
|
this.inviteUserOrAssignUsers(data.getId(), model.getUsers());
|
||||||
}else{
|
|
||||||
this.addOwner(data);
|
|
||||||
this.assignUsers(data.getId(), new ArrayList<>(), null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.elasticService.persistDmp(data);
|
this.elasticService.persistDmp(data);
|
||||||
|
@ -211,6 +214,13 @@ public class DmpServiceImpl implements DmpService {
|
||||||
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
|
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DmpUserPersist createOwnerPersist() {
|
||||||
|
DmpUserPersist persist = new DmpUserPersist();
|
||||||
|
persist.setRole(DmpUserRole.Owner);
|
||||||
|
persist.setUser(userScope.getUserIdSafe());
|
||||||
|
return persist;
|
||||||
|
}
|
||||||
|
|
||||||
private void addOwner(DmpEntity dmpEntity) throws InvalidApplicationException {
|
private void addOwner(DmpEntity dmpEntity) throws InvalidApplicationException {
|
||||||
DmpUserEntity data = new DmpUserEntity();
|
DmpUserEntity data = new DmpUserEntity();
|
||||||
data.setId(UUID.randomUUID());
|
data.setId(UUID.randomUUID());
|
||||||
|
@ -286,14 +296,14 @@ public class DmpServiceImpl implements DmpService {
|
||||||
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
|
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
|
||||||
logger.debug("deleting dmp: {}", id);
|
logger.debug("deleting dmp: {}", id);
|
||||||
|
|
||||||
this.authorizationService.authorizeForce(Permission.DeleteDmp);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.DeleteDmp);
|
||||||
|
|
||||||
this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false);
|
this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException {
|
public Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException {
|
||||||
this.authorizationService.authorizeForce(Permission.CreateNewVersionDmp);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation( model.getId())), Permission.CreateNewVersionDmp);
|
||||||
|
|
||||||
DmpEntity oldDmpEntity = this.entityManager.find(DmpEntity.class, model.getId());
|
DmpEntity oldDmpEntity = this.entityManager.find(DmpEntity.class, model.getId());
|
||||||
if (oldDmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (oldDmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
@ -395,7 +405,7 @@ public class DmpServiceImpl implements DmpService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException {
|
public Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException {
|
||||||
this.authorizationService.authorizeForce(Permission.CloneDmp);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation( model.getId())), Permission.CloneDmp);
|
||||||
|
|
||||||
DmpEntity existingDmpEntity = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(model.getId()).firstAs(fields);
|
DmpEntity existingDmpEntity = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(model.getId()).firstAs(fields);
|
||||||
if (!this.conventionService.isValidGuid(model.getId()) || existingDmpEntity == null)
|
if (!this.conventionService.isValidGuid(model.getId()) || existingDmpEntity == null)
|
||||||
|
@ -527,7 +537,7 @@ public class DmpServiceImpl implements DmpService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException {
|
public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException {
|
||||||
this.authorizationService.authorizeForce(Permission.AssignDmpUsers);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getDmpId())), Permission.AssignDmpUsers);
|
||||||
DmpEntity data = this.entityManager.find(DmpEntity.class, model.getDmpId());
|
DmpEntity data = this.entityManager.find(DmpEntity.class, model.getDmpId());
|
||||||
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
|
|
||||||
|
@ -566,7 +576,7 @@ public class DmpServiceImpl implements DmpService {
|
||||||
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||||
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
|
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
|
||||||
if (model.getStatus() != null && model.getStatus() == DmpStatus.Finalized && data.getStatus() != DmpStatus.Finalized) {
|
if (model.getStatus() != null && model.getStatus() == DmpStatus.Finalized && data.getStatus() != DmpStatus.Finalized) {
|
||||||
this.authorizationService.authorizeForce(Permission.FinalizeDmp);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getId())), Permission.FinalizeDmp);
|
||||||
data.setStatus(model.getStatus());
|
data.setStatus(model.getStatus());
|
||||||
data.setFinalizedAt(Instant.now());
|
data.setFinalizedAt(Instant.now());
|
||||||
}
|
}
|
||||||
|
@ -807,9 +817,8 @@ public class DmpServiceImpl implements DmpService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// invites
|
// invites
|
||||||
public void inviteUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
|
public void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
|
||||||
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.InviteDmpUsers);
|
||||||
this.authorizationService.authorizeForce(Permission.InviteDmpUsers);
|
|
||||||
|
|
||||||
DmpEntity dmp = this.queryFactory.query(DmpQuery.class).ids(id).first();
|
DmpEntity dmp = this.queryFactory.query(DmpQuery.class).ids(id).first();
|
||||||
if (dmp == null){
|
if (dmp == null){
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.eudat.service.lock;
|
||||||
|
|
||||||
import eu.eudat.authorization.AuthorizationFlags;
|
import eu.eudat.authorization.AuthorizationFlags;
|
||||||
import eu.eudat.authorization.Permission;
|
import eu.eudat.authorization.Permission;
|
||||||
|
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
|
||||||
import eu.eudat.commons.scope.user.UserScope;
|
import eu.eudat.commons.scope.user.UserScope;
|
||||||
import eu.eudat.convention.ConventionService;
|
import eu.eudat.convention.ConventionService;
|
||||||
import eu.eudat.data.LockEntity;
|
import eu.eudat.data.LockEntity;
|
||||||
|
@ -51,6 +52,7 @@ public class LockServiceImpl implements LockService {
|
||||||
private final ConventionService conventionService;
|
private final ConventionService conventionService;
|
||||||
private final MessageSource messageSource;
|
private final MessageSource messageSource;
|
||||||
private final ErrorThesaurusProperties errors;
|
private final ErrorThesaurusProperties errors;
|
||||||
|
private final AuthorizationContentResolver authorizationContentResolver;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LockServiceImpl(
|
public LockServiceImpl(
|
||||||
|
@ -62,7 +64,7 @@ public class LockServiceImpl implements LockService {
|
||||||
QueryFactory queryFactory,
|
QueryFactory queryFactory,
|
||||||
ConventionService conventionService,
|
ConventionService conventionService,
|
||||||
MessageSource messageSource,
|
MessageSource messageSource,
|
||||||
ErrorThesaurusProperties errors) {
|
ErrorThesaurusProperties errors, AuthorizationContentResolver authorizationContentResolver) {
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
this.userScope = userScope;
|
this.userScope = userScope;
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
|
@ -72,12 +74,13 @@ public class LockServiceImpl implements LockService {
|
||||||
this.conventionService = conventionService;
|
this.conventionService = conventionService;
|
||||||
this.messageSource = messageSource;
|
this.messageSource = messageSource;
|
||||||
this.errors = errors;
|
this.errors = errors;
|
||||||
|
this.authorizationContentResolver = authorizationContentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.authorizeForce(Permission.EditLock);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getTarget())), Permission.EditLock);
|
||||||
|
|
||||||
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
|
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
|
||||||
|
|
||||||
|
@ -176,7 +179,7 @@ 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.authorizeForce(Permission.DeleteLock);
|
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.DeleteLock);
|
||||||
|
|
||||||
this.deleterFactory.deleter(LockDeleter.class).deleteAndSaveByIds(List.of(id));
|
this.deleterFactory.deleter(LockDeleter.class).deleteAndSaveByIds(List.of(id));
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ public class DmpController {
|
||||||
public boolean inviteUsers(@PathVariable("id") UUID id, @RequestBody DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException {
|
public boolean inviteUsers(@PathVariable("id") UUID id, @RequestBody DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException {
|
||||||
logger.debug(new MapLogEntry("inviting users to dmp").And("model", model));
|
logger.debug(new MapLogEntry("inviting users to dmp").And("model", model));
|
||||||
|
|
||||||
this.dmpService.inviteUsers(id, model.getUsers());
|
this.dmpService.inviteUserOrAssignUsers(id, model.getUsers());
|
||||||
|
|
||||||
this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries(
|
this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries(
|
||||||
new AbstractMap.SimpleEntry<String, Object>("model", model)
|
new AbstractMap.SimpleEntry<String, Object>("model", model)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.eudat.controllers;
|
||||||
import eu.eudat.audit.AuditableAction;
|
import eu.eudat.audit.AuditableAction;
|
||||||
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 gr.cite.tools.validation.ValidationFilterAnnotation;
|
import gr.cite.tools.validation.ValidationFilterAnnotation;
|
||||||
import eu.eudat.data.LockEntity;
|
import eu.eudat.data.LockEntity;
|
||||||
import eu.eudat.model.Lock;
|
import eu.eudat.model.Lock;
|
||||||
|
@ -62,14 +63,14 @@ public class LockController {
|
||||||
private final MessageSource messageSource;
|
private final MessageSource messageSource;
|
||||||
|
|
||||||
private final AuthorizationService authService;
|
private final AuthorizationService authService;
|
||||||
|
private final AuthorizationContentResolver authorizationContentResolver;
|
||||||
@Autowired
|
@Autowired
|
||||||
public LockController(BuilderFactory builderFactory,
|
public LockController(BuilderFactory builderFactory,
|
||||||
AuditService auditService,
|
AuditService auditService,
|
||||||
LockService lockService,
|
LockService lockService,
|
||||||
CensorFactory censorFactory,
|
CensorFactory censorFactory,
|
||||||
QueryFactory queryFactory,
|
QueryFactory queryFactory,
|
||||||
MessageSource messageSource, AuthorizationService authService) {
|
MessageSource messageSource, AuthorizationService authService, AuthorizationContentResolver authorizationContentResolver) {
|
||||||
this.builderFactory = builderFactory;
|
this.builderFactory = builderFactory;
|
||||||
this.auditService = auditService;
|
this.auditService = auditService;
|
||||||
this.lockService = lockService;
|
this.lockService = lockService;
|
||||||
|
@ -77,6 +78,7 @@ public class LockController {
|
||||||
this.queryFactory = queryFactory;
|
this.queryFactory = queryFactory;
|
||||||
this.messageSource = messageSource;
|
this.messageSource = messageSource;
|
||||||
this.authService = authService;
|
this.authService = authService;
|
||||||
|
this.authorizationContentResolver = authorizationContentResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("query")
|
@PostMapping("query")
|
||||||
|
@ -154,7 +156,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.authorizeForce(Permission.BrowseLock);
|
this.authService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(targetId)), 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(
|
||||||
|
@ -167,7 +169,7 @@ public class LockController {
|
||||||
@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));
|
logger.debug(new MapLogEntry("unlock" + Lock.class.getSimpleName()).And("targetId", targetId));
|
||||||
this.authService.authorizeForce(Permission.BrowseLock);
|
this.authService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(targetId)), Permission.BrowseLock);
|
||||||
|
|
||||||
this.lockService.unlock(targetId);
|
this.lockService.unlock(targetId);
|
||||||
this.auditService.track(AuditableAction.Lock_UnLocked, Map.ofEntries(
|
this.auditService.track(AuditableAction.Lock_UnLocked, Map.ofEntries(
|
||||||
|
|
|
@ -342,6 +342,12 @@ permissions:
|
||||||
DeleteDmp:
|
DeleteDmp:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
@ -349,6 +355,12 @@ permissions:
|
||||||
CloneDmp:
|
CloneDmp:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
@ -356,6 +368,12 @@ permissions:
|
||||||
CreateNewVersionDmp:
|
CreateNewVersionDmp:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
@ -363,6 +381,12 @@ permissions:
|
||||||
ExportDmp:
|
ExportDmp:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
@ -370,6 +394,12 @@ permissions:
|
||||||
FinalizeDmp:
|
FinalizeDmp:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
@ -390,6 +420,12 @@ permissions:
|
||||||
InviteDmpUsers:
|
InviteDmpUsers:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
@ -709,18 +745,36 @@ permissions:
|
||||||
BrowseLock:
|
BrowseLock:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
allowAuthenticated: false
|
allowAuthenticated: false
|
||||||
EditLock:
|
EditLock:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
allowAuthenticated: false
|
allowAuthenticated: false
|
||||||
DeleteLock:
|
DeleteLock:
|
||||||
roles:
|
roles:
|
||||||
- Admin
|
- Admin
|
||||||
|
dmp:
|
||||||
|
roles:
|
||||||
|
- Owner
|
||||||
|
- User
|
||||||
|
- DescriptionContributor
|
||||||
|
- Reviewer
|
||||||
claims: [ ]
|
claims: [ ]
|
||||||
clients: [ ]
|
clients: [ ]
|
||||||
allowAnonymous: false
|
allowAnonymous: false
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { ReferencePersist } from '../reference/reference';
|
||||||
import { DmpAssociatedUser, User } from "../user/user";
|
import { DmpAssociatedUser, User } from "../user/user";
|
||||||
import { DmpReference } from './dmp-reference';
|
import { DmpReference } from './dmp-reference';
|
||||||
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';
|
||||||
|
|
||||||
export interface Dmp extends BaseEntity {
|
export interface Dmp extends BaseEntity {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
@ -33,6 +34,7 @@ export interface Dmp extends BaseEntity {
|
||||||
descriptions?: Description[];
|
descriptions?: Description[];
|
||||||
dmpDescriptionTemplates?: DmpDescriptionTemplate[];
|
dmpDescriptionTemplates?: DmpDescriptionTemplate[];
|
||||||
entityDois?: EntityDoi[];
|
entityDois?: EntityDoi[];
|
||||||
|
authorizationFlags?: AppPermission[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DmpProperties {
|
export interface DmpProperties {
|
||||||
|
|
|
@ -196,7 +196,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
buildForm() {
|
buildForm() {
|
||||||
const canedit = this.isNew ? this.authService.hasPermission(AppPermission.NewDmp) : this.authService.hasPermission(AppPermission.EditDmp);
|
const canedit = this.isNew ? this.authService.hasPermission(AppPermission.NewDmp) : this.item.authorizationFlags?.some(x => x === AppPermission.EditDmp) || this.authService.hasPermission(AppPermission.EditDmp);
|
||||||
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !canedit);
|
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !canedit);
|
||||||
|
|
||||||
if (this.editorModel.status == DmpStatus.Finalized || this.isDeleted) {
|
if (this.editorModel.status == DmpStatus.Finalized || this.isDeleted) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { AppPermission } from '@app/core/common/enum/permission.enum';
|
||||||
import { Description } from '@app/core/model/description/description';
|
import { Description } from '@app/core/model/description/description';
|
||||||
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, ExtraFieldInSection, FieldInSection, ReferenceTypeFieldInSection, SystemFieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
|
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, ExtraFieldInSection, FieldInSection, ReferenceTypeFieldInSection, SystemFieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
|
||||||
import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties, DmpUser } from '@app/core/model/dmp/dmp';
|
import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties, DmpUser } from '@app/core/model/dmp/dmp';
|
||||||
|
@ -38,6 +39,8 @@ export class DmpEditorResolver extends BaseEditorResolver {
|
||||||
nameof<Dmp>(x => x.publicAfter),
|
nameof<Dmp>(x => x.publicAfter),
|
||||||
nameof<Dmp>(x => x.hash),
|
nameof<Dmp>(x => x.hash),
|
||||||
|
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].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('.'),
|
||||||
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.user), nameof<DmpAssociatedUser>(x => x.id)].join('.'),
|
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.user), nameof<DmpAssociatedUser>(x => x.id)].join('.'),
|
||||||
|
|
|
@ -35,10 +35,11 @@ const routes: Routes = [
|
||||||
data: {
|
data: {
|
||||||
...BreadcrumbService.generateRouteDataConfiguration({
|
...BreadcrumbService.generateRouteDataConfiguration({
|
||||||
title: 'BREADCRUMBS.EDIT-DMP'
|
title: 'BREADCRUMBS.EDIT-DMP'
|
||||||
}),
|
})
|
||||||
authContext: {
|
// ,
|
||||||
permissions: [AppPermission.EditDmp]
|
// authContext: {
|
||||||
}
|
// permissions: [AppPermission.EditDmp]
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -196,39 +196,39 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
||||||
|
|
||||||
isDmpOwner(): boolean {
|
isDmpOwner(): boolean {
|
||||||
const principalId: Guid = this.authentication.userId();
|
const principalId: Guid = this.authentication.userId();
|
||||||
if (principalId) return !!this.dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id));
|
if (principalId) return !!this.dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.user?.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
canEditDmp(): boolean{
|
canEditDmp(): boolean{
|
||||||
return (this.isDraftDmp()) && (this.isDmpOwner() || this.authentication.hasPermission(AppPermission.EditDmp)) && this.isPublicView == false;
|
return (this.isDraftDmp()) && (this.dmp.authorizationFlags?.some(x => x === AppPermission.EditDmp) || this.authentication.hasPermission(AppPermission.EditDmp)) && this.isPublicView == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canCreateNewVersion(): boolean {
|
canCreateNewVersion(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp) && this.isPublicView == false;
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.CreateNewVersionDmp) || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp) && this.isPublicView == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canDeleteDmp(): boolean {
|
canDeleteDmp(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.DeleteDmp) && this.isPublicView == false;
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.DeleteDmp) || this.authentication.hasPermission(AppPermission.DeleteDmp) && this.isPublicView == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canCloneDmp(): boolean {
|
canCloneDmp(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.CloneDmp);
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.CloneDmp) || this.authentication.hasPermission(AppPermission.CloneDmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
canFinalizeDmp(): boolean {
|
canFinalizeDmp(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.FinalizeDmp) && this.isPublicView == false;
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.FinalizeDmp) || this.authentication.hasPermission(AppPermission.FinalizeDmp) && this.isPublicView == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canExportDmp(): boolean {
|
canExportDmp(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.ExportDmp);
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.ExportDmp) || this.authentication.hasPermission(AppPermission.ExportDmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
canInviteDmpUsers(): boolean {
|
canInviteDmpUsers(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.InviteDmpUsers) && this.isPublicView == false;
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.InviteDmpUsers) || this.authentication.hasPermission(AppPermission.InviteDmpUsers) && this.isPublicView == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canAssignDmpUsers(): boolean {
|
canAssignDmpUsers(): boolean {
|
||||||
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.AssignDmpUsers) && this.isPublicView == false;
|
return this.dmp.authorizationFlags?.some(x => x === AppPermission.AssignDmpUsers) || this.authentication.hasPermission(AppPermission.AssignDmpUsers) && this.isPublicView == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
editClicked() {
|
editClicked() {
|
||||||
|
@ -768,6 +768,15 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
||||||
nameof<Dmp>(x => x.groupId),
|
nameof<Dmp>(x => x.groupId),
|
||||||
nameof<Dmp>(x => x.version),
|
nameof<Dmp>(x => x.version),
|
||||||
nameof<Dmp>(x => x.updatedAt),
|
nameof<Dmp>(x => x.updatedAt),
|
||||||
|
nameof<Dmp>(x => x.updatedAt),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CreateNewVersionDmp].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.FinalizeDmp].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.ExportDmp].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.InviteDmpUsers].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.AssignDmpUsers].join('.'),
|
||||||
|
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),
|
||||||
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'),
|
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'),
|
||||||
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'),
|
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'),
|
||||||
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),
|
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),
|
||||||
|
|
Loading…
Reference in New Issue