mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-17 15:03:18 +00:00
[CST-5306] Migrate Researcher Profile (REST)
This commit is contained in:

committed by
eskander

parent
2e4489e4bb
commit
6a1cdd6e2d
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides an exception to be used when a conflict on a resource
|
||||||
|
* occurs.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ResourceConflictException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Object resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ResourceConflictException with a message and the conflicting
|
||||||
|
* resource.
|
||||||
|
*
|
||||||
|
* @param message the error message
|
||||||
|
* @param resource the resource that caused the conflict
|
||||||
|
*/
|
||||||
|
public ResourceConflictException(String message, Object resource) {
|
||||||
|
super(message);
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile;
|
||||||
|
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.MetadataValue;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.dspace.core.Constants.READ;
|
||||||
|
import static org.dspace.eperson.Group.ANONYMOUS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing a Researcher Profile.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ResearcherProfile {
|
||||||
|
|
||||||
|
private final Item item;
|
||||||
|
|
||||||
|
private final MetadataValue dspaceObjectOwner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ResearcherProfile object from the given item.
|
||||||
|
*
|
||||||
|
* @param item the profile item
|
||||||
|
* @throws IllegalArgumentException if the given item has not a dspace.object.owner
|
||||||
|
* metadata with a valid authority
|
||||||
|
*/
|
||||||
|
public ResearcherProfile(Item item) {
|
||||||
|
Assert.notNull(item, "A researcher profile requires an item");
|
||||||
|
this.item = item;
|
||||||
|
this.dspaceObjectOwner = getDspaceObjectOwnerMetadata(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getId() {
|
||||||
|
return UUIDUtils.fromString(dspaceObjectOwner.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return dspaceObjectOwner.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return item.getResourcePolicies().stream()
|
||||||
|
.filter(policy -> policy.getGroup() != null)
|
||||||
|
.anyMatch(policy -> READ == policy.getAction() && ANONYMOUS.equals(policy.getGroup().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item getItem() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Optional<String> getOrcid() {
|
||||||
|
// return getMetadataValue(item, "person.identifier.orcid")
|
||||||
|
// .map(metadataValue -> metadataValue.getValue());
|
||||||
|
// }
|
||||||
|
|
||||||
|
private MetadataValue getDspaceObjectOwnerMetadata(Item item) {
|
||||||
|
return getMetadataValue(item, "dspace.object.owner")
|
||||||
|
.filter(metadata -> UUIDUtils.fromString(metadata.getAuthority()) != null)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("A profile item must have a valid dspace.object.owner metadata"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<MetadataValue> getMetadataValue(Item item, String metadataField) {
|
||||||
|
return getMetadataValues(item, metadataField).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<MetadataValue> getMetadataValues(Item item, String metadataField) {
|
||||||
|
return item.getMetadata().stream()
|
||||||
|
.filter(metadata -> metadataField.equals(metadata.getMetadataField().toString('.')));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,312 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile;
|
||||||
|
|
||||||
|
import static org.dspace.content.authority.Choices.CF_ACCEPTED;
|
||||||
|
import static org.dspace.core.Constants.READ;
|
||||||
|
import static org.dspace.eperson.Group.ANONYMOUS;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.dspace.app.exception.ResourceConflictException;
|
||||||
|
import org.dspace.app.profile.service.AfterResearcherProfileCreationAction;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.authorize.service.AuthorizeService;
|
||||||
|
import org.dspace.content.Collection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.MetadataValue;
|
||||||
|
import org.dspace.content.WorkspaceItem;
|
||||||
|
import org.dspace.content.service.CollectionService;
|
||||||
|
import org.dspace.content.service.InstallItemService;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.content.service.WorkspaceItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.discovery.DiscoverQuery;
|
||||||
|
import org.dspace.discovery.DiscoverResult;
|
||||||
|
import org.dspace.discovery.IndexableObject;
|
||||||
|
import org.dspace.discovery.SearchService;
|
||||||
|
import org.dspace.discovery.SearchServiceException;
|
||||||
|
import org.dspace.discovery.indexobject.IndexableCollection;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.eperson.Group;
|
||||||
|
import org.dspace.eperson.service.GroupService;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link ResearcherProfileService}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ResearcherProfileServiceImpl implements ResearcherProfileService {
|
||||||
|
|
||||||
|
private static Logger log = LoggerFactory.getLogger(ResearcherProfileServiceImpl.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WorkspaceItemService workspaceItemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InstallItemService installItemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CollectionService collectionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SearchService searchService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private GroupService groupService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthorizeService authorizeService;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private List<AfterResearcherProfileCreationAction> afterCreationActions;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
|
||||||
|
if (afterCreationActions == null) {
|
||||||
|
afterCreationActions = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResearcherProfile findById(Context context, UUID id) throws SQLException, AuthorizeException {
|
||||||
|
Assert.notNull(id, "An id must be provided to find a researcher profile");
|
||||||
|
|
||||||
|
Item profileItem = findResearcherProfileItemById(context, id);
|
||||||
|
if (profileItem == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResearcherProfile(profileItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResearcherProfile createAndReturn(Context context, EPerson ePerson)
|
||||||
|
throws AuthorizeException, SQLException, SearchServiceException {
|
||||||
|
|
||||||
|
Item profileItem = findResearcherProfileItemById(context, ePerson.getID());
|
||||||
|
if (profileItem != null) {
|
||||||
|
ResearcherProfile profile = new ResearcherProfile(profileItem);
|
||||||
|
throw new ResourceConflictException("A profile is already linked to the provided User", profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection collection = findProfileCollection(context);
|
||||||
|
if (collection == null) {
|
||||||
|
throw new IllegalStateException("No collection found for researcher profiles");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
Item item = createProfileItem(context, ePerson, collection);
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
|
||||||
|
ResearcherProfile researcherProfile = new ResearcherProfile(item);
|
||||||
|
|
||||||
|
for (AfterResearcherProfileCreationAction afterCreationAction : afterCreationActions) {
|
||||||
|
afterCreationAction.perform(context, researcherProfile, ePerson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return researcherProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteById(Context context, UUID id) throws SQLException, AuthorizeException {
|
||||||
|
Assert.notNull(id, "An id must be provided to find a researcher profile");
|
||||||
|
|
||||||
|
Item profileItem = findResearcherProfileItemById(context, id);
|
||||||
|
if (profileItem == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHardDeleteEnabled()) {
|
||||||
|
deleteItem(context, profileItem);
|
||||||
|
} else {
|
||||||
|
removeDspaceObjectOwnerMetadata(context, profileItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeVisibility(Context context, ResearcherProfile profile, boolean visible)
|
||||||
|
throws AuthorizeException, SQLException {
|
||||||
|
|
||||||
|
if (profile.isVisible() == visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item item = profile.getItem();
|
||||||
|
Group anonymous = groupService.findByName(context, ANONYMOUS);
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
authorizeService.addPolicy(context, item, READ, anonymous);
|
||||||
|
} else {
|
||||||
|
authorizeService.removeGroupPolicies(context, item, anonymous);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResearcherProfile claim(final Context context, final EPerson ePerson, final URI uri)
|
||||||
|
throws SQLException, AuthorizeException, SearchServiceException {
|
||||||
|
Item profileItem = findResearcherProfileItemById(context, ePerson.getID());
|
||||||
|
if (profileItem != null) {
|
||||||
|
ResearcherProfile profile = new ResearcherProfile(profileItem);
|
||||||
|
throw new ResourceConflictException("A profile is already linked to the provided User", profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection collection = findProfileCollection(context);
|
||||||
|
if (collection == null) {
|
||||||
|
throw new IllegalStateException("No collection found for researcher profiles");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String path = uri.getPath();
|
||||||
|
final UUID uuid = UUIDUtils.fromString(path.substring(path.lastIndexOf("/") + 1 ));
|
||||||
|
Item item = itemService.find(context, uuid);
|
||||||
|
if (Objects.isNull(item) || !item.isArchived() || item.isWithdrawn() || notClaimableEntityType(item)) {
|
||||||
|
throw new IllegalArgumentException("Provided uri does not represent a valid Item to be claimed");
|
||||||
|
}
|
||||||
|
final String existingOwner = itemService.getMetadataFirstValue(item, "dspace", "object",
|
||||||
|
"owner", null);
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(existingOwner)) {
|
||||||
|
throw new IllegalArgumentException("Item with provided uri has already an owner");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
itemService.addMetadata(context, item, "dspace", "object", "owner", null, ePerson.getName(),
|
||||||
|
ePerson.getID().toString(), CF_ACCEPTED);
|
||||||
|
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
return new ResearcherProfile(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean notClaimableEntityType(final Item item) {
|
||||||
|
final String entityType = itemService.getEntityType(item);
|
||||||
|
return Arrays.stream(configurationService.getArrayProperty("claimable.entityType"))
|
||||||
|
.noneMatch(entityType::equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item findResearcherProfileItemById(Context context, UUID id) throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
String profileType = getProfileType();
|
||||||
|
|
||||||
|
Iterator<Item> items = itemService.findByAuthorityValue(context, "dspace", "object", "owner", id.toString());
|
||||||
|
while (items.hasNext()) {
|
||||||
|
Item item = items.next();
|
||||||
|
if (hasEntityTypeMetadataEqualsTo(item, profileType)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private Collection findProfileCollection(Context context) throws SQLException, SearchServiceException {
|
||||||
|
UUID uuid = UUIDUtils.fromString(configurationService.getProperty("researcher-profile.collection.uuid"));
|
||||||
|
if (uuid != null) {
|
||||||
|
return collectionService.find(context, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
String profileType = getProfileType();
|
||||||
|
|
||||||
|
DiscoverQuery discoverQuery = new DiscoverQuery();
|
||||||
|
discoverQuery.setDSpaceObjectFilter(IndexableCollection.TYPE);
|
||||||
|
discoverQuery.addFilterQueries("dspace.entity.type:" + profileType);
|
||||||
|
|
||||||
|
DiscoverResult discoverResult = searchService.search(context, discoverQuery);
|
||||||
|
List<IndexableObject> indexableObjects = discoverResult.getIndexableObjects();
|
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(indexableObjects)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indexableObjects.size() > 1) {
|
||||||
|
log.warn("Multiple " + profileType + " type collections were found during profile creation");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Collection) indexableObjects.get(0).getIndexedObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item createProfileItem(Context context, EPerson ePerson, Collection collection)
|
||||||
|
throws AuthorizeException, SQLException {
|
||||||
|
|
||||||
|
String id = ePerson.getID().toString();
|
||||||
|
|
||||||
|
WorkspaceItem workspaceItem = workspaceItemService.create(context, collection, true);
|
||||||
|
Item item = workspaceItem.getItem();
|
||||||
|
itemService.addMetadata(context, item, "dc", "title", null, null, ePerson.getFullName());
|
||||||
|
itemService.addMetadata(context, item, "dspace", "object", "owner", null, ePerson.getFullName(), id, CF_ACCEPTED);
|
||||||
|
|
||||||
|
item = installItemService.installItem(context, workspaceItem);
|
||||||
|
|
||||||
|
Group anonymous = groupService.findByName(context, ANONYMOUS);
|
||||||
|
authorizeService.removeGroupPolicies(context, item, anonymous);
|
||||||
|
authorizeService.addPolicy(context, item, READ, ePerson);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasEntityTypeMetadataEqualsTo(Item item, String entityType) {
|
||||||
|
return item.getMetadata().stream().anyMatch(metadataValue -> {
|
||||||
|
return "dspace.entity.type".equals(metadataValue.getMetadataField().toString('.')) &&
|
||||||
|
entityType.equals(metadataValue.getValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHardDeleteEnabled() {
|
||||||
|
return configurationService.getBooleanProperty("researcher-profile.hard-delete.enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeDspaceObjectOwnerMetadata(Context context, Item profileItem) throws SQLException {
|
||||||
|
List<MetadataValue> metadata = itemService.getMetadata(profileItem, "dspace", "object", "owner", Item.ANY);
|
||||||
|
itemService.removeMetadataValues(context, profileItem, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteItem(Context context, Item profileItem) throws SQLException, AuthorizeException {
|
||||||
|
try {
|
||||||
|
context.turnOffAuthorisationSystem();
|
||||||
|
itemService.delete(context, profileItem);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
context.restoreAuthSystemState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getProfileType() {
|
||||||
|
return configurationService.getProperty("researcher-profile.type", "Person");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile.service;
|
||||||
|
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to mark classes that allow to perform additional logic on created
|
||||||
|
* researcher profile.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface AfterResearcherProfileCreationAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform some actions on the given researcher profile and returns the updated
|
||||||
|
* profile.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
* @param researcherProfile the created researcher profile
|
||||||
|
* @param owner the EPerson that is owner of the given profile
|
||||||
|
* @throws SQLException if a SQL error occurs
|
||||||
|
*/
|
||||||
|
void perform(Context context, ResearcherProfile researcherProfile, EPerson owner) throws SQLException;
|
||||||
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.profile.service;
|
||||||
|
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.discovery.SearchServiceException;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service interface class for the {@link ResearcherProfile} object. The
|
||||||
|
* implementation of this class is responsible for all business logic calls for
|
||||||
|
* the {@link ResearcherProfile} object.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface ResearcherProfileService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the ResearcherProfile by UUID.
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param id the ResearcherProfile id
|
||||||
|
* @return the found ResearcherProfile
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws AuthorizeException
|
||||||
|
*/
|
||||||
|
public ResearcherProfile findById(Context context, UUID id) throws SQLException, AuthorizeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new researcher profile for the given ePerson.
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param ePerson the ePerson
|
||||||
|
* @return the created profile
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws AuthorizeException
|
||||||
|
* @throws SearchServiceException
|
||||||
|
*/
|
||||||
|
public ResearcherProfile createAndReturn(Context context, EPerson ePerson)
|
||||||
|
throws AuthorizeException, SQLException, SearchServiceException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the association between the researcher profile and eperson related to
|
||||||
|
* the input uuid.
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param id the researcher profile id
|
||||||
|
* @throws AuthorizeException
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
public void deleteById(Context context, UUID id) throws SQLException, AuthorizeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the visibility of the given profile using the given new visible value
|
||||||
|
*
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param profile the researcher profile to update
|
||||||
|
* @param visible the visible value to set
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws AuthorizeException
|
||||||
|
*/
|
||||||
|
public void changeVisibility(Context context, ResearcherProfile profile, boolean visible)
|
||||||
|
throws AuthorizeException, SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claims and links an eperson to an existing DSpaceObject
|
||||||
|
* @param context the relevant DSpace Context.
|
||||||
|
* @param ePerson the ePerson
|
||||||
|
* @param uri uri of existing DSpaceObject to be linked to the eperson
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ResearcherProfile claim(Context context, EPerson ePerson, URI uri)
|
||||||
|
throws SQLException, AuthorizeException, SearchServiceException;
|
||||||
|
}
|
@@ -959,4 +959,8 @@ public class AuthorizeServiceImpl implements AuthorizeService {
|
|||||||
return query + " AND ";
|
return query + " AND ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public boolean isPartOfTheGroup(Context c, String egroup) throws SQLException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -592,4 +592,5 @@ public interface AuthorizeService {
|
|||||||
*/
|
*/
|
||||||
long countAdminAuthorizedCollection(Context context, String query)
|
long countAdminAuthorizedCollection(Context context, String query)
|
||||||
throws SearchServiceException, SQLException;
|
throws SearchServiceException, SQLException;
|
||||||
|
public boolean isPartOfTheGroup(Context c, String egroup) throws SQLException;
|
||||||
}
|
}
|
||||||
|
@@ -1131,6 +1131,50 @@ prevent the generation of resource policy entry values with null dspace_object a
|
|||||||
return !(hasCustomPolicy && isAnonimousGroup && datesAreNull);
|
return !(hasCustomPolicy && isAnonimousGroup && datesAreNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator of Items possessing the passed metadata field, or only
|
||||||
|
* those matching the passed value, if value is not Item.ANY
|
||||||
|
*
|
||||||
|
* @param context DSpace context object
|
||||||
|
* @param schema metadata field schema
|
||||||
|
* @param element metadata field element
|
||||||
|
* @param qualifier metadata field qualifier
|
||||||
|
* @param value field value or Item.ANY to match any value
|
||||||
|
* @return an iterator over the items matching that authority value
|
||||||
|
* @throws SQLException if database error
|
||||||
|
* An exception that provides information on a database access error or other errors.
|
||||||
|
* @throws AuthorizeException if authorization error
|
||||||
|
* Exception indicating the current user of the context does not have permission
|
||||||
|
* to perform a particular action.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Item> findArchivedByMetadataField(Context context,
|
||||||
|
String schema, String element, String qualifier, String value)
|
||||||
|
throws SQLException, AuthorizeException {
|
||||||
|
MetadataSchema mds = metadataSchemaService.find(context, schema);
|
||||||
|
if (mds == null) {
|
||||||
|
throw new IllegalArgumentException("No such metadata schema: " + schema);
|
||||||
|
}
|
||||||
|
MetadataField mdf = metadataFieldService.findByElement(context, mds, element, qualifier);
|
||||||
|
if (mdf == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"No such metadata field: schema=" + schema + ", element=" + element + ", qualifier=" + qualifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Item.ANY.equals(value)) {
|
||||||
|
return itemDAO.findByMetadataField(context, mdf, null, true);
|
||||||
|
} else {
|
||||||
|
return itemDAO.findByMetadataField(context, mdf, value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Item> findArchivedByMetadataField(Context context, String metadataField, String value)
|
||||||
|
throws SQLException, AuthorizeException {
|
||||||
|
String[] mdValueByField = getMDValueByField(metadataField);
|
||||||
|
return findArchivedByMetadataField(context, mdValueByField[0], mdValueByField[1], mdValueByField[2], value);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator of Items possessing the passed metadata field, or only
|
* Returns an iterator of Items possessing the passed metadata field, or only
|
||||||
* those matching the passed value, if value is not Item.ANY
|
* those matching the passed value, if value is not Item.ANY
|
||||||
@@ -1535,5 +1579,9 @@ prevent the generation of resource policy entry values with null dspace_object a
|
|||||||
.stream().findFirst().orElse(null);
|
.stream().findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEntityType(Item item) {
|
||||||
|
return getMetadataFirstValue(item, new MetadataFieldName("dspace.entity.type"), Item.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.content.authority;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.eperson.factory.EPersonServiceFactory;
|
||||||
|
import org.dspace.eperson.service.EPersonService;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Mykhaylo Boychuk (4science.it)
|
||||||
|
*/
|
||||||
|
public class EPersonAuthority implements ChoiceAuthority {
|
||||||
|
private static final Logger log = Logger.getLogger(EPersonAuthority.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the name assigned to the specific instance by the PluginService, @see
|
||||||
|
* {@link NameAwarePlugin}
|
||||||
|
**/
|
||||||
|
private String authorityName;
|
||||||
|
|
||||||
|
private EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Choices getBestMatch(String text, String locale) {
|
||||||
|
return getMatches(text, 0, 2, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Choices getMatches(String text, int start, int limit, String locale) {
|
||||||
|
Context context = null;
|
||||||
|
if (limit <= 0) {
|
||||||
|
limit = 20;
|
||||||
|
}
|
||||||
|
context = new Context();
|
||||||
|
List<EPerson> ePersons = null;
|
||||||
|
try {
|
||||||
|
ePersons = ePersonService.search(context, text, start, limit);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
List<Choice> choiceList = new ArrayList<Choice>();
|
||||||
|
for (EPerson eperson : ePersons) {
|
||||||
|
choiceList.add(new Choice(eperson.getID().toString(), eperson.getFullName(), eperson.getFullName()));
|
||||||
|
}
|
||||||
|
Choice[] results = new Choice[choiceList.size()];
|
||||||
|
results = choiceList.toArray(results);
|
||||||
|
return new Choices(results, start, ePersons.size(), Choices.CF_AMBIGUOUS, ePersons.size() > (start + limit), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabel(String key, String locale) {
|
||||||
|
|
||||||
|
UUID uuid = UUIDUtils.fromString(key);
|
||||||
|
if (uuid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context = new Context();
|
||||||
|
try {
|
||||||
|
EPerson ePerson = ePersonService.find(context, uuid);
|
||||||
|
return ePerson != null ? ePerson.getFullName() : null;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPluginInstanceName() {
|
||||||
|
return authorityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPluginInstanceName(String name) {
|
||||||
|
this.authorityName = name;
|
||||||
|
}
|
||||||
|
}
|
@@ -579,6 +579,36 @@ public interface ItemService
|
|||||||
*/
|
*/
|
||||||
public boolean canCreateNewVersion(Context context, Item item) throws SQLException;
|
public boolean canCreateNewVersion(Context context, Item item) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator of in archive items possessing the passed metadata field, or only
|
||||||
|
* those matching the passed value, if value is not Item.ANY
|
||||||
|
*
|
||||||
|
* @param context DSpace context object
|
||||||
|
* @param schema metadata field schema
|
||||||
|
* @param element metadata field element
|
||||||
|
* @param qualifier metadata field qualifier
|
||||||
|
* @param value field value or Item.ANY to match any value
|
||||||
|
* @return an iterator over the items matching that authority value
|
||||||
|
* @throws SQLException if database error
|
||||||
|
* @throws AuthorizeException if authorization error
|
||||||
|
*/
|
||||||
|
public Iterator<Item> findArchivedByMetadataField(Context context, String schema, String element,
|
||||||
|
String qualifier, String value) throws SQLException, AuthorizeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator of in archive items possessing the passed metadata field, or only
|
||||||
|
* those matching the passed value, if value is not Item.ANY
|
||||||
|
*
|
||||||
|
* @param context DSpace context object
|
||||||
|
* @param metadataField metadata
|
||||||
|
* @param value field value or Item.ANY to match any value
|
||||||
|
* @return an iterator over the items matching that authority value
|
||||||
|
* @throws SQLException if database error
|
||||||
|
* @throws AuthorizeException if authorization error
|
||||||
|
*/
|
||||||
|
public Iterator<Item> findArchivedByMetadataField(Context context, String metadataField, String value)
|
||||||
|
throws SQLException, AuthorizeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator of Items possessing the passed metadata field, or only
|
* Returns an iterator of Items possessing the passed metadata field, or only
|
||||||
* those matching the passed value, if value is not Item.ANY
|
* those matching the passed value, if value is not Item.ANY
|
||||||
@@ -618,7 +648,7 @@ public interface ItemService
|
|||||||
*/
|
*/
|
||||||
public Iterator<Item> findByAuthorityValue(Context context,
|
public Iterator<Item> findByAuthorityValue(Context context,
|
||||||
String schema, String element, String qualifier, String value)
|
String schema, String element, String qualifier, String value)
|
||||||
throws SQLException, AuthorizeException, IOException;
|
throws SQLException, AuthorizeException;
|
||||||
|
|
||||||
|
|
||||||
public Iterator<Item> findByMetadataFieldAuthority(Context context, String mdString, String authority)
|
public Iterator<Item> findByMetadataFieldAuthority(Context context, String mdString, String authority)
|
||||||
@@ -782,5 +812,12 @@ public interface ItemService
|
|||||||
*/
|
*/
|
||||||
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier,
|
public List<MetadataValue> getMetadata(Item item, String schema, String element, String qualifier,
|
||||||
String lang, boolean enableVirtualMetadata);
|
String lang, boolean enableVirtualMetadata);
|
||||||
|
/**
|
||||||
|
* Returns the item's entity type, if any.
|
||||||
|
*
|
||||||
|
* @param item the item
|
||||||
|
* @return the entity type as string, if any
|
||||||
|
*/
|
||||||
|
public String getEntityType(Item item);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
# For example, including "dspace.dir" in this local.cfg will override the
|
# For example, including "dspace.dir" in this local.cfg will override the
|
||||||
# default value of "dspace.dir" in the dspace.cfg file.
|
# default value of "dspace.dir" in the dspace.cfg file.
|
||||||
#
|
#
|
||||||
|
researcher-profile.type = Person
|
||||||
##########################
|
##########################
|
||||||
# SERVER CONFIGURATION #
|
# SERVER CONFIGURATION #
|
||||||
##########################
|
##########################
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.matcher;
|
||||||
|
|
||||||
|
import org.hamcrest.BaseMatcher;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matcher based on an {@link Predicate}.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
* @param <T> the type of the instance to match
|
||||||
|
*/
|
||||||
|
public class LambdaMatcher<T> extends BaseMatcher<T> {
|
||||||
|
|
||||||
|
private final Predicate<T> matcher;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
public static <T> LambdaMatcher<T> matches(Predicate<T> matcher) {
|
||||||
|
return new LambdaMatcher<T>(matcher, "Matches the given predicate");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> LambdaMatcher<T> matches(Predicate<T> matcher, String description) {
|
||||||
|
return new LambdaMatcher<T>(matcher, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Matcher<Iterable<? super T>> has(Predicate<T> matcher) {
|
||||||
|
return Matchers.hasItem(matches(matcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
private LambdaMatcher(Predicate<T> matcher, String description) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public boolean matches(Object argument) {
|
||||||
|
return matcher.test((T) argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText(this.description);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.matcher;
|
||||||
|
|
||||||
|
import org.dspace.content.MetadataValue;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link org.hamcrest.Matcher} to match a MetadataValue by
|
||||||
|
* all its attributes.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MetadataValueMatcher extends TypeSafeMatcher<MetadataValue> {
|
||||||
|
|
||||||
|
private String field;
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private String language;
|
||||||
|
|
||||||
|
private String authority;
|
||||||
|
|
||||||
|
private Integer place;
|
||||||
|
|
||||||
|
private Integer confidence;
|
||||||
|
|
||||||
|
private MetadataValueMatcher(String field, String value, String language, String authority, Integer place,
|
||||||
|
Integer confidence) {
|
||||||
|
|
||||||
|
this.field = field;
|
||||||
|
this.value = value;
|
||||||
|
this.language = language;
|
||||||
|
this.authority = authority;
|
||||||
|
this.place = place;
|
||||||
|
this.confidence = confidence;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("MetadataValue with the following attributes [field=" + field + ", value="
|
||||||
|
+ value + ", language=" + language + ", authority=" + authority + ", place=" + place + ", confidence="
|
||||||
|
+ confidence + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void describeMismatchSafely(MetadataValue item, Description mismatchDescription) {
|
||||||
|
mismatchDescription.appendText("was ")
|
||||||
|
.appendValue("MetadataValue [metadataField=").appendValue(item.getMetadataField().toString('.'))
|
||||||
|
.appendValue(", value=").appendValue(item.getValue()).appendValue(", language=").appendValue(language)
|
||||||
|
.appendValue(", place=").appendValue(item.getPlace()).appendValue(", authority=")
|
||||||
|
.appendValue(item.getAuthority()).appendValue(", confidence=").appendValue(item.getConfidence() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean matchesSafely(MetadataValue metadataValue) {
|
||||||
|
return Objects.equals(metadataValue.getValue(), value) &&
|
||||||
|
Objects.equals(metadataValue.getMetadataField().toString('.'), field) &&
|
||||||
|
Objects.equals(metadataValue.getLanguage(), language) &&
|
||||||
|
Objects.equals(metadataValue.getAuthority(), authority) &&
|
||||||
|
Objects.equals(metadataValue.getPlace(), place) &&
|
||||||
|
Objects.equals(metadataValue.getConfidence(), confidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetadataValueMatcher with(String field, String value, String language,
|
||||||
|
String authority, Integer place, Integer confidence) {
|
||||||
|
return new MetadataValueMatcher(field, value, language, authority, place, confidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetadataValueMatcher with(String field, String value) {
|
||||||
|
return with(field, value, null, null, 0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetadataValueMatcher with(String field, String value, String authority, int place, int confidence) {
|
||||||
|
return with(field, value, null, authority, place, confidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetadataValueMatcher with(String field, String value, String authority, int confidence) {
|
||||||
|
return with(field, value, null, authority, 0, confidence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MetadataValueMatcher with(String field, String value, int place) {
|
||||||
|
return with(field, value, null, null, place, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -22,6 +22,9 @@ import org.dspace.core.Context;
|
|||||||
import org.dspace.eperson.EPerson;
|
import org.dspace.eperson.EPerson;
|
||||||
import org.dspace.eperson.Group;
|
import org.dspace.eperson.Group;
|
||||||
|
|
||||||
|
import static org.dspace.content.MetadataSchemaEnum.DC;
|
||||||
|
import static org.dspace.content.authority.Choices.CF_ACCEPTED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder to construct Item objects
|
* Builder to construct Item objects
|
||||||
*
|
*
|
||||||
@@ -73,6 +76,11 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder<Item> {
|
|||||||
public ItemBuilder withAuthor(final String authorName) {
|
public ItemBuilder withAuthor(final String authorName) {
|
||||||
return addMetadataValue(item, MetadataSchemaEnum.DC.getName(), "contributor", "author", authorName);
|
return addMetadataValue(item, MetadataSchemaEnum.DC.getName(), "contributor", "author", authorName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ItemBuilder withAuthor(final String authorName, final String authority) {
|
||||||
|
return addMetadataValue(item, DC.getName(), "contributor", "author", null, authorName, authority, 600);
|
||||||
|
}
|
||||||
|
|
||||||
public ItemBuilder withAuthor(final String authorName, final String authority, final int confidence) {
|
public ItemBuilder withAuthor(final String authorName, final String authority, final int confidence) {
|
||||||
return addMetadataValue(item, MetadataSchemaEnum.DC.getName(), "contributor", "author",
|
return addMetadataValue(item, MetadataSchemaEnum.DC.getName(), "contributor", "author",
|
||||||
null, authorName, authority, confidence);
|
null, authorName, authority, confidence);
|
||||||
@@ -144,6 +152,13 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder<Item> {
|
|||||||
return addMetadataValue(item, schema, element, qualifier, value);
|
return addMetadataValue(item, schema, element, qualifier, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ItemBuilder withDspaceObjectOwner(String value, String authority) {
|
||||||
|
return addMetadataValue(item, "dspace", "object", "owner", null, value, authority, CF_ACCEPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemBuilder withDspaceObjectOwner(EPerson ePerson) {
|
||||||
|
return withDspaceObjectOwner(ePerson.getFullName(), ePerson.getID().toString());
|
||||||
|
}
|
||||||
public ItemBuilder makeUnDiscoverable() {
|
public ItemBuilder makeUnDiscoverable() {
|
||||||
item.setDiscoverable(false);
|
item.setDiscoverable(false);
|
||||||
return this;
|
return this;
|
||||||
@@ -181,6 +196,9 @@ public class ItemBuilder extends AbstractDSpaceObjectBuilder<Item> {
|
|||||||
return setAdminPermission(item, ePerson, null);
|
return setAdminPermission(item, ePerson, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ItemBuilder withPersonEmail(String email) {
|
||||||
|
return addMetadataValue(item, "person", "email", null, email);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Item build() {
|
public Item build() {
|
||||||
|
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.converter;
|
||||||
|
|
||||||
|
//import static org.dspace.app.orcid.model.OrcidEntityType.FUNDING;
|
||||||
|
//import static org.dspace.app.orcid.model.OrcidEntityType.PUBLICATION;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
//import org.dspace.app.orcid.service.OrcidSynchronizationService;
|
||||||
|
//import org.dspace.app.profile.OrcidEntitySyncPreference;
|
||||||
|
//import org.dspace.app.profile.OrcidProfileSyncPreference;
|
||||||
|
//import org.dspace.app.profile.OrcidSynchronizationMode;
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
//import org.dspace.app.rest.model.ResearcherProfileRest.OrcidSynchronizationRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This converter is responsible for transforming an model that represent a
|
||||||
|
* ResearcherProfile to the REST representation of an ResearcherProfile.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ResearcherProfileConverter implements DSpaceConverter<ResearcherProfile, ResearcherProfileRest> {
|
||||||
|
|
||||||
|
// @Autowired
|
||||||
|
// private OrcidSynchronizationService orcidSynchronizationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResearcherProfileRest convert(ResearcherProfile profile, Projection projection) {
|
||||||
|
ResearcherProfileRest researcherProfileRest = new ResearcherProfileRest();
|
||||||
|
|
||||||
|
researcherProfileRest.setVisible(profile.isVisible());
|
||||||
|
researcherProfileRest.setId(profile.getId());
|
||||||
|
researcherProfileRest.setProjection(projection);
|
||||||
|
|
||||||
|
Item item = profile.getItem();
|
||||||
|
|
||||||
|
// if (orcidSynchronizationService.isLinkedToOrcid(item)) {
|
||||||
|
// profile.getOrcid().ifPresent(researcherProfileRest::setOrcid);
|
||||||
|
//
|
||||||
|
// OrcidSynchronizationRest orcidSynchronization = new OrcidSynchronizationRest();
|
||||||
|
// orcidSynchronization.setMode(getMode(item));
|
||||||
|
// orcidSynchronization.setProfilePreferences(getProfilePreferences(item));
|
||||||
|
// orcidSynchronization.setFundingsPreference(getFundingsPreference(item));
|
||||||
|
// orcidSynchronization.setPublicationsPreference(getPublicationsPreference(item));
|
||||||
|
// researcherProfileRest.setOrcidSynchronization(orcidSynchronization);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return researcherProfileRest;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private String getPublicationsPreference(Item item) {
|
||||||
|
// return orcidSynchronizationService.getEntityPreference(item, PUBLICATION)
|
||||||
|
// .map(OrcidEntitySyncPreference::name)
|
||||||
|
// .orElse(OrcidEntitySyncPreference.DISABLED.name());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String getFundingsPreference(Item item) {
|
||||||
|
// return orcidSynchronizationService.getEntityPreference(item, FUNDING)
|
||||||
|
// .map(OrcidEntitySyncPreference::name)
|
||||||
|
// .orElse(OrcidEntitySyncPreference.DISABLED.name());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private List<String> getProfilePreferences(Item item) {
|
||||||
|
// return orcidSynchronizationService.getProfilePreferences(item).stream()
|
||||||
|
// .map(OrcidProfileSyncPreference::name)
|
||||||
|
// .collect(Collectors.toList());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private String getMode(Item item) {
|
||||||
|
// return orcidSynchronizationService.getSynchronizationMode(item)
|
||||||
|
// .map(OrcidSynchronizationMode::name)
|
||||||
|
// .orElse(OrcidSynchronizationMode.MANUAL.name());
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ResearcherProfile> getModelClass() {
|
||||||
|
return ResearcherProfile.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,11 +20,16 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.dspace.app.exception.ResourceConflictException;
|
||||||
|
import org.dspace.app.rest.converter.ConverterService;
|
||||||
|
import org.dspace.app.rest.model.RestModel;
|
||||||
import org.dspace.app.rest.utils.ContextUtil;
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
|
import org.dspace.app.rest.utils.Utils;
|
||||||
import org.dspace.authorize.AuthorizeException;
|
import org.dspace.authorize.AuthorizeException;
|
||||||
import org.dspace.core.Context;
|
import org.dspace.core.Context;
|
||||||
import org.dspace.services.ConfigurationService;
|
import org.dspace.services.ConfigurationService;
|
||||||
import org.springframework.beans.TypeMismatchException;
|
import org.springframework.beans.TypeMismatchException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.data.repository.support.QueryMethodParameterConversionException;
|
import org.springframework.data.repository.support.QueryMethodParameterConversionException;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@@ -67,6 +72,12 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
|
|||||||
@Inject
|
@Inject
|
||||||
private ConfigurationService configurationService;
|
private ConfigurationService configurationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ConverterService converterService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Utils utils;
|
||||||
|
|
||||||
@ExceptionHandler({AuthorizeException.class, RESTAuthorizationException.class, AccessDeniedException.class})
|
@ExceptionHandler({AuthorizeException.class, RESTAuthorizationException.class, AccessDeniedException.class})
|
||||||
protected void handleAuthorizeException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
protected void handleAuthorizeException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -166,6 +177,12 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH
|
|||||||
HttpStatus.BAD_REQUEST.value());
|
HttpStatus.BAD_REQUEST.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(ResourceConflictException.class)
|
||||||
|
protected ResponseEntity<? extends RestModel> resourceConflictException(ResourceConflictException ex) {
|
||||||
|
RestModel resource = converterService.toRest(ex.getResource(), utils.obtainProjection());
|
||||||
|
return new ResponseEntity<RestModel>(resource, HttpStatus.CONFLICT);
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(MissingParameterException.class)
|
@ExceptionHandler(MissingParameterException.class)
|
||||||
protected void MissingParameterException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
protected void MissingParameterException(HttpServletRequest request, HttpServletResponse response, Exception ex)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.login;
|
||||||
|
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for classes that need to perform some operations after the user
|
||||||
|
* login.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface PostLoggedInAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform some operations after the user login.
|
||||||
|
*
|
||||||
|
* @param context the DSpace context
|
||||||
|
*/
|
||||||
|
public void loggedIn(Context context);
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.login.impl;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.login.PostLoggedInAction;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.services.EventService;
|
||||||
|
import org.dspace.usage.UsageEvent;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link PostLoggedInAction} that fire an LOGIN event.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class LoginEventFireAction implements PostLoggedInAction {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EventService eventService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loggedIn(Context context) {
|
||||||
|
|
||||||
|
HttpServletRequest request = getCurrentRequest();
|
||||||
|
EPerson currentUser = context.getCurrentUser();
|
||||||
|
|
||||||
|
eventService.fireEvent(new UsageEvent(UsageEvent.Action.LOGIN, request, context, currentUser));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpServletRequest getCurrentRequest() {
|
||||||
|
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.login.impl;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.app.rest.login.PostLoggedInAction;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Item;
|
||||||
|
import org.dspace.content.MetadataFieldName;
|
||||||
|
import org.dspace.content.service.ItemService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.eperson.service.EPersonService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.commons.collections4.IteratorUtils.toList;
|
||||||
|
import static org.dspace.content.authority.Choices.CF_ACCEPTED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link PostLoggedInAction} that perform an automatic claim
|
||||||
|
* between the logged eperson and possible profiles without eperson present in
|
||||||
|
* the system. This pairing between eperson and profile is done starting from
|
||||||
|
* the configured metadata of the logged in user.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ResearcherProfileAutomaticClaim implements PostLoggedInAction {
|
||||||
|
|
||||||
|
private final static Logger LOGGER = LoggerFactory.getLogger(ResearcherProfileAutomaticClaim.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResearcherProfileService researcherProfileService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ItemService itemService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EPersonService ePersonService;
|
||||||
|
|
||||||
|
private final String ePersonField;
|
||||||
|
|
||||||
|
private final String profileFiled;
|
||||||
|
|
||||||
|
public ResearcherProfileAutomaticClaim(String ePersonField, String profileField) {
|
||||||
|
Assert.notNull(ePersonField, "An eperson field is required to perform automatic claim");
|
||||||
|
Assert.notNull(profileField, "An profile field is required to perform automatic claim");
|
||||||
|
this.ePersonField = ePersonField;
|
||||||
|
this.profileFiled = profileField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loggedIn(Context context) {
|
||||||
|
|
||||||
|
EPerson currentUser = context.getCurrentUser();
|
||||||
|
if (currentUser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
claimProfile(context, currentUser);
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
LOGGER.error("An error occurs during the profile claim by email", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void claimProfile(Context context, EPerson currentUser) throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
UUID id = currentUser.getID();
|
||||||
|
String fullName = currentUser.getFullName();
|
||||||
|
|
||||||
|
if (currentUserHasAlreadyResearcherProfile(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item item = findClaimableItem(context, currentUser);
|
||||||
|
if (item != null) {
|
||||||
|
itemService.addMetadata(context, item, "dspace", "object", "owner", null, fullName, id.toString(), CF_ACCEPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean currentUserHasAlreadyResearcherProfile(Context context) throws SQLException, AuthorizeException {
|
||||||
|
return researcherProfileService.findById(context, context.getCurrentUser().getID()) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item findClaimableItem(Context context, EPerson currentUser)
|
||||||
|
throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
String value = getValueToSearchFor(context, currentUser);
|
||||||
|
if (StringUtils.isEmpty(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Item> items = toList(itemService.findArchivedByMetadataField(context, profileFiled, value)).stream()
|
||||||
|
.filter(this::hasNotCrisOwner)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return items.size() == 1 ? items.get(0) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getValueToSearchFor(Context context, EPerson currentUser) {
|
||||||
|
if ("email".equals(ePersonField)) {
|
||||||
|
return currentUser.getEmail();
|
||||||
|
}
|
||||||
|
return ePersonService.getMetadataFirstValue(currentUser, new MetadataFieldName(ePersonField), Item.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasNotCrisOwner(Item item) {
|
||||||
|
return CollectionUtils.isEmpty(itemService.getMetadata(item, "dspace", "object", "owner", Item.ANY));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||||
|
import org.dspace.app.rest.RestResourceController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Researcher Profile REST resource.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@LinksRest(links = {
|
||||||
|
@LinkRest(name = ResearcherProfileRest.ITEM, method = "getItem"),
|
||||||
|
@LinkRest(name = ResearcherProfileRest.EPERSON, method = "getEPerson")
|
||||||
|
})
|
||||||
|
public class ResearcherProfileRest extends BaseObjectRest<UUID> {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
// changed from RestModel.CRIS to RestModel.EPERSON
|
||||||
|
public static final String CATEGORY = RestModel.EPERSON;
|
||||||
|
public static final String NAME = "profile";
|
||||||
|
|
||||||
|
public static final String ITEM = "item";
|
||||||
|
public static final String EPERSON = "eperson";
|
||||||
|
|
||||||
|
private boolean visible;
|
||||||
|
|
||||||
|
// @JsonInclude(Include.NON_NULL)
|
||||||
|
// private String orcid;
|
||||||
|
|
||||||
|
// @JsonInclude(Include.NON_NULL)
|
||||||
|
// private OrcidSynchronizationRest orcidSynchronization;
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisible(boolean visible) {
|
||||||
|
this.visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public OrcidSynchronizationRest getOrcidSynchronization() {
|
||||||
|
// return orcidSynchronization;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public void setOrcidSynchronization(OrcidSynchronizationRest orcidSynchronization) {
|
||||||
|
// this.orcidSynchronization = orcidSynchronization;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public String getOrcid() {
|
||||||
|
// return orcid;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setOrcid(String orcid) {
|
||||||
|
// this.orcid = orcid;
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCategory() {
|
||||||
|
return CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getController() {
|
||||||
|
return RestResourceController.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class to model ORCID synchronization preferences and mode.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
// public static class OrcidSynchronizationRest {
|
||||||
|
//
|
||||||
|
// private String mode;
|
||||||
|
//
|
||||||
|
// private String publicationsPreference;
|
||||||
|
//
|
||||||
|
// private String fundingsPreference;
|
||||||
|
//
|
||||||
|
// private List<String> profilePreferences;
|
||||||
|
//
|
||||||
|
// public String getMode() {
|
||||||
|
// return mode;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setMode(String mode) {
|
||||||
|
// this.mode = mode;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public List<String> getProfilePreferences() {
|
||||||
|
// return profilePreferences;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setProfilePreferences(List<String> profilePreferences) {
|
||||||
|
// this.profilePreferences = profilePreferences;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public String getPublicationsPreference() {
|
||||||
|
// return publicationsPreference;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setPublicationsPreference(String publicationsPreference) {
|
||||||
|
// this.publicationsPreference = publicationsPreference;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public String getFundingsPreference() {
|
||||||
|
// return fundingsPreference;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void setFundingsPreference(String fundingsPreference) {
|
||||||
|
// this.fundingsPreference = fundingsPreference;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.model.hateoas;
|
||||||
|
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource;
|
||||||
|
import org.dspace.app.rest.utils.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class serves as a wrapper class to wrap the SearchConfigurationRest into
|
||||||
|
* a HAL resource.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@RelNameDSpaceResource(ResearcherProfileRest.NAME)
|
||||||
|
public class ResearcherProfileResource extends DSpaceResource<ResearcherProfileRest> {
|
||||||
|
|
||||||
|
public ResearcherProfileResource(ResearcherProfileRest data, Utils utils) {
|
||||||
|
super(data, utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.app.rest.model.EPersonRest;
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.eperson.service.EPersonService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link repository for "ePerson" subresource of an individual researcher
|
||||||
|
* profile.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(ResearcherProfileRest.CATEGORY + "." + ResearcherProfileRest.NAME + "." + ResearcherProfileRest.EPERSON)
|
||||||
|
public class ResearcherProfileEPersonLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EPersonService ePersonService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResearcherProfileService researcherProfileService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ePerson related to the Research profile with the given UUID.
|
||||||
|
*
|
||||||
|
* @param request the http servlet request
|
||||||
|
* @param id the profile UUID
|
||||||
|
* @param pageable the optional pageable
|
||||||
|
* @param projection the projection object
|
||||||
|
* @return the ePerson rest representation
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasPermission(#id, 'PROFILE', 'READ')")
|
||||||
|
public EPersonRest getEPerson(@Nullable HttpServletRequest request, UUID id,
|
||||||
|
@Nullable Pageable pageable, Projection projection) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Context context = obtainContext();
|
||||||
|
|
||||||
|
ResearcherProfile profile = researcherProfileService.findById(context, id);
|
||||||
|
if (profile == null) {
|
||||||
|
throw new ResourceNotFoundException("No such profile with UUID: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
EPerson ePerson = ePersonService.find(context, id);
|
||||||
|
if (ePerson == null) {
|
||||||
|
throw new ResourceNotFoundException("No such eperson related to a profile with EPerson UUID: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return converter.toRest(ePerson, projection);
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.app.rest.model.ItemRest;
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
import org.dspace.app.rest.projection.Projection;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link repository for "item" subresource of an individual researcher profile.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(ResearcherProfileRest.CATEGORY + "." + ResearcherProfileRest.NAME + "." + ResearcherProfileRest.ITEM)
|
||||||
|
public class ResearcherProfileItemLinkRepository extends AbstractDSpaceRestRepository implements LinkRestRepository {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResearcherProfileService researcherProfileService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item related to the Research profile with the given UUID.
|
||||||
|
*
|
||||||
|
* @param request the http servlet request
|
||||||
|
* @param id the profile UUID
|
||||||
|
* @param pageable the optional pageable
|
||||||
|
* @param projection the projection object
|
||||||
|
* @return the item rest representation
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasPermission(#id, 'PROFILE', 'READ')")
|
||||||
|
public ItemRest getItem(@Nullable HttpServletRequest request, UUID id,
|
||||||
|
@Nullable Pageable pageable, Projection projection) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Context context = obtainContext();
|
||||||
|
|
||||||
|
ResearcherProfile profile = researcherProfileService.findById(context, id);
|
||||||
|
if (profile == null) {
|
||||||
|
throw new ResourceNotFoundException("No such item related to a profile with EPerson UUID: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return converter.toRest(profile.getItem(), projection);
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,189 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.app.rest.exception.DSpaceBadRequestException;
|
||||||
|
import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
import org.dspace.app.rest.model.patch.Patch;
|
||||||
|
import org.dspace.app.rest.repository.patch.ResourcePatch;
|
||||||
|
import org.dspace.app.rest.security.DSpacePermissionEvaluator;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.discovery.SearchServiceException;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.eperson.service.EPersonService;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the repository responsible of exposing researcher profiles.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component(ResearcherProfileRest.CATEGORY + "." + ResearcherProfileRest.NAME)
|
||||||
|
@ConditionalOnProperty(
|
||||||
|
value="researcher-profile.type"
|
||||||
|
)
|
||||||
|
public class ResearcherProfileRestRepository extends DSpaceRestRepository<ResearcherProfileRest, UUID> {
|
||||||
|
|
||||||
|
public static final String NO_VISIBILITY_CHANGE_MSG = "Refused to perform the Researcher Profile patch based "
|
||||||
|
+ "on a token without changing the visibility";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResearcherProfileService researcherProfileService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DSpacePermissionEvaluator permissionEvaluator;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EPersonService ePersonService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResourcePatch<ResearcherProfile> resourcePatch;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'PROFILE', 'READ')")
|
||||||
|
public ResearcherProfileRest findOne(Context context, UUID id) {
|
||||||
|
try {
|
||||||
|
ResearcherProfile profile = researcherProfileService.findById(context, id);
|
||||||
|
if (profile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return converter.toRest(profile, utils.obtainProjection());
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
protected ResearcherProfileRest createAndReturn(Context context) throws AuthorizeException, SQLException {
|
||||||
|
|
||||||
|
UUID id = getEPersonIdFromRequest(context);
|
||||||
|
if (isNotAuthorized(id, "WRITE")) {
|
||||||
|
throw new AuthorizeException("User unauthorized to create a new profile for user " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
EPerson ePerson = ePersonService.find(context, id);
|
||||||
|
if (ePerson == null) {
|
||||||
|
throw new UnprocessableEntityException("No EPerson exists with id: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ResearcherProfile newProfile = researcherProfileService.createAndReturn(context, ePerson);
|
||||||
|
return converter.toRest(newProfile, utils.obtainProjection());
|
||||||
|
} catch (SearchServiceException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResearcherProfileRest createAndReturn(final Context context, final List<String> list)
|
||||||
|
throws AuthorizeException, SQLException, RepositoryMethodNotImplementedException {
|
||||||
|
if (CollectionUtils.isEmpty(list) || list.size() > 1) {
|
||||||
|
throw new IllegalArgumentException("Uri list must contain exactly one element");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UUID id = getEPersonIdFromRequest(context);
|
||||||
|
if (isNotAuthorized(id, "WRITE")) {
|
||||||
|
throw new AuthorizeException("User unauthorized to create a new profile for user " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
EPerson ePerson = ePersonService.find(context, id);
|
||||||
|
if (ePerson == null) {
|
||||||
|
throw new UnprocessableEntityException("No EPerson exists with id: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ResearcherProfile newProfile = researcherProfileService
|
||||||
|
.claim(context, ePerson, URI.create(list.get(0)));
|
||||||
|
return converter.toRest(newProfile, utils.obtainProjection());
|
||||||
|
} catch (SearchServiceException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<ResearcherProfileRest> findAll(Context context, Pageable pageable) {
|
||||||
|
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'PROFILE', 'WRITE')")
|
||||||
|
protected void delete(Context context, UUID id) {
|
||||||
|
try {
|
||||||
|
researcherProfileService.deleteById(context, id);
|
||||||
|
} catch (SQLException | AuthorizeException e) {
|
||||||
|
throw new RuntimeException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@PreAuthorize("hasPermission(#id, 'PROFILE', #patch)")
|
||||||
|
protected void patch(Context context, HttpServletRequest request, String apiCategory, String model,
|
||||||
|
UUID id, Patch patch) throws SQLException, AuthorizeException {
|
||||||
|
|
||||||
|
ResearcherProfile profile = researcherProfileService.findById(context, id);
|
||||||
|
if (profile == null) {
|
||||||
|
throw new ResourceNotFoundException(apiCategory + "." + model + " with id: " + id + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcePatch.patch(context, profile, patch.getOperations());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ResearcherProfileRest> getDomainClass() {
|
||||||
|
return ResearcherProfileRest.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private UUID getEPersonIdFromRequest(Context context) {
|
||||||
|
HttpServletRequest request = getRequestService().getCurrentRequest().getHttpServletRequest();
|
||||||
|
|
||||||
|
String ePersonId = request.getParameter("eperson");
|
||||||
|
if (ePersonId == null) {
|
||||||
|
return context.getCurrentUser().getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID uuid = UUIDUtils.fromString(ePersonId);
|
||||||
|
if (uuid == null) {
|
||||||
|
throw new DSpaceBadRequestException("The provided eperson parameter is not a valid uuid");
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNotAuthorized(UUID id, String permission) {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
return !permissionEvaluator.hasPermission(authentication, id, "PROFILE", permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.repository.patch.operation;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.dspace.app.profile.ResearcherProfile;
|
||||||
|
import org.dspace.app.profile.service.ResearcherProfileService;
|
||||||
|
import org.dspace.app.rest.exception.RESTAuthorizationException;
|
||||||
|
import org.dspace.app.rest.exception.UnprocessableEntityException;
|
||||||
|
import org.dspace.app.rest.model.patch.Operation;
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for ResearcherProfile visibility patches.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code> curl -X PATCH http://${dspace.server.url}/api/cris/profiles/<:id-eperson> -H "
|
||||||
|
* Content-Type: application/json" -d '[{ "op": "replace", "path": "
|
||||||
|
* /visible", "value": true]'
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ResearcherProfileVisibleReplaceOperation extends PatchOperation<ResearcherProfile> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ResearcherProfileService researcherProfileService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path in json body of patch that uses this operation.
|
||||||
|
*/
|
||||||
|
public static final String OPERATION_VISIBLE_CHANGE = "/visible";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResearcherProfile perform(Context context, ResearcherProfile profile, Operation operation)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
Object value = operation.getValue();
|
||||||
|
if (value == null | !(value instanceof Boolean)) {
|
||||||
|
throw new UnprocessableEntityException("The /visible value must be a boolean (true|false)");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
researcherProfileService.changeVisibility(context, profile, (boolean) value);
|
||||||
|
} catch (AuthorizeException e) {
|
||||||
|
throw new RESTAuthorizationException("Unauthorized user for profile visibility change");
|
||||||
|
}
|
||||||
|
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Object objectToMatch, Operation operation) {
|
||||||
|
return (objectToMatch instanceof ResearcherProfile
|
||||||
|
&& operation.getOp().trim().equalsIgnoreCase(OPERATION_REPLACE)
|
||||||
|
&& operation.getPath().trim().equalsIgnoreCase(OPERATION_VISIBLE_CHANGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -11,12 +11,15 @@ import static org.dspace.app.rest.security.WebSecurityConfiguration.ADMIN_GRANT;
|
|||||||
import static org.dspace.app.rest.security.WebSecurityConfiguration.AUTHENTICATED_GRANT;
|
import static org.dspace.app.rest.security.WebSecurityConfiguration.AUTHENTICATED_GRANT;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.login.PostLoggedInAction;
|
||||||
import org.dspace.app.rest.utils.ContextUtil;
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
import org.dspace.app.util.AuthorizeUtil;
|
import org.dspace.app.util.AuthorizeUtil;
|
||||||
import org.dspace.authenticate.AuthenticationMethod;
|
import org.dspace.authenticate.AuthenticationMethod;
|
||||||
@@ -62,6 +65,16 @@ public class EPersonRestAuthenticationProvider implements AuthenticationProvider
|
|||||||
@Autowired
|
@Autowired
|
||||||
private HttpServletRequest request;
|
private HttpServletRequest request;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private List<PostLoggedInAction> postLoggedInActions;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
if (postLoggedInActions == null) {
|
||||||
|
postLoggedInActions = Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
Context context = ContextUtil.obtainContext(request);
|
Context context = ContextUtil.obtainContext(request);
|
||||||
@@ -122,6 +135,11 @@ public class EPersonRestAuthenticationProvider implements AuthenticationProvider
|
|||||||
.getHeader(newContext, "login", "type=explicit"));
|
.getHeader(newContext, "login", "type=explicit"));
|
||||||
|
|
||||||
output = createAuthentication(newContext);
|
output = createAuthentication(newContext);
|
||||||
|
|
||||||
|
for (PostLoggedInAction action : postLoggedInActions) {
|
||||||
|
action.loggedIn(newContext);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.info(LogHelper.getHeader(newContext, "failed_login", "email="
|
log.info(LogHelper.getHeader(newContext, "failed_login", "email="
|
||||||
+ name + ", result="
|
+ name + ", result="
|
||||||
|
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
package org.dspace.app.rest.security;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.app.rest.model.ResearcherProfileRest;
|
||||||
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
import org.dspace.eperson.EPerson;
|
||||||
|
import org.dspace.services.RequestService;
|
||||||
|
import org.dspace.services.model.Request;
|
||||||
|
import org.dspace.util.UUIDUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.dspace.app.rest.security.DSpaceRestPermission.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* An authenticated user is allowed to view, update or delete his or her own
|
||||||
|
* data. This {@link RestPermissionEvaluatorPlugin} implements that requirement.
|
||||||
|
*
|
||||||
|
* @author Luca Giamminonni (luca.giamminonni at 4science.it)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ResearcherProfileRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RequestService requestService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType,
|
||||||
|
DSpaceRestPermission restPermission) {
|
||||||
|
|
||||||
|
if (!READ.equals(restPermission) && !WRITE.equals(restPermission) && !DELETE.equals(restPermission)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.equalsIgnoreCase(targetType, ResearcherProfileRest.NAME)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID id = UUIDUtils.fromString(targetId.toString());
|
||||||
|
if (id == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Request request = requestService.getCurrentRequest();
|
||||||
|
Context context = ContextUtil.obtainContext((HttpServletRequest) request.getServletRequest());
|
||||||
|
|
||||||
|
EPerson currentUser = context.getCurrentUser();
|
||||||
|
if (currentUser == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.equals(currentUser.getID())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ import static org.hamcrest.Matchers.is;
|
|||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
import org.hamcrest.core.StringEndsWith;
|
import org.hamcrest.core.StringEndsWith;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +68,22 @@ public class MetadataMatcher {
|
|||||||
return hasJsonPath("$.['" + key + "'][" + position + "].value", is(value));
|
return hasJsonPath("$.['" + key + "'][" + position + "].value", is(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a matcher to ensure a given value is present at a specific position in
|
||||||
|
* the list of values for a given key.
|
||||||
|
*
|
||||||
|
* @param key the metadata key.
|
||||||
|
* @param value the value that must be present.
|
||||||
|
* @param authority the authority that must be present.
|
||||||
|
* @param position the position it must be present at.
|
||||||
|
* @return the matcher.
|
||||||
|
*/
|
||||||
|
public static Matcher<? super Object> matchMetadata(String key, String value, String authority, int position) {
|
||||||
|
Matcher<Object> hasValue = hasJsonPath("$.['" + key + "'][" + position + "].value", is(value));
|
||||||
|
Matcher<Object> hasAuthority = hasJsonPath("$.['" + key + "'][" + position + "].authority", is(authority));
|
||||||
|
return Matchers.allOf(hasValue, hasAuthority);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a matcher to ensure a given key is not present.
|
* Gets a matcher to ensure a given key is not present.
|
||||||
*
|
*
|
||||||
|
15
dspace/config/modules/authority.cfg
Normal file
15
dspace/config/modules/authority.cfg
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#---------------------------------------------------------------#
|
||||||
|
#----------------- AUTHORITY CONFIGURATIONS --------------------#
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
# These configs are used by the authority framework #
|
||||||
|
#---------------------------------------------------------------#
|
||||||
|
|
||||||
|
##### Authority Control Settings #####
|
||||||
|
|
||||||
|
plugin.named.org.dspace.content.authority.ChoiceAuthority = \
|
||||||
|
org.dspace.content.authority.EPersonAuthority = EPersonAuthority
|
||||||
|
|
||||||
|
|
||||||
|
choices.plugin.dspace.object.owner = EPersonAuthority
|
||||||
|
choices.presentation.dspace.object.owner = suggest
|
||||||
|
authority.controlled.dspace.object.owner = true
|
@@ -43,4 +43,12 @@
|
|||||||
<qualifier>enabled</qualifier>
|
<qualifier>enabled</qualifier>
|
||||||
<scope_note>Stores a boolean text value (true or false) to indicate if the iiif feature is enabled or not for the dspace object. If absent the value is derived from the parent dspace object</scope_note>
|
<scope_note>Stores a boolean text value (true or false) to indicate if the iiif feature is enabled or not for the dspace object. If absent the value is derived from the parent dspace object</scope_note>
|
||||||
</dc-type>
|
</dc-type>
|
||||||
|
|
||||||
|
<dc-type>
|
||||||
|
<schema>dspace</schema>
|
||||||
|
<element>object</element>
|
||||||
|
<qualifier>owner</qualifier>
|
||||||
|
<scope_note>Stores the reference to the eperson that own the item </scope_note>
|
||||||
|
</dc-type>
|
||||||
|
|
||||||
</dspace-dc-types>
|
</dspace-dc-types>
|
||||||
|
@@ -63,6 +63,8 @@
|
|||||||
<bean class="org.dspace.content.authority.ChoiceAuthorityServiceImpl"/>
|
<bean class="org.dspace.content.authority.ChoiceAuthorityServiceImpl"/>
|
||||||
<bean class="org.dspace.content.authority.MetadataAuthorityServiceImpl" lazy-init="true"/>
|
<bean class="org.dspace.content.authority.MetadataAuthorityServiceImpl" lazy-init="true"/>
|
||||||
|
|
||||||
|
<bean class="org.dspace.app.profile.ResearcherProfileServiceImpl"/>
|
||||||
|
|
||||||
<bean class='org.dspace.service.impl.HttpConnectionPoolService'
|
<bean class='org.dspace.service.impl.HttpConnectionPoolService'
|
||||||
id='solrHttpConnectionPoolService'
|
id='solrHttpConnectionPoolService'
|
||||||
scope='singleton'
|
scope='singleton'
|
||||||
|
15
dspace/config/spring/rest/post-logged-in-actions.xml
Normal file
15
dspace/config/spring/rest/post-logged-in-actions.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:util="http://www.springframework.org/schema/util"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||||||
|
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
|
||||||
|
|
||||||
|
<bean id="researcherProfileClaimByEmail" class="org.dspace.app.rest.login.impl.ResearcherProfileAutomaticClaim">
|
||||||
|
<constructor-arg name="ePersonField" value="email" />
|
||||||
|
<constructor-arg name="profileField" value="person.email" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="loginEventFireAction" class="org.dspace.app.rest.login.impl.LoginEventFireAction"/>
|
||||||
|
|
||||||
|
</beans>
|
Reference in New Issue
Block a user