From 003daf0a054932e600569966be938ef8844bec1b Mon Sep 17 00:00:00 2001 From: xuejiangtao <779480193@qq.com> Date: Wed, 15 May 2019 23:26:02 +0800 Subject: [PATCH 01/73] remove the condition that is always 'true' --- .../src/main/java/org/dspace/app/util/IndexVersion.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java b/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java index d8b2d6868aac..a01c8af616a9 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java +++ b/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java @@ -252,10 +252,6 @@ public static int compareSoftwareVersions(String firstVersion, String secondVers return GREATER_THAN; } else if (firstMinor < secondMinor) { return LESS_THAN; - } else { - // This is an impossible scenario. - // This 'else' should never be triggered since we've checked for equality above already - return EQUAL; } } From 6ad6186375d596014530fc502e8d721eb197d543 Mon Sep 17 00:00:00 2001 From: xuejiangtao <779480193@qq.com> Date: Mon, 20 May 2019 22:54:45 +0800 Subject: [PATCH 02/73] add default return statement --- dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java b/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java index a01c8af616a9..0c4e4753391d 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java +++ b/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java @@ -253,6 +253,7 @@ public static int compareSoftwareVersions(String firstVersion, String secondVers } else if (firstMinor < secondMinor) { return LESS_THAN; } + return EQUAL; } /** From 9766fe131d241bf55695b7b8768d5a33ac62940b Mon Sep 17 00:00:00 2001 From: xuejiangtao <779480193@qq.com> Date: Tue, 21 May 2019 21:52:36 +0800 Subject: [PATCH 03/73] remove the last if statement to avoid the dead code --- dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java b/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java index 0c4e4753391d..7bdaa95b5c02 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java +++ b/dspace-api/src/main/java/org/dspace/app/util/IndexVersion.java @@ -250,10 +250,9 @@ public static int compareSoftwareVersions(String firstVersion, String secondVers } else if (firstMinor > secondMinor) { // If we get here, major versions must be EQUAL. Now, time to check our minor versions return GREATER_THAN; - } else if (firstMinor < secondMinor) { + } else { return LESS_THAN; } - return EQUAL; } /** From 5d714a174ee9c36e82baa2249df85b46357a0c08 Mon Sep 17 00:00:00 2001 From: nicholas Date: Wed, 1 Apr 2020 09:22:29 -0500 Subject: [PATCH 04/73] include limit and offset when retrieving items for a collection --- .../org/dspace/rest/CollectionsResource.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java b/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java index af06792b7b13..66919ad5c70e 100644 --- a/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java +++ b/dspace-rest/src/main/java/org/dspace/rest/CollectionsResource.java @@ -274,16 +274,16 @@ public org.dspace.rest.common.Item[] getCollectionItems(@PathParam("collection_i headers, request, context); items = new ArrayList(); - Iterator dspaceItems = itemService.findByCollection(context, dspaceCollection); - for (int i = 0; (dspaceItems.hasNext()) && (i < (limit + offset)); i++) { + Iterator dspaceItems = itemService.findByCollection(context, dspaceCollection, + limit, offset); + + while (dspaceItems.hasNext()) { org.dspace.content.Item dspaceItem = dspaceItems.next(); - if (i >= offset) { - if (itemService.isItemListedForUser(context, dspaceItem)) { - items.add(new Item(dspaceItem, servletContext, expand, context)); - writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, - headers, request, context); - } + if (itemService.isItemListedForUser(context, dspaceItem)) { + items.add(new Item(dspaceItem, servletContext, expand, context)); + writeStats(dspaceItem, UsageEvent.Action.VIEW, user_ip, user_agent, xforwardedfor, + headers, request, context); } } From 6e0396e6ad33cedbe81adddf0e146887abaea4d9 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 8 Apr 2020 15:14:18 +0200 Subject: [PATCH 05/73] 70213: Initial findAll endpoint & backend --- .../java/org/dspace/license/CCLicense.java | 26 ++- .../license/CCLicenseConnectorService.java | 26 +++ .../CCLicenseConnectorServiceImpl.java | 216 ++++++++++++++++++ .../org/dspace/license/CCLicenseField.java | 25 +- .../dspace/license/CCLicenseFieldEnum.java | 82 +++++++ .../java/org/dspace/license/CCLookup.java | 50 ++-- .../license/CreativeCommonsServiceImpl.java | 68 ++++-- .../service/CreativeCommonsService.java | 17 ++ .../MockCCLicenseConnectorServiceImpl.java | 72 ++++++ .../SubmissionCCLicenseConverter.java | 59 +++++ .../SubmissionCCLicenseFieldConverter.java | 61 +++++ ...SubmissionCCLicenseFieldEnumConverter.java | 45 ++++ .../SubmissionCCLicenseFieldEnumRest.java | 44 ++++ .../model/SubmissionCCLicenseFieldRest.java | 59 +++++ .../rest/model/SubmissionCCLicenseRest.java | 73 ++++++ .../hateoas/SubmissionCCLicenseResource.java | 23 ++ .../SubmissionCCLicenseRestRepository.java | 44 ++++ .../config/spring/api/core-services-mock.xml | 1 + .../SubmissionCCLicenseRestRepositoryIT.java | 46 ++++ .../matcher/SubmissionCCLicenseMatcher.java | 82 +++++++ .../MockCCLicenseConnectorServiceImpl.java | 72 ++++++ dspace/config/spring/api/core-services.xml | 1 + 22 files changed, 1121 insertions(+), 71 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java create mode 100644 dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java create mode 100644 dspace-api/src/main/java/org/dspace/license/CCLicenseFieldEnum.java create mode 100644 dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldEnumRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseResource.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SubmissionCCLicenseMatcher.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicense.java b/dspace-api/src/main/java/org/dspace/license/CCLicense.java index b015e3a9d3ed..d5d9fe14a23a 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicense.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicense.java @@ -8,6 +8,8 @@ package org.dspace.license; +import java.util.List; + /** * @author wbossons */ @@ -15,17 +17,17 @@ public class CCLicense { private String licenseName; private String licenseId; - private int order = 0; + private List ccLicenseFieldList; public CCLicense() { super(); } - public CCLicense(String licenseId, String licenseName, int order) { + public CCLicense(String licenseId, String licenseName, List ccLicenseFieldList) { super(); this.licenseId = licenseId; this.licenseName = licenseName; - this.order = order; + this.ccLicenseFieldList = ccLicenseFieldList; } public String getLicenseName() { @@ -44,13 +46,19 @@ public void setLicenseId(String licenseId) { this.licenseId = licenseId; } - public int getOrder() { - return this.order; + /** + * Gets the list of CC License Fields + * @return the list of CC License Fields + */ + public List getCcLicenseFieldList() { + return ccLicenseFieldList; } - public void setOrder(int order) { - this.order = order; + /** + * Sets the list of CC License Fields + * @param ccLicenseFieldList + */ + public void setCcLicenseFieldList(final List ccLicenseFieldList) { + this.ccLicenseFieldList = ccLicenseFieldList; } - - } diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java new file mode 100644 index 000000000000..52bbf39cc7b4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java @@ -0,0 +1,26 @@ +/** + * 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.license; + +import java.util.List; + +/** + * Service interface class for the Creative commons license connector service. + * The implementation of this class is responsible for all the calls to the CC license API and parsing the response + * The service is autowired by spring + */ +public interface CCLicenseConnectorService { + + /** + * Retrieves the CC Licenses for the provided language from the CC License API + * @param language - the language to retrieve the licenses for + * @return a list of licenses obtained for the provided languages + */ + public List retrieveLicenses(String language); + +} diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java new file mode 100644 index 000000000000..edc99346941a --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -0,0 +1,216 @@ +/** + * 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.license; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.apache.logging.log4j.Logger; +import org.dspace.services.ConfigurationService; +import org.jaxen.JaxenException; +import org.jaxen.jdom.JDOMXPath; +import org.jdom.Attribute; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jdom.input.SAXBuilder; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.xml.sax.InputSource; + +/** + * Implementation for the Creative commons license connector service. + * This class is responsible for all the calls to the CC license API and parsing the response + */ +public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, InitializingBean { + + private Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLicenseConnectorServiceImpl.class); + + private CloseableHttpClient client; + private SAXBuilder parser = new SAXBuilder(); + + + @Autowired + private ConfigurationService configurationService; + + @Override + public void afterPropertiesSet() throws Exception { + HttpClientBuilder builder = HttpClientBuilder.create(); + + client = builder + .disableAutomaticRetries() + .setMaxConnTotal(5) + .build(); + } + + /** + * Retrieves the CC Licenses for the provided language from the CC License API + * @param language - the language to retrieve the licenses for + * @return a list of licenses obtained for the provided languages + */ + public List retrieveLicenses(String language) { + String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); + + HttpGet httpGet = new HttpGet(ccLicenseUrl + "/?locale=" + language); + + List licenses; + try (CloseableHttpResponse response = client.execute(httpGet)) { + licenses = retrieveLicenses(response); + } catch (JDOMException | JaxenException | IOException e) { + log.error(e); + licenses = Collections.emptyList(); + } + + List ccLicenses = new LinkedList<>(); + + for (String license : licenses) { + + HttpGet licenseHttpGet = new HttpGet(ccLicenseUrl + "/license/" + license); + try (CloseableHttpResponse response = client.execute(licenseHttpGet)) { + CCLicense ccLicense = retrieveLicenseObject(response); + ccLicenses.add(ccLicense); + } catch (JaxenException | JDOMException | IOException e) { + log.error(e); + } + } + + return ccLicenses; + } + + /** + * Retrieve the list of licenses from the response from the CC License API and remove the licenses configured + * to be excluded + * @param response The response from the API + * @return a list of license identifiers for which details need to be retrieved + * @throws IOException + * @throws JaxenException + * @throws JDOMException + */ + private List retrieveLicenses(CloseableHttpResponse response) + throws IOException, JaxenException, JDOMException { + + List domains = new LinkedList<>(); + String[] excludedLicenses = configurationService.getArrayProperty("cc.license.classfilter"); + + + String responseString = EntityUtils.toString(response.getEntity()); + JDOMXPath licenseClassXpath = new JDOMXPath("//licenses/license"); + + + InputSource is = new InputSource(new StringReader(responseString)); + org.jdom.Document classDoc = this.parser.build(is); + + List elements = licenseClassXpath.selectNodes(classDoc); + for (Element element : elements) { + String licenseId = getSingleNodeValue(element, "@id"); + if (StringUtils.isNotBlank(licenseId) && !ArrayUtils.contains(excludedLicenses, licenseId)) { + domains.add(licenseId); + } + } + + return domains; + + } + + /** + * Parse the response for a single CC License and return the corresponding CC License Object + * @param response for a specific CC License response + * @return the corresponding CC License Object + * @throws IOException + * @throws JaxenException + * @throws JDOMException + */ + private CCLicense retrieveLicenseObject(CloseableHttpResponse response) + throws IOException, JaxenException, JDOMException { + + String responseString = EntityUtils.toString(response.getEntity()); + + + JDOMXPath licenseClassXpath = new JDOMXPath("//licenseclass"); + JDOMXPath licenseFieldXpath = new JDOMXPath("field"); + + + InputSource is; + + is = new InputSource(new StringReader(responseString)); + + org.jdom.Document classDoc = this.parser.build(is); + + Object element = licenseClassXpath.selectSingleNode(classDoc); + String licenseId = getSingleNodeValue(element, "@id"); + String licenseLabel = getSingleNodeValue(element, "label"); + + List ccLicenseFields = new LinkedList<>(); + + List licenseFields = licenseFieldXpath.selectNodes(element); + for (Element licenseField : licenseFields) { + CCLicenseField ccLicenseField = parseLicenseField(licenseField); + ccLicenseFields.add(ccLicenseField); + } + + + return new CCLicense(licenseId, licenseLabel, ccLicenseFields); + } + + private CCLicenseField parseLicenseField(final Element licenseField) throws JaxenException { + String id = getSingleNodeValue(licenseField, "@id"); + String label = getSingleNodeValue(licenseField, "label"); + String description = getSingleNodeValue(licenseField, "description"); + + JDOMXPath enumXpath = new JDOMXPath("enum"); + List enums = enumXpath.selectNodes(licenseField); + + List ccLicenseFieldEnumList = new LinkedList<>(); + + for (Element enumElement : enums) { + CCLicenseFieldEnum ccLicenseFieldEnum = parseEnum(enumElement); + ccLicenseFieldEnumList.add(ccLicenseFieldEnum); + } + + return new CCLicenseField(id, label, description, ccLicenseFieldEnumList); + + } + + private CCLicenseFieldEnum parseEnum(final Element enumElement) throws JaxenException { + String id = getSingleNodeValue(enumElement, "@id"); + String label = getSingleNodeValue(enumElement, "label"); + String description = getSingleNodeValue(enumElement, "description"); + + return new CCLicenseFieldEnum(id, label, description); + } + + + private String getNodeValue(final Object el) { + if (el instanceof Element) { + return ((Element) el).getValue(); + } else if (el instanceof Attribute) { + return ((Attribute) el).getValue(); + } else if (el instanceof String) { + return (String) el; + } else { + return null; + } + } + + private String getSingleNodeValue(final Object t, String query) throws JaxenException { + JDOMXPath xpath = new JDOMXPath(query); + Object singleNode = xpath.selectSingleNode(t); + + return getNodeValue(singleNode); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseField.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseField.java index 6360249f65c9..8fb6de5478a7 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseField.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseField.java @@ -7,8 +7,7 @@ */ package org.dspace.license; -import java.util.HashMap; -import java.util.Map; +import java.util.List; /** * Wrapper class for representation of a license field declaration. @@ -22,7 +21,7 @@ public class CCLicenseField { private String description = ""; private String type = ""; - private HashMap fieldEnum = null; + private List fieldEnum = null; /** * Construct a new LicenseField class. Note that after construction, @@ -31,13 +30,11 @@ public class CCLicenseField { * @param id The unique identifier for this field; this value will be used in constructing the answers XML. * @param label The label to use when generating the user interface. */ - public CCLicenseField(String id, String label) { - super(); - - this.fieldEnum = new HashMap(); - + public CCLicenseField(String id, String label, String description, List fieldEnum) { this.id = id; this.label = label; + this.description = description; + this.fieldEnum = fieldEnum; } /** @@ -90,16 +87,12 @@ public void setType(String type) { } /** - * @return Returns an instance implementing the Map interface; - * the instance contains a mapping from identifiers to - * labels for the enumeration values. - * @see Map + * Returns the list of enums of this field + * @return the list of enums of this field */ - public Map getEnum() { - return this.fieldEnum; + public List getFieldEnum() { + return fieldEnum; } - - } diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseFieldEnum.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseFieldEnum.java new file mode 100644 index 000000000000..628fcb83546e --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseFieldEnum.java @@ -0,0 +1,82 @@ +/** + * 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.license; + +import org.apache.commons.lang3.StringUtils; + +/** + * Wrapper class for representation of a license field enum declaration. + * A field enum is a single "answer" to the field question + */ +public class CCLicenseFieldEnum { + + private String id = ""; + private String label = ""; + private String description = ""; + + public CCLicenseFieldEnum(String id, String label, String description) { + if (StringUtils.isNotBlank(id)) { + this.id = id; + } + if (StringUtils.isNotBlank(label)) { + this.label = label; + } + if (StringUtils.isNotBlank(description)) { + this.description = description; + } + + } + + /** + * Get the id of this enum + * @return the id of this enum + */ + public String getId() { + return id; + } + + /** + * Set the id of this enum + * @param id + */ + public void setId(final String id) { + this.id = id; + } + + /** + * Get the label of this enum + * @return the label of this enum + */ + public String getLabel() { + return label; + } + + /** + * Set the label of this enum + * @param label + */ + public void setLabel(final String label) { + this.label = label; + } + + /** + * Get the description of this enum + * @return the description of this enum + */ + public String getDescription() { + return description; + } + + /** + * Set the description of this enum + * @param description + */ + public void setDescription(final String description) { + this.description = description; + } +} diff --git a/dspace-api/src/main/java/org/dspace/license/CCLookup.java b/dspace-api/src/main/java/org/dspace/license/CCLookup.java index c86aa7830186..b7ddfa2314b4 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLookup.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLookup.java @@ -128,7 +128,7 @@ public Collection getLicenses(String language) { // add if not filtered String liD = ((Attribute) xp_LicenseID.selectSingleNode(license)).getValue(); if (!lcFilter.contains(liD)) { - this.licenses.add(new CCLicense(liD, license.getText(), i)); +// this.licenses.add(new CCLicense(liD, license.getText(), i)); } } } catch (JaxenException jaxen_e) { @@ -213,30 +213,30 @@ public Collection getLicenseFields(String license, String langua for (int i = 0; i < results.size(); i++) { Element field = (Element) results.get(i); - try { - // create the field object - CCLicenseField cclicensefield = new CCLicenseField( - ((Attribute) xp_LicenseID.selectSingleNode(field)).getValue(), - ((Element) xp_Label.selectSingleNode(field)).getText()); - - // extract additional properties - cclicensefield.setDescription(((Element) xp_Description.selectSingleNode(field)).getText()); - cclicensefield.setType(((Element) xp_FieldType.selectSingleNode(field)).getText()); - - enumOptions = xp_Enum.selectNodes(field); - - for (int j = 0; j < enumOptions.size(); j++) { - String id = ((Attribute) xp_LicenseID.selectSingleNode(enumOptions.get(j))).getValue(); - String label = ((Element) xp_Label.selectSingleNode(enumOptions.get(j))).getText(); - - cclicensefield.getEnum().put(id, label); - - } // for each enum option - - this.licenseFields.add(cclicensefield); - } catch (JaxenException e) { - return null; - } +// try { +// // create the field object +// CCLicenseField cclicensefield = new CCLicenseField( +// ((Attribute) xp_LicenseID.selectSingleNode(field)).getValue(), +// ((Element) xp_Label.selectSingleNode(field)).getText()); +// +// // extract additional properties +// cclicensefield.setDescription(((Element) xp_Description.selectSingleNode(field)).getText()); +// cclicensefield.setType(((Element) xp_FieldType.selectSingleNode(field)).getText()); +// +// enumOptions = xp_Enum.selectNodes(field); +// +// for (int j = 0; j < enumOptions.size(); j++) { +// String id = ((Attribute) xp_LicenseID.selectSingleNode(enumOptions.get(j))).getValue(); +// String label = ((Element) xp_Label.selectSingleNode(enumOptions.get(j))).getText(); +// +//// cclicensefield.getEnum().put(id, label); +// +// } // for each enum option +// +// this.licenseFields.add(cclicensefield); +// } catch (JaxenException e) { +// return null; +// } } return licenseFields; diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index 384b82ddc33c..fed51a9f0a6c 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -82,9 +82,13 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi protected BundleService bundleService; @Autowired(required = true) protected ItemService itemService; + @Autowired + protected CCLicenseConnectorService ccLicenseConnectorService; protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private List ccLicenses; + protected CreativeCommonsServiceImpl() { } @@ -103,8 +107,8 @@ public void afterPropertiesSet() throws Exception { try { templates = TransformerFactory.newInstance().newTemplates( - new StreamSource(CreativeCommonsServiceImpl.class - .getResourceAsStream("CreativeCommons.xsl"))); + new StreamSource(CreativeCommonsServiceImpl.class + .getResourceAsStream("CreativeCommons.xsl"))); } catch (TransformerConfigurationException e) { throw new RuntimeException(e.getMessage(), e); } @@ -120,7 +124,7 @@ public boolean isEnabled() { // create the CC bundle if it doesn't exist // If it does, remove it and create a new one. protected Bundle getCcBundle(Context context, Item item) - throws SQLException, AuthorizeException, IOException { + throws SQLException, AuthorizeException, IOException { List bundles = itemService.getBundles(item, CC_BUNDLE_NAME); if ((bundles.size() > 0) && (bundles.get(0) != null)) { @@ -131,8 +135,8 @@ protected Bundle getCcBundle(Context context, Item item) @Override public void setLicenseRDF(Context context, Item item, String licenseRdf) - throws SQLException, IOException, - AuthorizeException { + throws SQLException, IOException, + AuthorizeException { Bundle bundle = getCcBundle(context, item); // set the format BitstreamFormat bs_rdf_format = bitstreamFormatService.findByShortDescription(context, "RDF XML"); @@ -144,7 +148,7 @@ public void setLicenseRDF(Context context, Item item, String licenseRdf) @Override public void setLicense(Context context, Item item, InputStream licenseStm, String mimeType) - throws SQLException, IOException, AuthorizeException { + throws SQLException, IOException, AuthorizeException { Bundle bundle = getCcBundle(context, item); // set the format @@ -160,9 +164,9 @@ public void setLicense(Context context, Item item, Bitstream bs = bitstreamService.create(context, bundle, licenseStm); bs.setSource(context, CC_BS_SOURCE); bs.setName(context, (mimeType != null && - (mimeType.equalsIgnoreCase("text/xml") || - mimeType.equalsIgnoreCase("text/rdf"))) ? - BSN_LICENSE_RDF : BSN_LICENSE_TEXT); + (mimeType.equalsIgnoreCase("text/xml") || + mimeType.equalsIgnoreCase("text/rdf"))) ? + BSN_LICENSE_RDF : BSN_LICENSE_TEXT); bs.setFormat(context, bs_format); bitstreamService.update(context, bs); } @@ -170,7 +174,7 @@ public void setLicense(Context context, Item item, @Override public void removeLicense(Context context, Item item) - throws SQLException, IOException, AuthorizeException { + throws SQLException, IOException, AuthorizeException { // remove CC license bundle if one exists List bundles = itemService.getBundles(item, CC_BUNDLE_NAME); @@ -181,7 +185,7 @@ public void removeLicense(Context context, Item item) @Override public boolean hasLicense(Context context, Item item) - throws SQLException, IOException { + throws SQLException, IOException { // try to find CC license bundle List bundles = itemService.getBundles(item, CC_BUNDLE_NAME); @@ -203,20 +207,20 @@ public boolean hasLicense(Context context, Item item) @Override public String getLicenseRDF(Context context, Item item) throws SQLException, - IOException, AuthorizeException { + IOException, AuthorizeException { return getStringFromBitstream(context, item, BSN_LICENSE_RDF); } @Override public Bitstream getLicenseRdfBitstream(Item item) throws SQLException, - IOException, AuthorizeException { + IOException, AuthorizeException { return getBitstream(item, BSN_LICENSE_RDF); } @Deprecated @Override public Bitstream getLicenseTextBitstream(Item item) throws SQLException, - IOException, AuthorizeException { + IOException, AuthorizeException { return getBitstream(item, BSN_LICENSE_TEXT); } @@ -237,8 +241,8 @@ public String fetchLicenseRDF(Document license) { try { templates.newTransformer().transform( - new JDOMSource(license), - new StreamResult(result) + new JDOMSource(license), + new StreamResult(result) ); } catch (TransformerException e) { throw new IllegalStateException(e.getMessage(), e); @@ -267,7 +271,7 @@ public String fetchLicenseRDF(Document license) { */ protected void setBitstreamFromBytes(Context context, Item item, Bundle bundle, String bitstream_name, BitstreamFormat format, byte[] bytes) - throws SQLException, IOException, AuthorizeException { + throws SQLException, IOException, AuthorizeException { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); Bitstream bs = bitstreamService.create(context, bundle, bais); @@ -297,7 +301,7 @@ protected void setBitstreamFromBytes(Context context, Item item, Bundle bundle, */ protected String getStringFromBitstream(Context context, Item item, String bitstream_name) throws SQLException, IOException, - AuthorizeException { + AuthorizeException { byte[] bytes = getBytesFromBitstream(context, item, bitstream_name); if (bytes == null) { @@ -320,7 +324,7 @@ protected String getStringFromBitstream(Context context, Item item, * to perform a particular action. */ protected Bitstream getBitstream(Item item, String bitstream_name) - throws SQLException, IOException, AuthorizeException { + throws SQLException, IOException, AuthorizeException { Bundle cc_bundle = null; // look for the CC bundle @@ -342,7 +346,7 @@ protected Bitstream getBitstream(Item item, String bitstream_name) } protected byte[] getBytesFromBitstream(Context context, Item item, String bitstream_name) - throws SQLException, IOException, AuthorizeException { + throws SQLException, IOException, AuthorizeException { Bitstream bs = getBitstream(item, bitstream_name); // no such bitstream @@ -368,7 +372,7 @@ public LicenseMetadataValue getCCField(String fieldId) { @Override public void removeLicense(Context context, LicenseMetadataValue uriField, LicenseMetadataValue nameField, Item item) - throws AuthorizeException, IOException, SQLException { + throws AuthorizeException, IOException, SQLException { // only remove any previous licenses String licenseUri = uriField.ccItemValue(item); if (licenseUri != null) { @@ -383,4 +387,26 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, } } + /** + * Find all CC Licenses using the default language found in the configuration + * @return A list of available CC Licenses + */ + public List findAllCCLicenses() { + String language = configurationService.getProperty("cc.license.locale", "en"); + return findAllCCLicenses(language); + } + + /** + * Find all CC Licenses for the provided language + * @param language - the language for which to find the CC Licenses + * @return A list of available CC Licenses for the provided language + */ + public List findAllCCLicenses(String language) { + + if (ccLicenses == null || ccLicenses.isEmpty()) { + ccLicenses = ccLicenseConnectorService.retrieveLicenses(language); + } + return ccLicenses; + } + } diff --git a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java index c99c38a127e7..d25f02ff7b25 100644 --- a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java +++ b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java @@ -10,11 +10,13 @@ import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; +import java.util.List; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; import org.dspace.content.Item; import org.dspace.core.Context; +import org.dspace.license.CCLicense; import org.dspace.license.LicenseMetadataValue; import org.jdom.Document; @@ -149,4 +151,19 @@ public Bitstream getLicenseTextBitstream(Item item) public void removeLicense(Context context, LicenseMetadataValue uriField, LicenseMetadataValue nameField, Item item) throws AuthorizeException, IOException, SQLException; + + /** + * Find all CC Licenses using the default language found in the configuration + * + * @return A list of available CC Licenses + */ + public List findAllCCLicenses(); + + /** + * Find all CC Licenses for the provided language + * + * @param language - the language for which to find the CC Licenses + * @return A list of available CC Licenses for the provided language + */ + public List findAllCCLicenses(String language); } diff --git a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java new file mode 100644 index 000000000000..54219bbaaaf1 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -0,0 +1,72 @@ +/** + * 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.license; + +import java.util.LinkedList; +import java.util.List; + +/** + * Mock implementation for the Creative commons license connector service. + * This class will return a structure of CC Licenses similar to the CC License API but without having to contact it + */ +public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorServiceImpl { + + /** + * Retrieves mock CC Licenses for the provided language + * @param language - the language + * @return a list of mocked licenses + */ + public List retrieveLicenses(String language) { + List ccLicenses = new LinkedList<>(); + ccLicenses.add(createMockLicense(1, new int[]{3, 2, 3})); + ccLicenses.add(createMockLicense(2, new int[]{2})); + ccLicenses.add(createMockLicense(3, new int[]{})); + + return ccLicenses; + } + + private CCLicense createMockLicense(int count, int[] amountOfFieldsAndEnums) { + String licenseId = "license" + count; + String licenseName = "License " + count + " - Name"; + List mockLicenseFields = createMockLicenseFields(count, amountOfFieldsAndEnums); + return new CCLicense(licenseId, licenseName, mockLicenseFields); + } + + private List createMockLicenseFields(int count, int[] amountOfFieldsAndEnums) { + List ccLicenseFields = new LinkedList<>(); + for (int index = 0; index < amountOfFieldsAndEnums.length; index++) { + String licenseFieldId = "license" + count + "-field" + index; + String licenseFieldLabel = "License " + count + " - Field " + index + " - Label"; + String licenseFieldDescription = "License " + count + " - Field " + index + " - Description"; + List mockLicenseFields = createMockLicenseFields(count, + index, + amountOfFieldsAndEnums[index]); + ccLicenseFields.add(new CCLicenseField(licenseFieldId, + licenseFieldLabel, + licenseFieldDescription, + mockLicenseFields)); + + } + + return ccLicenseFields; + } + + private List createMockLicenseFields(int count, int index, int amountOfEnums) { + List ccLicenseFieldEnumList = new LinkedList<>(); + for (int i = 0; i < amountOfEnums; i++) { + String enumId = "license" + count + "-field" + index + "-enum" + i; + String enumLabel = "License " + count + " - Field " + index + " - Enum " + i + " - Label"; + String enumDescription = "License " + count + " - Field " + index + " - Enum " + i + " - " + + "Description"; + ccLicenseFieldEnumList.add(new CCLicenseFieldEnum(enumId, enumLabel, enumDescription)); + } + return ccLicenseFieldEnumList; + + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java new file mode 100644 index 000000000000..bd7e582e75b2 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java @@ -0,0 +1,59 @@ +/** + * 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 java.util.LinkedList; +import java.util.List; + +import org.dspace.app.rest.model.SubmissionCCLicenseFieldRest; +import org.dspace.app.rest.model.SubmissionCCLicenseRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.license.CCLicense; +import org.dspace.license.CCLicenseField; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * This converter is responsible for transforming the model representation of an CCLicense to the REST + * representation of an CCLicense and vice versa + **/ +@Component +public class SubmissionCCLicenseConverter implements DSpaceConverter { + + @Autowired + private ConverterService converter; + + /** + * Convert a CCLicense to its REST representation + * @param modelObject - the CCLicense to convert + * @param projection - the projection + * @return the corresponding SubmissionCCLicenseRest object + */ + @Override + public SubmissionCCLicenseRest convert(final CCLicense modelObject, final Projection projection) { + SubmissionCCLicenseRest submissionCCLicenseRest = new SubmissionCCLicenseRest(); + submissionCCLicenseRest.setProjection(projection); + submissionCCLicenseRest.setId(modelObject.getLicenseId()); + submissionCCLicenseRest.setName(modelObject.getLicenseName()); + + List ccLicenseFieldList = modelObject.getCcLicenseFieldList(); + List submissionCCLicenseFieldRests = new LinkedList<>(); + if (ccLicenseFieldList != null) { + for (CCLicenseField ccLicenseField : ccLicenseFieldList) { + submissionCCLicenseFieldRests.add(converter.toRest(ccLicenseField, projection)); + } + } + submissionCCLicenseRest.setFields(submissionCCLicenseFieldRests); + return submissionCCLicenseRest; + } + + public Class getModelClass() { + return CCLicense.class; + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java new file mode 100644 index 000000000000..b0418ef4b586 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java @@ -0,0 +1,61 @@ +/** + * 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 java.util.LinkedList; +import java.util.List; + +import org.dspace.app.rest.model.SubmissionCCLicenseFieldEnumRest; +import org.dspace.app.rest.model.SubmissionCCLicenseFieldRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.license.CCLicenseField; +import org.dspace.license.CCLicenseFieldEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * This converter is responsible for transforming the model representation of an CCLicenseField to the REST + * representation of an CCLicenseField and vice versa + * The CCLicenseField is a sub component of the CCLicense object + **/ +@Component +public class SubmissionCCLicenseFieldConverter + implements DSpaceConverter { + + @Autowired + private ConverterService converter; + + /** + * Convert a CCLicenseField to its REST representation + * @param modelObject - the CCLicenseField to convert + * @param projection - the projection + * @return the corresponding SubmissionCCLicenseFieldRest object + */ + @Override + public SubmissionCCLicenseFieldRest convert(final CCLicenseField modelObject, final Projection projection) { + SubmissionCCLicenseFieldRest submissionCCLicenseFieldRest = new SubmissionCCLicenseFieldRest(); + submissionCCLicenseFieldRest.setId(modelObject.getId()); + submissionCCLicenseFieldRest.setLabel(modelObject.getLabel()); + submissionCCLicenseFieldRest.setDescription(modelObject.getDescription()); + + List fieldEnum = modelObject.getFieldEnum(); + List submissionCCLicenseFieldEnumRests = new LinkedList<>(); + if (fieldEnum != null) { + for (CCLicenseFieldEnum ccLicenseFieldEnum : fieldEnum) { + submissionCCLicenseFieldEnumRests.add(converter.toRest(ccLicenseFieldEnum, projection)); + } + } + submissionCCLicenseFieldRest.setEnums(submissionCCLicenseFieldEnumRests); + return submissionCCLicenseFieldRest; + } + + public Class getModelClass() { + return CCLicenseField.class; + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java new file mode 100644 index 000000000000..85ed1c45e40d --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java @@ -0,0 +1,45 @@ +/** + * 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 org.dspace.app.rest.model.SubmissionCCLicenseFieldEnumRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.license.CCLicenseFieldEnum; +import org.springframework.stereotype.Component; + +/** + * This converter is responsible for transforming the model representation of an CCLicenseFieldEnum to the REST + * representation of an CCLicenseFieldEnum and vice versa + * The CCLicenseFieldEnum is a sub component of the CCLicenseField object + **/ +@Component +public class SubmissionCCLicenseFieldEnumConverter + implements DSpaceConverter { + + /** + * Convert a CCLicenseFieldEnum to its REST representation + * + * @param modelObject - the CCLicenseField to convert + * @param projection - the projection + * @return the corresponding SubmissionCCLicenseFieldEnumRest object + */ + @Override + public SubmissionCCLicenseFieldEnumRest convert(final CCLicenseFieldEnum modelObject, final Projection projection) { + SubmissionCCLicenseFieldEnumRest submissionCCLicenseFieldEnumRest = new SubmissionCCLicenseFieldEnumRest(); + submissionCCLicenseFieldEnumRest.setId(modelObject.getId()); + submissionCCLicenseFieldEnumRest.setLabel(modelObject.getLabel()); + submissionCCLicenseFieldEnumRest.setDescription(modelObject.getDescription()); + + return submissionCCLicenseFieldEnumRest; + } + + public Class getModelClass() { + return CCLicenseFieldEnum.class; + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldEnumRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldEnumRest.java new file mode 100644 index 000000000000..770eb25782a8 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldEnumRest.java @@ -0,0 +1,44 @@ +/** + * 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; + +/** + * This class is the REST representation of the CCLicenseFieldEnum model object and acts as a data sub object + * for the SubmissionCCLicenseFieldRest class. + * Refer to {@link org.dspace.license.CCLicenseFieldEnum} for explanation of the properties + */ +public class SubmissionCCLicenseFieldEnumRest { + + private String id; + private String label; + private String description; + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getLabel() { + return label; + } + + public void setLabel(final String label) { + this.label = label; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldRest.java new file mode 100644 index 000000000000..bcc90279dc8a --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseFieldRest.java @@ -0,0 +1,59 @@ +/** + * 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 java.util.List; + +/** + * This class is the REST representation of the CCLicenseField model object and acts as a data sub object + * for the SubmissionCCLicenseRest class. + * Refer to {@link org.dspace.license.CCLicenseField} for explanation of the properties + */ +public class SubmissionCCLicenseFieldRest { + + private String id; + + private String label; + + private String description; + + private List enums; + + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getLabel() { + return label; + } + + public void setLabel(final String label) { + this.label = label; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public List getEnums() { + return enums; + } + + public void setEnums(final List enums) { + this.enums = enums; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java new file mode 100644 index 000000000000..396e014531dd --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java @@ -0,0 +1,73 @@ +/** + * 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 java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dspace.app.rest.RestResourceController; + +/** + * This class is the REST representation of the CCLicense model object and acts as a data object + * * for the SubmissionCCLicenseResource class. + * Refer to {@link org.dspace.license.CCLicense} for explanation of the properties + */ +public class SubmissionCCLicenseRest extends BaseObjectRest { + public static final String NAME = "submissioncclicense"; + + public static final String CATEGORY = RestAddressableModel.CONFIGURATION; + + private String id; + + private String name; + + private List fields; + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public List getFields() { + return fields; + } + + public void setFields(final List fields) { + this.fields = fields; + } + + @JsonIgnore + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } + + @Override + @JsonIgnore + public Class getController() { + return RestResourceController.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseResource.java new file mode 100644 index 000000000000..fb041d28271c --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseResource.java @@ -0,0 +1,23 @@ +/** + * 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.SubmissionCCLicenseRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * CCLicense HAL Resource. This resource adds the data from the REST object together with embedded objects + * and a set of links if applicable + */ +@RelNameDSpaceResource(SubmissionCCLicenseRest.NAME) +public class SubmissionCCLicenseResource extends DSpaceResource { + public SubmissionCCLicenseResource(SubmissionCCLicenseRest submissionCCLicenseRest, Utils utils) { + super(submissionCCLicenseRest, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java new file mode 100644 index 000000000000..d3c5236141d0 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java @@ -0,0 +1,44 @@ +/** + * 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.util.List; + +import org.dspace.app.rest.model.SubmissionCCLicenseRest; +import org.dspace.core.Context; +import org.dspace.license.CCLicense; +import org.dspace.license.service.CreativeCommonsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +/** + * This is the repository that is responsible to manage CCLicense Rest objects + */ +@Component(SubmissionCCLicenseRest.CATEGORY + "." + SubmissionCCLicenseRest.NAME) +public class SubmissionCCLicenseRestRepository extends DSpaceRestRepository { + + @Autowired + protected CreativeCommonsService creativeCommonsService; + + + public SubmissionCCLicenseRest findOne(final Context context, final String s) { + return null; + } + + public Page findAll(final Context context, final Pageable pageable) { + + List allCCLicenses = creativeCommonsService.findAllCCLicenses(); + return converter.toRestPage(utils.getPage(allCCLicenses, pageable), utils.obtainProjection()); + } + + public Class getDomainClass() { + return null; + } +} diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/core-services-mock.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/core-services-mock.xml index ff13e7f6b48a..8010d3e5d6e0 100644 --- a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/core-services-mock.xml +++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/core-services-mock.xml @@ -6,4 +6,5 @@ + diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java new file mode 100644 index 000000000000..0cf4f2b387ec --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java @@ -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; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.dspace.app.rest.matcher.SubmissionCCLicenseMatcher; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.hamcrest.Matchers; +import org.junit.Test; + +/** + * Class to the methods from the SubmissionCCLicenseRestRepository + * Since the CC Licenses are obtained from the CC License API, a mock service has been implemented + * This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the + * CC License API. + * Refer to {@link org.dspace.license.MockCCLicenseConnectorServiceImpl} for more information + */ +public class SubmissionCCLicenseRestRepositoryIT extends AbstractControllerIntegrationTest { + + + /** + * Test the findAll method form the SubmissionCCLicenseRestRepository + * @throws Exception + */ + @Test + public void findAllTest() throws Exception { + + getClient().perform(get("/api/config/submissioncclicenses")) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded.submissioncclicenses", Matchers.containsInAnyOrder( + SubmissionCCLicenseMatcher.matchLicenseEntry(1, new int[]{3, 2, 3}), + SubmissionCCLicenseMatcher.matchLicenseEntry(2, new int[]{2}), + SubmissionCCLicenseMatcher.matchLicenseEntry(3, new int[]{}) + ))); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SubmissionCCLicenseMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SubmissionCCLicenseMatcher.java new file mode 100644 index 000000000000..cdf0470b519c --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/SubmissionCCLicenseMatcher.java @@ -0,0 +1,82 @@ +/** + * 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.matcher; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; + +import java.util.LinkedList; +import java.util.List; + +import org.hamcrest.Matcher; + +public class SubmissionCCLicenseMatcher { + + private SubmissionCCLicenseMatcher() { + } + + public static Matcher matchLicenseEntry(int count, int[] amountOfFieldsAndEnums) { + return allOf( + matchLicenseProperties(count), + matchFields(count, amountOfFieldsAndEnums) + ); + } + + private static Matcher matchFields(int count, int[] amountOfFieldsAndEnums) { + List> matchers = new LinkedList<>(); + for (int index = 0; index < amountOfFieldsAndEnums.length; index++) { + matchers.add(matchField(count, index, amountOfFieldsAndEnums[index])); + } + return hasJsonPath("$.fields", containsInAnyOrder(matchers)); + } + + private static Matcher matchField(int count, int fieldIndex, int amountOfEnums) { + return allOf( + matchLicenseFieldProperties(count, fieldIndex), + matchEnums(count, fieldIndex, amountOfEnums) + ); + + } + + private static Matcher matchEnums(int count, int fieldIndex, int amountOfEnums) { + List> matchers = new LinkedList<>(); + for (int index = 0; index < amountOfEnums; index++) { + matchers.add(matchLicenseFieldEnumProperties(count, fieldIndex, index)); + } +// return hasJsonPath("$.enums"); + return hasJsonPath("$.enums", containsInAnyOrder(matchers)); + } + + + public static Matcher matchLicenseProperties(int count) { + return allOf( + hasJsonPath("$.id", is("license" + count)), + hasJsonPath("$.name", is("License " + count + " - Name")) + ); + } + + public static Matcher matchLicenseFieldProperties(int count, int fieldIndex) { + return allOf( + hasJsonPath("$.id", is("license" + count + "-field" + fieldIndex)), + hasJsonPath("$.label", is("License " + count + " - Field " + fieldIndex + " - Label")), + hasJsonPath("$.description", is("License " + count + " - Field " + fieldIndex + " - Description")) + ); + } + + public static Matcher matchLicenseFieldEnumProperties(int count, int fieldIndex, int enumIndex) { + return allOf( + hasJsonPath("$.id", is("license" + count + "-field" + fieldIndex + "-enum" + enumIndex)), + hasJsonPath("$.label", + is("License " + count + " - Field " + fieldIndex + " - Enum " + enumIndex + " - Label")), + hasJsonPath("$.description", + is("License " + count + " - Field " + fieldIndex + " - Enum " + enumIndex + " - " + "Description")) + ); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java new file mode 100644 index 000000000000..54219bbaaaf1 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -0,0 +1,72 @@ +/** + * 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.license; + +import java.util.LinkedList; +import java.util.List; + +/** + * Mock implementation for the Creative commons license connector service. + * This class will return a structure of CC Licenses similar to the CC License API but without having to contact it + */ +public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorServiceImpl { + + /** + * Retrieves mock CC Licenses for the provided language + * @param language - the language + * @return a list of mocked licenses + */ + public List retrieveLicenses(String language) { + List ccLicenses = new LinkedList<>(); + ccLicenses.add(createMockLicense(1, new int[]{3, 2, 3})); + ccLicenses.add(createMockLicense(2, new int[]{2})); + ccLicenses.add(createMockLicense(3, new int[]{})); + + return ccLicenses; + } + + private CCLicense createMockLicense(int count, int[] amountOfFieldsAndEnums) { + String licenseId = "license" + count; + String licenseName = "License " + count + " - Name"; + List mockLicenseFields = createMockLicenseFields(count, amountOfFieldsAndEnums); + return new CCLicense(licenseId, licenseName, mockLicenseFields); + } + + private List createMockLicenseFields(int count, int[] amountOfFieldsAndEnums) { + List ccLicenseFields = new LinkedList<>(); + for (int index = 0; index < amountOfFieldsAndEnums.length; index++) { + String licenseFieldId = "license" + count + "-field" + index; + String licenseFieldLabel = "License " + count + " - Field " + index + " - Label"; + String licenseFieldDescription = "License " + count + " - Field " + index + " - Description"; + List mockLicenseFields = createMockLicenseFields(count, + index, + amountOfFieldsAndEnums[index]); + ccLicenseFields.add(new CCLicenseField(licenseFieldId, + licenseFieldLabel, + licenseFieldDescription, + mockLicenseFields)); + + } + + return ccLicenseFields; + } + + private List createMockLicenseFields(int count, int index, int amountOfEnums) { + List ccLicenseFieldEnumList = new LinkedList<>(); + for (int i = 0; i < amountOfEnums; i++) { + String enumId = "license" + count + "-field" + index + "-enum" + i; + String enumLabel = "License " + count + " - Field " + index + " - Enum " + i + " - Label"; + String enumDescription = "License " + count + " - Field " + index + " - Enum " + i + " - " + + "Description"; + ccLicenseFieldEnumList.add(new CCLicenseFieldEnum(enumId, enumLabel, enumDescription)); + } + return ccLicenseFieldEnumList; + + } + +} diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index 316f81fd18a2..64d8cc7147b7 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -100,6 +100,7 @@ + From 18c8efd95106f02ec12fe6edc9fb2abaf3735a0e Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 9 Apr 2020 14:26:39 +0200 Subject: [PATCH 06/73] 70332: Implement feedback --- .../CCLicenseConnectorServiceImpl.java | 62 ++++++++++--------- .../SubmissionCCLicenseConverter.java | 1 + .../SubmissionCCLicenseFieldConverter.java | 1 + ...SubmissionCCLicenseFieldEnumConverter.java | 1 + .../SubmissionCCLicenseRestRepository.java | 6 +- 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index edc99346941a..a0dbd075cf9d 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -59,19 +59,21 @@ public void afterPropertiesSet() throws Exception { /** * Retrieves the CC Licenses for the provided language from the CC License API + * * @param language - the language to retrieve the licenses for * @return a list of licenses obtained for the provided languages */ public List retrieveLicenses(String language) { String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); - HttpGet httpGet = new HttpGet(ccLicenseUrl + "/?locale=" + language); + String uri = ccLicenseUrl + "/?locale=" + language; + HttpGet httpGet = new HttpGet(uri); List licenses; try (CloseableHttpResponse response = client.execute(httpGet)) { licenses = retrieveLicenses(response); } catch (JDOMException | JaxenException | IOException e) { - log.error(e); + log.error("Error while retrieving the license details using url: " + uri, e); licenses = Collections.emptyList(); } @@ -79,12 +81,13 @@ public List retrieveLicenses(String language) { for (String license : licenses) { - HttpGet licenseHttpGet = new HttpGet(ccLicenseUrl + "/license/" + license); + String licenseUri = ccLicenseUrl + "/license/" + license; + HttpGet licenseHttpGet = new HttpGet(licenseUri); try (CloseableHttpResponse response = client.execute(licenseHttpGet)) { - CCLicense ccLicense = retrieveLicenseObject(response); + CCLicense ccLicense = retrieveLicenseObject(license, response); ccLicenses.add(ccLicense); } catch (JaxenException | JDOMException | IOException e) { - log.error(e); + log.error("Error while retrieving the license details using url: " + licenseUri, e); } } @@ -94,6 +97,7 @@ public List retrieveLicenses(String language) { /** * Retrieve the list of licenses from the response from the CC License API and remove the licenses configured * to be excluded + * * @param response The response from the API * @return a list of license identifiers for which details need to be retrieved * @throws IOException @@ -111,14 +115,16 @@ private List retrieveLicenses(CloseableHttpResponse response) JDOMXPath licenseClassXpath = new JDOMXPath("//licenses/license"); - InputSource is = new InputSource(new StringReader(responseString)); - org.jdom.Document classDoc = this.parser.build(is); + try (StringReader stringReader = new StringReader(responseString)) { + InputSource is = new InputSource(stringReader); + org.jdom.Document classDoc = this.parser.build(is); - List elements = licenseClassXpath.selectNodes(classDoc); - for (Element element : elements) { - String licenseId = getSingleNodeValue(element, "@id"); - if (StringUtils.isNotBlank(licenseId) && !ArrayUtils.contains(excludedLicenses, licenseId)) { - domains.add(licenseId); + List elements = licenseClassXpath.selectNodes(classDoc); + for (Element element : elements) { + String licenseId = getSingleNodeValue(element, "@id"); + if (StringUtils.isNotBlank(licenseId) && !ArrayUtils.contains(excludedLicenses, licenseId)) { + domains.add(licenseId); + } } } @@ -128,13 +134,15 @@ private List retrieveLicenses(CloseableHttpResponse response) /** * Parse the response for a single CC License and return the corresponding CC License Object - * @param response for a specific CC License response + * + * @param licenseId the license id of the CC License to retrieve + * @param response for a specific CC License response * @return the corresponding CC License Object * @throws IOException * @throws JaxenException * @throws JDOMException */ - private CCLicense retrieveLicenseObject(CloseableHttpResponse response) + private CCLicense retrieveLicenseObject(final String licenseId, CloseableHttpResponse response) throws IOException, JaxenException, JDOMException { String responseString = EntityUtils.toString(response.getEntity()); @@ -144,26 +152,24 @@ private CCLicense retrieveLicenseObject(CloseableHttpResponse response) JDOMXPath licenseFieldXpath = new JDOMXPath("field"); - InputSource is; + try (StringReader stringReader = new StringReader(responseString)) { + InputSource is = new InputSource(stringReader); - is = new InputSource(new StringReader(responseString)); + org.jdom.Document classDoc = this.parser.build(is); - org.jdom.Document classDoc = this.parser.build(is); + Object element = licenseClassXpath.selectSingleNode(classDoc); + String licenseLabel = getSingleNodeValue(element, "label"); - Object element = licenseClassXpath.selectSingleNode(classDoc); - String licenseId = getSingleNodeValue(element, "@id"); - String licenseLabel = getSingleNodeValue(element, "label"); + List ccLicenseFields = new LinkedList<>(); - List ccLicenseFields = new LinkedList<>(); + List licenseFields = licenseFieldXpath.selectNodes(element); + for (Element licenseField : licenseFields) { + CCLicenseField ccLicenseField = parseLicenseField(licenseField); + ccLicenseFields.add(ccLicenseField); + } - List licenseFields = licenseFieldXpath.selectNodes(element); - for (Element licenseField : licenseFields) { - CCLicenseField ccLicenseField = parseLicenseField(licenseField); - ccLicenseFields.add(ccLicenseField); + return new CCLicense(licenseId, licenseLabel, ccLicenseFields); } - - - return new CCLicense(licenseId, licenseLabel, ccLicenseFields); } private CCLicenseField parseLicenseField(final Element licenseField) throws JaxenException { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java index bd7e582e75b2..bf6b92a6180e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseConverter.java @@ -52,6 +52,7 @@ public SubmissionCCLicenseRest convert(final CCLicense modelObject, final Projec return submissionCCLicenseRest; } + @Override public Class getModelClass() { return CCLicense.class; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java index b0418ef4b586..782056dc1c85 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldConverter.java @@ -54,6 +54,7 @@ public SubmissionCCLicenseFieldRest convert(final CCLicenseField modelObject, fi return submissionCCLicenseFieldRest; } + @Override public Class getModelClass() { return CCLicenseField.class; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java index 85ed1c45e40d..6c8993905fec 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseFieldEnumConverter.java @@ -38,6 +38,7 @@ public SubmissionCCLicenseFieldEnumRest convert(final CCLicenseFieldEnum modelOb return submissionCCLicenseFieldEnumRest; } + @Override public Class getModelClass() { return CCLicenseFieldEnum.class; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java index d3c5236141d0..d8ee6bec15b7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java @@ -27,18 +27,20 @@ public class SubmissionCCLicenseRestRepository extends DSpaceRestRepository findAll(final Context context, final Pageable pageable) { List allCCLicenses = creativeCommonsService.findAllCCLicenses(); return converter.toRestPage(utils.getPage(allCCLicenses, pageable), utils.obtainProjection()); } + @Override public Class getDomainClass() { - return null; + return SubmissionCCLicenseRest.class; } } From f51a12d0106281d32885e1006d91f0c48110de05 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 10 Apr 2020 10:27:32 +0200 Subject: [PATCH 07/73] 70334: CC license (REST): Initial fineOne endpoint --- .../license/CCLicenseConnectorService.java | 7 ++- .../CCLicenseConnectorServiceImpl.java | 10 ++-- .../license/CreativeCommonsServiceImpl.java | 57 +++++++++++++++++-- .../service/CreativeCommonsService.java | 18 ++++++ .../MockCCLicenseConnectorServiceImpl.java | 18 ++++-- .../SubmissionCCLicenseRestRepository.java | 9 ++- .../SubmissionCCLicenseRestRepositoryIT.java | 16 ++++++ .../MockCCLicenseConnectorServiceImpl.java | 18 ++++-- 8 files changed, 127 insertions(+), 26 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java index 52bbf39cc7b4..caf079d230ee 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java @@ -7,7 +7,7 @@ */ package org.dspace.license; -import java.util.List; +import java.util.Map; /** * Service interface class for the Creative commons license connector service. @@ -18,9 +18,10 @@ public interface CCLicenseConnectorService { /** * Retrieves the CC Licenses for the provided language from the CC License API + * * @param language - the language to retrieve the licenses for - * @return a list of licenses obtained for the provided languages + * @return a map of licenses with the id and the license for the provided language */ - public List retrieveLicenses(String language); + public Map retrieveLicenses(String language); } diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index a0dbd075cf9d..4cb6d74b0f41 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -10,8 +10,10 @@ import java.io.IOException; import java.io.StringReader; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -61,9 +63,9 @@ public void afterPropertiesSet() throws Exception { * Retrieves the CC Licenses for the provided language from the CC License API * * @param language - the language to retrieve the licenses for - * @return a list of licenses obtained for the provided languages + * @return a map of licenses with the id and the license for the provided language */ - public List retrieveLicenses(String language) { + public Map retrieveLicenses(String language) { String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); String uri = ccLicenseUrl + "/?locale=" + language; @@ -77,7 +79,7 @@ public List retrieveLicenses(String language) { licenses = Collections.emptyList(); } - List ccLicenses = new LinkedList<>(); + Map ccLicenses = new HashMap<>(); for (String license : licenses) { @@ -85,7 +87,7 @@ public List retrieveLicenses(String language) { HttpGet licenseHttpGet = new HttpGet(licenseUri); try (CloseableHttpResponse response = client.execute(licenseHttpGet)) { CCLicense ccLicense = retrieveLicenseObject(license, response); - ccLicenses.add(ccLicense); + ccLicenses.put(ccLicense.getLicenseId(), ccLicense); } catch (JaxenException | JDOMException | IOException e) { log.error("Error while retrieving the license details using url: " + licenseUri, e); } diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index fed51a9f0a6c..1c08d297dcb5 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -13,7 +13,10 @@ import java.io.InputStream; import java.io.StringWriter; import java.sql.SQLException; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; @@ -87,7 +90,7 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - private List ccLicenses; + private Map> ccLicenses; protected CreativeCommonsServiceImpl() { @@ -105,6 +108,9 @@ public void afterPropertiesSet() throws Exception { System.setProperty("http.proxyPort", proxyPort); } + ccLicenses = new HashMap<>(); + + try { templates = TransformerFactory.newInstance().newTemplates( new StreamSource(CreativeCommonsServiceImpl.class @@ -389,6 +395,7 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, /** * Find all CC Licenses using the default language found in the configuration + * * @return A list of available CC Licenses */ public List findAllCCLicenses() { @@ -398,15 +405,55 @@ public List findAllCCLicenses() { /** * Find all CC Licenses for the provided language - * @param language - the language for which to find the CC Licenses + * + * @param language - the language for which to find the CC Licenses * @return A list of available CC Licenses for the provided language */ public List findAllCCLicenses(String language) { - if (ccLicenses == null || ccLicenses.isEmpty()) { - ccLicenses = ccLicenseConnectorService.retrieveLicenses(language); + if (!ccLicenses.containsKey(language)) { + initLicenses(language); + } + return new LinkedList<>(ccLicenses.get(language).values()); + } + + /** + * Find the CC License corresponding to the provided ID using the default language found in the configuration + * + * @param id - the ID of the license to be found + * @return the corresponding license if found or null when not found + */ + public CCLicense findOne(String id) { + String language = configurationService.getProperty("cc.license.locale", "en"); + return findOne(id, language); + } + + /** + * Find the CC License corresponding to the provided ID and provided language + * + * @param id - the ID of the license to be found + * @param language - the language for which to find the CC License + * @return the corresponding license if found or null when not found + */ + public CCLicense findOne(String id, String language) { + if (!ccLicenses.containsKey(language)) { + initLicenses(language); + } + Map licenseMap = ccLicenses.get(language); + if (licenseMap.containsKey(id)) { + return licenseMap.get(id); } - return ccLicenses; + return null; + } + + /** + * Retrieves the licenses for a specific language and cache them in this service + * + * @param language - the language for which to find the CC Licenses + */ + private void initLicenses(final String language) { + Map licenseMap = ccLicenseConnectorService.retrieveLicenses(language); + ccLicenses.put(language, licenseMap); } } diff --git a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java index d25f02ff7b25..edb9410f7e84 100644 --- a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java +++ b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java @@ -166,4 +166,22 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, * @return A list of available CC Licenses for the provided language */ public List findAllCCLicenses(String language); + + /** + * Find the CC License corresponding to the provided ID using the default language found in the configuration + * + * @param id - the ID of the license to be found + * @return the corresponding license if found or null when not found + */ + public CCLicense findOne(String id); + + /** + * Find the CC License corresponding to the provided ID and provided language + * + * @param id - the ID of the license to be found + * @param language - the language for which to find the CC License + * @return the corresponding license if found or null when not found + */ + public CCLicense findOne(String id, String language); + } diff --git a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index 54219bbaaaf1..df934312d110 100644 --- a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -7,8 +7,10 @@ */ package org.dspace.license; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; /** * Mock implementation for the Creative commons license connector service. @@ -19,13 +21,17 @@ public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorService /** * Retrieves mock CC Licenses for the provided language * @param language - the language - * @return a list of mocked licenses + * @return a map of mocked licenses with the id and the license */ - public List retrieveLicenses(String language) { - List ccLicenses = new LinkedList<>(); - ccLicenses.add(createMockLicense(1, new int[]{3, 2, 3})); - ccLicenses.add(createMockLicense(2, new int[]{2})); - ccLicenses.add(createMockLicense(3, new int[]{})); + public Map retrieveLicenses(String language) { + Map ccLicenses = new HashMap<>(); + CCLicense mockLicense1 = createMockLicense(1, new int[]{3, 2, 3}); + CCLicense mockLicense2 = createMockLicense(2, new int[]{2}); + CCLicense mockLicense3 = createMockLicense(3, new int[]{}); + + ccLicenses.put(mockLicense1.getLicenseId(), mockLicense1); + ccLicenses.put(mockLicense2.getLicenseId(), mockLicense2); + ccLicenses.put(mockLicense3.getLicenseId(), mockLicense3); return ccLicenses; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java index d8ee6bec15b7..88ba43863941 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java @@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.stereotype.Component; /** @@ -28,8 +29,12 @@ public class SubmissionCCLicenseRestRepository extends DSpaceRestRepository retrieveLicenses(String language) { - List ccLicenses = new LinkedList<>(); - ccLicenses.add(createMockLicense(1, new int[]{3, 2, 3})); - ccLicenses.add(createMockLicense(2, new int[]{2})); - ccLicenses.add(createMockLicense(3, new int[]{})); + public Map retrieveLicenses(String language) { + Map ccLicenses = new HashMap<>(); + CCLicense mockLicense1 = createMockLicense(1, new int[]{3, 2, 3}); + CCLicense mockLicense2 = createMockLicense(2, new int[]{2}); + CCLicense mockLicense3 = createMockLicense(3, new int[]{}); + + ccLicenses.put(mockLicense1.getLicenseId(), mockLicense1); + ccLicenses.put(mockLicense2.getLicenseId(), mockLicense2); + ccLicenses.put(mockLicense3.getLicenseId(), mockLicense3); return ccLicenses; } From 44d3d2bcc3c39c57cea29ed1ae9b158e0769f2be Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 14 Apr 2020 11:58:50 +0200 Subject: [PATCH 08/73] [Task 70273] added POST endpoint for new registration creation and IT --- .../dspace/eperson/AccountServiceImpl.java | 6 ++ .../app/rest/RegistrationRestController.java | 86 +++++++++++++++++++ .../app/rest/model/RegistrationRest.java | 72 ++++++++++++++++ .../rest/RegistrationRestControllerIT.java | 75 ++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java diff --git a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java index e00a9568e379..9282fc116e82 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java @@ -22,6 +22,7 @@ import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.RegistrationDataService; +import org.dspace.services.ConfigurationService; import org.springframework.beans.factory.annotation.Autowired; /** @@ -47,6 +48,8 @@ public class AccountServiceImpl implements AccountService { protected EPersonService ePersonService; @Autowired(required = true) protected RegistrationDataService registrationDataService; + @Autowired + private ConfigurationService configurationService; protected AccountServiceImpl() { @@ -67,6 +70,9 @@ protected AccountServiceImpl() { public void sendRegistrationInfo(Context context, String email) throws SQLException, IOException, MessagingException, AuthorizeException { + if (!configurationService.getBooleanProperty("user.registration", true)) { + throw new IllegalStateException("The user.registration parameter was set to false"); + } sendInfo(context, email, true, true); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java new file mode 100644 index 000000000000..f11bed240f94 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java @@ -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.rest; + +import java.io.IOException; +import java.sql.SQLException; +import javax.mail.MessagingException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.RegistrationRest; +import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.authorize.AuthorizeException; +import org.dspace.core.Context; +import org.dspace.eperson.service.AccountService; +import org.dspace.eperson.service.EPersonService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.ControllerUtils; +import org.springframework.hateoas.ResourceSupport; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/" + RegistrationRest.CATEGORY + "/" + RegistrationRest.NAME_PLURAL) +public class RegistrationRestController { + +// @Autowired +// private AuthorizationFeatureService authorizationFeatureService; +// +// @Autowired +// private SiteService siteService; +// +// @Autowired +// private ConverterService converterService; + + @Autowired + private AccountService accountService; + + @Autowired + private EPersonService ePersonService; + + @RequestMapping(method = RequestMethod.POST) + public ResponseEntity register(HttpServletRequest request, HttpServletResponse response) + throws SQLException, IOException, MessagingException, AuthorizeException { + + Context context = ContextUtil.obtainContext(request); +// AuthorizationFeature epersonRegistration = authorizationFeatureService.find("epersonRegistration"); +// Site site = siteService.findSite(context); +// SiteRest siteRest = converterService.toRest(site, Projection.DEFAULT); +// if (!authorizationFeatureService.isAuthorized(context, epersonRegistration, siteRest)) { +// throw new AccessDeniedException("Registration is disabled, you are not authorized to create +// a new Authorization"); +// } + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest; + try { + ServletInputStream input = request.getInputStream(); + registrationRest = mapper.readValue(input, RegistrationRest.class); + } catch (IOException e1) { + throw new UnprocessableEntityException("Error parsing request body.", e1); + } + if (StringUtils.isBlank(registrationRest.getEmail())) { + throw new UnprocessableEntityException("The email cannot be omitted from the Registration endpoint"); + } + if (ePersonService.findByEmail(context, registrationRest.getEmail()) != null) { + accountService.sendForgotPasswordInfo(context, registrationRest.getEmail()); + } else { + accountService.sendRegistrationInfo(context, registrationRest.getEmail()); + } + context.complete(); + return ControllerUtils.toEmptyResponse(HttpStatus.CREATED); + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java new file mode 100644 index 000000000000..0d7f2e30bde9 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java @@ -0,0 +1,72 @@ +/** + * 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 java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dspace.app.rest.RegistrationRestController; + + +public class RegistrationRest extends BaseObjectRest { + + public static final String NAME = "registration"; + public static final String NAME_PLURAL = "registrations"; + public static final String CATEGORY = EPERSON; + + private String email; + private UUID user; + + /** + * Generic getter for the email + * @return the email value of this RegisterRest + */ + public String getEmail() { + return email; + } + + /** + * Generic setter for the email + * @param email The email to be set on this RegisterRest + */ + public void setEmail(String email) { + this.email = email; + } + + /** + * Generic getter for the user + * @return the user value of this RegisterRest + */ + public UUID getUser() { + return user; + } + + /** + * Generic setter for the user + * @param user The user to be set on this RegisterRest + */ + public void setUser(UUID user) { + this.user = user; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RegistrationRestController.class; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java new file mode 100644 index 000000000000..16135d2c68fa --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -0,0 +1,75 @@ +/** + * 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; + +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.model.RegistrationRest; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.eperson.RegistrationData; +import org.dspace.eperson.dao.RegistrationDataDAO; +import org.dspace.services.ConfigurationService; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class RegistrationRestControllerIT extends AbstractControllerIntegrationTest { + + @Autowired + private RegistrationDataDAO registrationDataDAO; + + @Autowired + private ConfigurationService configurationService; + + @Test + public void registrationFlowTest() throws Exception { + List registrationData = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationData.isEmpty()); + + String token = getAuthToken(eperson.getEmail(), password); + String t = ";;"; + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(eperson.getEmail()); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationData = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationData.size() == 1); + assertTrue(StringUtils.equalsIgnoreCase(registrationData.get(0).getEmail(), eperson.getEmail())); + + String newEmail = "newEPersonTest@gmail.com"; + registrationRest.setEmail(newEmail); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationData = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationData.size() == 2); + assertTrue(StringUtils.equalsIgnoreCase(registrationData.get(0).getEmail(), newEmail) || + StringUtils.equalsIgnoreCase(registrationData.get(1).getEmail(), newEmail)); + configurationService.setProperty("user.registration", false); + + newEmail = "newEPersonTestTwo@gmail.com"; + registrationRest.setEmail(newEmail); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().is(500)); + + assertTrue(registrationData.size() == 2); + assertTrue(!StringUtils.equalsIgnoreCase(registrationData.get(0).getEmail(), newEmail) && + !StringUtils.equalsIgnoreCase(registrationData.get(1).getEmail(), newEmail)); + } +} From 794ee9fb9fc723c787709a35ba172ce522ca133c Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 14 Apr 2020 15:59:21 +0200 Subject: [PATCH 09/73] 70337: Search CC License --- .../license/CCLicenseConnectorService.java | 12 ++ .../CCLicenseConnectorServiceImpl.java | 94 +++++++++++++ .../license/CreativeCommonsServiceImpl.java | 131 +++++++++++++++++- .../service/CreativeCommonsService.java | 87 ++++++++++-- .../MockCCLicenseConnectorServiceImpl.java | 15 ++ .../SubmissionCCLicenseSearchController.java | 95 +++++++++++++ .../rest/model/SubmissionCCLicenseRest.java | 1 + .../SubmissionCCLicenseRestRepositoryIT.java | 1 + ...SubmissionCCLicenseSearchControllerIT.java | 68 +++++++++ .../MockCCLicenseConnectorServiceImpl.java | 14 ++ 10 files changed, 501 insertions(+), 17 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java index caf079d230ee..48ed5f7200d3 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java @@ -24,4 +24,16 @@ public interface CCLicenseConnectorService { */ public Map retrieveLicenses(String language); + /** + * Retrieve the CC License URI based on the provided license id, language and answers to the field questions from + * the CC License API + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param answerMap - the answers to the different field questions + * @return the CC License URI + */ + public String retrieveRightsByQuestion(String licenseId, + String language, + Map answerMap); + } diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 4cb6d74b0f41..7fedc7e2e1f3 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.StringReader; +import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -17,8 +18,11 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; @@ -45,6 +49,15 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, private CloseableHttpClient client; private SAXBuilder parser = new SAXBuilder(); + private String postArgument = "answers"; + private String postAnswerFormat = + " " + + "{1}" + + "" + + "{2}" + + "" + + ""; + @Autowired private ConfigurationService configurationService; @@ -221,4 +234,85 @@ private String getSingleNodeValue(final Object t, String query) throws JaxenExce return getNodeValue(singleNode); } + /** + * Retrieve the CC License URI based on the provided license id, language and answers to the field questions from + * the CC License API + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param answerMap - the answers to the different field questions + * @return the CC License URI + */ + public String retrieveRightsByQuestion(String licenseId, + String language, + Map answerMap) { + + String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); + + + HttpPost httpPost = new HttpPost(ccLicenseUrl + "/license/" + licenseId + "/issue"); + + + String answers = createAnswerString(answerMap); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + String text = MessageFormat.format(postAnswerFormat, licenseId, language, answers); + builder.addTextBody(postArgument, text); + + HttpEntity multipart = builder.build(); + + httpPost.setEntity(multipart); + + try (CloseableHttpResponse response = client.execute(httpPost)) { + return retrieveLicenseUri(response); + } catch (JDOMException | JaxenException | IOException e) { + log.error("Error while retrieving the license uri for license : " + licenseId + " with answers " + + answerMap.toString(), e); + } + return null; + } + + /** + * Parse the response for the CC License URI request and return the corresponding CC License URI + * + * @param response for a specific CC License URI response + * @return the corresponding CC License URI as a string + * @throws IOException + * @throws JaxenException + * @throws JDOMException + */ + private String retrieveLicenseUri(final CloseableHttpResponse response) + throws IOException, JaxenException, JDOMException { + + String responseString = EntityUtils.toString(response.getEntity()); + JDOMXPath licenseClassXpath = new JDOMXPath("//result/license-uri"); + + + try (StringReader stringReader = new StringReader(responseString)) { + InputSource is = new InputSource(stringReader); + org.jdom.Document classDoc = this.parser.build(is); + + Object node = licenseClassXpath.selectSingleNode(classDoc); + String nodeValue = getNodeValue(node); + + if (StringUtils.isNotBlank(nodeValue)) { + return nodeValue; + } + } + return null; + } + + private String createAnswerString(final Map parameterMap) { + StringBuilder sb = new StringBuilder(); + for (String key : parameterMap.keySet()) { + sb.append("<"); + sb.append(key); + sb.append(">"); + sb.append(parameterMap.get(key)); + sb.append(""); + } + return sb.toString(); + } + + } diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index 1c08d297dcb5..c0c190307b08 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -90,6 +90,9 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); + private String defaultLanguage; + + private Map> ccLicenses; protected CreativeCommonsServiceImpl() { @@ -109,7 +112,7 @@ public void afterPropertiesSet() throws Exception { } ccLicenses = new HashMap<>(); - + defaultLanguage = configurationService.getProperty("cc.license.locale", "en"); try { templates = TransformerFactory.newInstance().newTemplates( @@ -399,8 +402,7 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, * @return A list of available CC Licenses */ public List findAllCCLicenses() { - String language = configurationService.getProperty("cc.license.locale", "en"); - return findAllCCLicenses(language); + return findAllCCLicenses(defaultLanguage); } /** @@ -424,8 +426,7 @@ public List findAllCCLicenses(String language) { * @return the corresponding license if found or null when not found */ public CCLicense findOne(String id) { - String language = configurationService.getProperty("cc.license.locale", "en"); - return findOne(id, language); + return findOne(id, defaultLanguage); } /** @@ -456,4 +457,124 @@ private void initLicenses(final String language) { ccLicenses.put(language, licenseMap); } + /** + * Retrieve the CC License URI for the provided license ID, based on the provided answers, using the default + * language found in the configuration + * + * @param licenseId - the ID of the license + * @param answerMap - the answers to the different field questions + * @return the corresponding license URI + */ + public String retrieveLicenseUri(String licenseId, Map answerMap) { + return retrieveLicenseUri(licenseId, defaultLanguage, answerMap); + + } + + /** + * Retrieve the CC License URI for the provided license ID and language based on the provided answers + * + * @param licenseId - the ID of the license + * @param language - the language for which to find the CC License URI + * @param answerMap - the answers to the different field questions + * @return the corresponding license URI + */ + public String retrieveLicenseUri(String licenseId, String language, Map answerMap) { + return ccLicenseConnectorService.retrieveRightsByQuestion(licenseId, language, answerMap); + + } + + /** + * Verify whether the answer map contains a valid response to all field questions and no answers that don't have a + * corresponding question in the license, using the default language found in the config to check the license + * + * @param licenseId - the ID of the license + * @param fullAnswerMap - the answers to the different field questions + * @return whether the information is valid + */ + public boolean verifyLicenseInformation(String licenseId, Map fullAnswerMap) { + return verifyLicenseInformation(licenseId, defaultLanguage, fullAnswerMap); + } + + /** + * Verify whether the answer map contains a valid response to all field questions and no answers that don't have a + * corresponding question in the license, using the provided language to check the license + * + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param fullAnswerMap - the answers to the different field questions + * @return whether the information is valid + */ + public boolean verifyLicenseInformation(String licenseId, String language, Map fullAnswerMap) { + CCLicense ccLicense = findOne(licenseId, language); + + List ccLicenseFieldList = ccLicense.getCcLicenseFieldList(); + + for (String field : fullAnswerMap.keySet()) { + CCLicenseField ccLicenseField = findCCLicenseField(field, ccLicenseFieldList); + if (ccLicenseField == null) { + return false; + } + if (!containsAnswerEnum(fullAnswerMap.get(field), ccLicenseField)) { + return false; + } + } + return true; + } + + /** + * Retrieve the full answer map containing empty values when an answer for a field was not provided in the + * answerMap, using the default language found in the configuration + * + * @param licenseId - the ID of the license + * @param answerMap - the answers to the different field questions + * @return the answerMap supplemented with all other license fields with a blank answer + */ + public Map retrieveFullAnswerMap(String licenseId, Map answerMap) { + return retrieveFullAnswerMap(licenseId, defaultLanguage, answerMap); + } + + /** + * Retrieve the full answer map for a provided language, containing empty values when an answer for a field was not + * provided in the answerMap. + * + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param answerMap - the answers to the different field questions + * @return the answerMap supplemented with all other license fields with a blank answer for the provided language + */ + public Map retrieveFullAnswerMap(String licenseId, String language, Map answerMap) { + CCLicense ccLicense = findOne(licenseId, language); + if (ccLicense == null) { + return null; + } + Map fullParamMap = new HashMap<>(answerMap); + List ccLicenseFieldList = ccLicense.getCcLicenseFieldList(); + for (CCLicenseField ccLicenseField : ccLicenseFieldList) { + if (!fullParamMap.containsKey(ccLicenseField.getId())) { + fullParamMap.put(ccLicenseField.getId(), ""); + } + } + return fullParamMap; + } + + private boolean containsAnswerEnum(final String enumAnswer, final CCLicenseField ccLicenseField) { + List fieldEnums = ccLicenseField.getFieldEnum(); + for (CCLicenseFieldEnum fieldEnum : fieldEnums) { + if (StringUtils.equals(fieldEnum.getId(), enumAnswer)) { + return true; + } + } + return false; + } + + private CCLicenseField findCCLicenseField(final String field, final List ccLicenseFieldList) { + for (CCLicenseField ccLicenseField : ccLicenseFieldList) { + if (StringUtils.equals(ccLicenseField.getId(), field)) { + return ccLicenseField; + } + } + + return null; + } + } diff --git a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java index edb9410f7e84..cda546246f14 100644 --- a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java +++ b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java @@ -11,6 +11,7 @@ import java.io.InputStream; import java.sql.SQLException; import java.util.List; +import java.util.Map; import org.dspace.authorize.AuthorizeException; import org.dspace.content.Bitstream; @@ -52,7 +53,7 @@ public interface CreativeCommonsService { * to perform a particular action. */ public void setLicenseRDF(Context context, Item item, String licenseRdf) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; /** @@ -74,19 +75,19 @@ public void setLicenseRDF(Context context, Item item, String licenseRdf) */ public void setLicense(Context context, Item item, InputStream licenseStm, String mimeType) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; public void removeLicense(Context context, Item item) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; public boolean hasLicense(Context context, Item item) - throws SQLException, IOException; + throws SQLException, IOException; public String getLicenseURL(Context context, Item item) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; public String getLicenseRDF(Context context, Item item) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; /** * Get Creative Commons license RDF, returning Bitstream object. @@ -99,7 +100,7 @@ public String getLicenseRDF(Context context, Item item) * to perform a particular action. */ public Bitstream getLicenseRdfBitstream(Item item) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; /** * Get Creative Commons license Text, returning Bitstream object. @@ -114,7 +115,7 @@ public Bitstream getLicenseRdfBitstream(Item item) * is no longer stored (see https://jira.duraspace.org/browse/DS-2604) */ public Bitstream getLicenseTextBitstream(Item item) - throws SQLException, IOException, AuthorizeException; + throws SQLException, IOException, AuthorizeException; /** * Get a few license-specific properties. We expect these to be cached at @@ -150,7 +151,7 @@ public Bitstream getLicenseTextBitstream(Item item) */ public void removeLicense(Context context, LicenseMetadataValue uriField, LicenseMetadataValue nameField, Item item) - throws AuthorizeException, IOException, SQLException; + throws AuthorizeException, IOException, SQLException; /** * Find all CC Licenses using the default language found in the configuration @@ -170,7 +171,7 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, /** * Find the CC License corresponding to the provided ID using the default language found in the configuration * - * @param id - the ID of the license to be found + * @param id - the ID of the license to be found * @return the corresponding license if found or null when not found */ public CCLicense findOne(String id); @@ -178,10 +179,72 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, /** * Find the CC License corresponding to the provided ID and provided language * - * @param id - the ID of the license to be found - * @param language - the language for which to find the CC License + * @param id - the ID of the license to be found + * @param language - the language for which to find the CC License * @return the corresponding license if found or null when not found */ public CCLicense findOne(String id, String language); + /** + * Retrieve the CC License URI for the provided license ID, based on the provided answers, using the default + * language found in the configuration + * + * @param licenseId - the ID of the license + * @param answerMap - the answers to the different field questions + * @return the corresponding license URI + */ + public String retrieveLicenseUri(String licenseId, Map answerMap); + + /** + * Retrieve the CC License URI for the provided license ID and language based on the provided answers + * + * @param licenseId - the ID of the license + * @param language - the language for which to find the CC License URI + * @param answerMap - the answers to the different field questions + * @return the corresponding license URI + */ + public String retrieveLicenseUri(String licenseId, String language, Map answerMap); + + /** + * Retrieve the full answer map containing empty values when an answer for a field was not provided in the + * answerMap, using the default language found in the configuration + * + * @param licenseId - the ID of the license + * @param answerMap - the answers to the different field questions + * @return the answerMap supplemented with all other license fields with a blank answer + */ + public Map retrieveFullAnswerMap(String licenseId, Map answerMap); + + /** + * Retrieve the full answer map for a provided language, containing empty values when an answer for a field was not + * provided in the answerMap. + * + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param answerMap - the answers to the different field questions + * @return the answerMap supplemented with all other license fields with a blank answer for the provided language + */ + public Map retrieveFullAnswerMap(String licenseId, String language, Map answerMap); + + /** + * Verify whether the answer map contains a valid response to all field questions and no answers that don't have a + * corresponding question in the license, using the default language found in the config to check the license + * + * @param licenseId - the ID of the license + * @param fullAnswerMap - the answers to the different field questions + * @return whether the information is valid + */ + public boolean verifyLicenseInformation(String licenseId, Map fullAnswerMap); + + /** + * Verify whether the answer map contains a valid response to all field questions and no answers that don't have a + * corresponding question in the license, using the provided language to check the license + * + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param fullAnswerMap - the answers to the different field questions + * @return whether the information is valid + */ + public boolean verifyLicenseInformation(String licenseId, String language, Map fullAnswerMap); + } diff --git a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index df934312d110..226e2aa77ba2 100644 --- a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -20,6 +20,7 @@ public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorService /** * Retrieves mock CC Licenses for the provided language + * * @param language - the language * @return a map of mocked licenses with the id and the license */ @@ -75,4 +76,18 @@ private List createMockLicenseFields(int count, int index, i } + /** + * Retrieve a mock CC License URI + * + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param answerMap - the answers to the different field questions + * @return the CC License URI + */ + public String retrieveRightsByQuestion(final String licenseId, + final String language, + final Map answerMap) { + + return "mock-license-uri"; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java new file mode 100644 index 000000000000..185fb7e8ebaf --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java @@ -0,0 +1,95 @@ +/** + * 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; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.SubmissionCCLicenseRest; +import org.dspace.app.rest.utils.Utils; +import org.dspace.license.service.CreativeCommonsService; +import org.dspace.services.RequestService; +import org.dspace.utils.DSpace; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * This controller is responsible for searching the CC License URI + */ +@RestController +@RequestMapping("/api/" + SubmissionCCLicenseRest.CATEGORY + "/" + SubmissionCCLicenseRest.PLURAL + "/search" + + "/rightsByQuestions") +public class SubmissionCCLicenseSearchController { + + private static final Logger log = LogManager.getLogger(); + + @Autowired + protected Utils utils; + + @Autowired + protected CreativeCommonsService creativeCommonsService; + + protected RequestService requestService = new DSpace().getRequestService(); + + /** + * Retrieves the CC License URI based on the license ID and answers in the field questions, provided as parameters + * to this request + * + * @return the CC License URI as a string + */ + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public String findByRightsByQuestions() { + ServletRequest servletRequest = requestService.getCurrentRequest() + .getServletRequest(); + Map requestParameterMap = servletRequest + .getParameterMap(); + Map parameterMap = new HashMap<>(); + String licenseId = servletRequest.getParameter("license"); + if (StringUtils.isBlank(licenseId)) { + throw new DSpaceBadRequestException( + "A \"license\" parameter needs to be provided."); + } + for (String parameter : requestParameterMap.keySet()) { + if (StringUtils.startsWith(parameter, "answer_")) { + String field = StringUtils.substringAfter(parameter, "answer_"); + String answer = ""; + if (requestParameterMap.get(parameter).length > 0) { + answer = requestParameterMap.get(parameter)[0]; + } + parameterMap.put(field, answer); + } + } + + Map fullParamMap = creativeCommonsService.retrieveFullAnswerMap(licenseId, parameterMap); + if (fullParamMap == null) { + throw new ResourceNotFoundException("No CC License could be matched on the provided ID: " + licenseId); + } + boolean licenseContainsCorrectInfo = creativeCommonsService.verifyLicenseInformation(licenseId, fullParamMap); + if (!licenseContainsCorrectInfo) { + throw new DSpaceBadRequestException( + "The provided answers do not match the required fields for the provided license."); + } + + String licenseUri = creativeCommonsService.retrieveLicenseUri(licenseId, fullParamMap); + + if (StringUtils.isBlank(licenseUri)) { + throw new ResourceNotFoundException("No CC License URI could be found for ID: " + licenseId); + } + return licenseUri; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java index 396e014531dd..611d532039cf 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java @@ -20,6 +20,7 @@ */ public class SubmissionCCLicenseRest extends BaseObjectRest { public static final String NAME = "submissioncclicense"; + public static final String PLURAL = "submissioncclicenses"; public static final String CATEGORY = RestAddressableModel.CONFIGURATION; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java index 1a2b1b0839e3..5fa22470fef3 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java @@ -29,6 +29,7 @@ public class SubmissionCCLicenseRestRepositoryIT extends AbstractControllerInteg /** * Test the findAll method form the SubmissionCCLicenseRestRepository + * * @throws Exception */ @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java new file mode 100644 index 000000000000..402f4c3a698b --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java @@ -0,0 +1,68 @@ +/** + * 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; + + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.junit.Test; + +/** + * Class to the methods from the SubmissionCCLicenseSearchController + * Since the CC Licenses and the corresponding URIs are obtained from the CC License API, a mock service has been + * implemented. + * This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the + * CC License API. + * Refer to {@link org.dspace.license.MockCCLicenseConnectorServiceImpl} for more information + */ +public class SubmissionCCLicenseSearchControllerIT extends AbstractControllerIntegrationTest { + + + @Test + public void searchRightsByQuestionsTest() throws Exception { + getClient().perform(get( + "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + + "=license2-field0-enum1")) + .andExpect(status().isOk()) + .andExpect(content().string("mock-license-uri")); + } + + @Test + public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception { + getClient().perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3")) + .andExpect(status().isOk()) + .andExpect(content().string("mock-license-uri")); + } + + @Test + public void searchRightsByQuestionsNonExistingLicense() throws Exception { + getClient().perform(get( + "/api/config/submissioncclicenses/search/rightsByQuestions?license=nonexisting-license" + + "&answer_license2-field0=license2-field0-enum1")) + .andExpect(status().isNotFound()); + } + + @Test + public void searchRightsByQuestionsMissingRequiredAnswer() throws Exception { + getClient().perform(get( + "/api/config/submissioncclicenses/search/rightsByQuestions?license=license1&answer_license1field0" + + "=license1field0enum1")) + .andExpect(status().isBadRequest()); + } + + @Test + public void searchRightsByQuestionsAdditionalNonExistingAnswer() throws Exception { + getClient().perform(get( + "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2" + + "&answer_license2field0=license2field0enum1&answer_nonexisting=test")) + .andExpect(status().isBadRequest()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index df934312d110..23d395ec2b56 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -75,4 +75,18 @@ private List createMockLicenseFields(int count, int index, i } + /** + * Retrieve a mock CC License URI + * + * @param licenseId - the ID of the license + * @param language - the language for which to retrieve the full answerMap + * @param answerMap - the answers to the different field questions + * @return the CC License URI + */ + public String retrieveRightsByQuestion(final String licenseId, + final String language, + final Map answerMap) { + + return "mock-license-uri"; + } } From f07c3824412919b1760ee50acb966e8e3d4b037a Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Wed, 15 Apr 2020 10:31:29 +0200 Subject: [PATCH 10/73] [Task 70382] added the search findByToken method for Registrations and added ITs --- .../app/rest/model/RegistrationRest.java | 2 +- .../model/hateoas/RegistrationResource.java | 18 ++++ .../RegistrationRestRepository.java | 68 ++++++++++++++ .../rest/RegistrationRestControllerIT.java | 33 ++++--- .../rest/RegistrationRestRepositoryIT.java | 90 +++++++++++++++++++ .../app/rest/matcher/RegistrationMatcher.java | 29 ++++++ 6 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java index 0d7f2e30bde9..b84699f140d8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java @@ -13,7 +13,7 @@ import org.dspace.app.rest.RegistrationRestController; -public class RegistrationRest extends BaseObjectRest { +public class RegistrationRest extends RestAddressableModel { public static final String NAME = "registration"; public static final String NAME_PLURAL = "registrations"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java new file mode 100644 index 000000000000..0d92b0d41e1e --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java @@ -0,0 +1,18 @@ +/** + * 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.RegistrationRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; + +@RelNameDSpaceResource(RegistrationRest.NAME) +public class RegistrationResource extends HALResource { + public RegistrationResource(RegistrationRest content) { + super(content); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java new file mode 100644 index 000000000000..f9ff62316314 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java @@ -0,0 +1,68 @@ +/** + * 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 org.dspace.app.rest.Parameter; +import org.dspace.app.rest.SearchRestMethod; +import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; +import org.dspace.app.rest.model.RegistrationRest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.RegistrationData; +import org.dspace.eperson.service.AccountService; +import org.dspace.eperson.service.RegistrationDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.stereotype.Component; + +@Component(RegistrationRest.CATEGORY + "." + RegistrationRest.NAME) +public class RegistrationRestRepository extends DSpaceRestRepository { + + @Autowired + private AccountService accountService; + + @Autowired + private RegistrationDataService registrationDataService; + + @Override + public RegistrationRest findOne(Context context, Integer integer) { + throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", ""); + } + + @Override + public Page findAll(Context context, Pageable pageable) { + throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", ""); + } + + @Override + public Class getDomainClass() { + return RegistrationRest.class; + } + + @SearchRestMethod(name = "findByToken") + public RegistrationRest findByToken(@Parameter(value = "token", required = true) String token) + throws SQLException, AuthorizeException { + Context context = obtainContext(); + RegistrationData registrationData = registrationDataService.findByToken(context, token); + if (registrationData == null) { + throw new ResourceNotFoundException("The token: " + token + " couldn't be found"); + } + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(registrationData.getEmail()); + EPerson ePerson = accountService.getEPerson(context, token); + if (ePerson != null) { + registrationRest.setUser(ePerson.getID()); + } + return registrationRest; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java index 16135d2c68fa..26abcb307506 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -11,6 +11,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.Iterator; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; @@ -33,11 +34,9 @@ public class RegistrationRestControllerIT extends AbstractControllerIntegrationT @Test public void registrationFlowTest() throws Exception { - List registrationData = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationData.isEmpty()); + List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationDataList.isEmpty()); - String token = getAuthToken(eperson.getEmail(), password); - String t = ";;"; ObjectMapper mapper = new ObjectMapper(); RegistrationRest registrationRest = new RegistrationRest(); registrationRest.setEmail(eperson.getEmail()); @@ -45,9 +44,9 @@ public void registrationFlowTest() throws Exception { .content(mapper.writeValueAsBytes(registrationRest)) .contentType(contentType)) .andExpect(status().isCreated()); - registrationData = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationData.size() == 1); - assertTrue(StringUtils.equalsIgnoreCase(registrationData.get(0).getEmail(), eperson.getEmail())); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationDataList.size() == 1); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); String newEmail = "newEPersonTest@gmail.com"; registrationRest.setEmail(newEmail); @@ -55,10 +54,10 @@ public void registrationFlowTest() throws Exception { .content(mapper.writeValueAsBytes(registrationRest)) .contentType(contentType)) .andExpect(status().isCreated()); - registrationData = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationData.size() == 2); - assertTrue(StringUtils.equalsIgnoreCase(registrationData.get(0).getEmail(), newEmail) || - StringUtils.equalsIgnoreCase(registrationData.get(1).getEmail(), newEmail)); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationDataList.size() == 2); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) || + StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); configurationService.setProperty("user.registration", false); newEmail = "newEPersonTestTwo@gmail.com"; @@ -68,8 +67,14 @@ public void registrationFlowTest() throws Exception { .contentType(contentType)) .andExpect(status().is(500)); - assertTrue(registrationData.size() == 2); - assertTrue(!StringUtils.equalsIgnoreCase(registrationData.get(0).getEmail(), newEmail) && - !StringUtils.equalsIgnoreCase(registrationData.get(1).getEmail(), newEmail)); + assertTrue(registrationDataList.size() == 2); + assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && + !StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); + + Iterator iterator = registrationDataList.iterator(); + while (iterator.hasNext()) { + RegistrationData registrationData = iterator.next(); + registrationDataDAO.delete(context, registrationData); + } } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java new file mode 100644 index 000000000000..88feebb2862d --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java @@ -0,0 +1,90 @@ +/** + * 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; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.dspace.app.rest.matcher.RegistrationMatcher; +import org.dspace.app.rest.model.RegistrationRest; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.eperson.RegistrationData; +import org.dspace.eperson.dao.RegistrationDataDAO; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class RegistrationRestRepositoryIT extends AbstractControllerIntegrationTest { + + @Autowired + private RegistrationDataDAO registrationDataDAO; + + @Test + public void findByTokenTestExistingUserTest() throws Exception { + String email = eperson.getEmail(); + createTokenForEmail(email); + RegistrationData registrationData = registrationDataDAO.findByEmail(context, email); + + getClient().perform(get("/api/eperson/registrations/search/findByToken") + .param("token", registrationData.getToken())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, eperson.getID())))); + + email = "newUser@testnewuser.com"; + createTokenForEmail(email); + registrationData = registrationDataDAO.findByEmail(context, email); + + getClient().perform(get("/api/eperson/registrations/search/findByToken") + .param("token", registrationData.getToken())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, null)))); + + registrationDataDAO.delete(context, registrationData); + + } + + @Test + public void findByTokenTestNewUserTest() throws Exception { + String email = "newUser@testnewuser.com"; + createTokenForEmail(email); + RegistrationData registrationData = registrationDataDAO.findByEmail(context, email); + + getClient().perform(get("/api/eperson/registrations/search/findByToken") + .param("token", registrationData.getToken())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, null)))); + + registrationDataDAO.delete(context, registrationData); + } + + @Test + public void findByTokenNotExistingTokenTest() throws Exception { + getClient().perform(get("/api/eperson/registration/search/findByToken") + .param("token", "ThisTokenDoesNotExist")) + .andExpect(status().isNotFound()); + } + + private void createTokenForEmail(String email) throws Exception { + List registrationDatas; + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(email); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java new file mode 100644 index 000000000000..a154091a2eff --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/RegistrationMatcher.java @@ -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.matcher; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.is; + +import java.util.UUID; + +import org.hamcrest.Matcher; + +public class RegistrationMatcher { + + private RegistrationMatcher(){} + + public static Matcher matchRegistration(String email, UUID epersonUuid) { + return allOf( + hasJsonPath("$.email", is(email)), + hasJsonPath("$.user", is(epersonUuid == null ? null : String.valueOf(epersonUuid))) + ); + + } +} From 7da7ff03786802a4c707e2fb68e5c99079fa2bae Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Wed, 15 Apr 2020 12:57:51 +0200 Subject: [PATCH 11/73] [Task 70397] added EPersonRegistrationFeature and added ITs --- .../app/rest/RegistrationRestController.java | 38 ++++-- .../impl/EPersonRegistrationFeature.java | 55 ++++++++ .../rest/RegistrationRestControllerIT.java | 2 +- .../EPersonRegistrationFeatureIT.java | 128 ++++++++++++++++++ 4 files changed, 207 insertions(+), 16 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java index f11bed240f94..b03eda36f076 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java @@ -16,10 +16,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.authorization.AuthorizationFeature; +import org.dspace.app.rest.authorization.AuthorizationFeatureService; +import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.RegistrationRest; +import org.dspace.app.rest.model.SiteRest; +import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Site; +import org.dspace.content.service.SiteService; import org.dspace.core.Context; import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; @@ -28,6 +35,7 @@ import org.springframework.hateoas.ResourceSupport; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @@ -36,14 +44,14 @@ @RequestMapping("/api/" + RegistrationRest.CATEGORY + "/" + RegistrationRest.NAME_PLURAL) public class RegistrationRestController { -// @Autowired -// private AuthorizationFeatureService authorizationFeatureService; -// -// @Autowired -// private SiteService siteService; -// -// @Autowired -// private ConverterService converterService; + @Autowired + private AuthorizationFeatureService authorizationFeatureService; + + @Autowired + private SiteService siteService; + + @Autowired + private ConverterService converterService; @Autowired private AccountService accountService; @@ -56,13 +64,13 @@ public ResponseEntity register(HttpServletRequest request, Http throws SQLException, IOException, MessagingException, AuthorizeException { Context context = ContextUtil.obtainContext(request); -// AuthorizationFeature epersonRegistration = authorizationFeatureService.find("epersonRegistration"); -// Site site = siteService.findSite(context); -// SiteRest siteRest = converterService.toRest(site, Projection.DEFAULT); -// if (!authorizationFeatureService.isAuthorized(context, epersonRegistration, siteRest)) { -// throw new AccessDeniedException("Registration is disabled, you are not authorized to create -// a new Authorization"); -// } + AuthorizationFeature epersonRegistration = authorizationFeatureService.find("epersonRegistration"); + Site site = siteService.findSite(context); + SiteRest siteRest = converterService.toRest(site, Projection.DEFAULT); + if (!authorizationFeatureService.isAuthorized(context, epersonRegistration, siteRest)) { + throw new AccessDeniedException( + "Registration is disabled, you are not authorized to create a new Authorization"); + } ObjectMapper mapper = new ObjectMapper(); RegistrationRest registrationRest; try { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java new file mode 100644 index 000000000000..44de9ce6d740 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java @@ -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.rest.authorization.impl; + +import java.sql.SQLException; + +import org.dspace.app.rest.authorization.AuthorizationFeature; +import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation; +import org.dspace.app.rest.model.BaseObjectRest; +import org.dspace.app.rest.model.SiteRest; +import org.dspace.authenticate.service.AuthenticationService; +import org.dspace.core.Context; +import org.dspace.services.ConfigurationService; +import org.dspace.services.RequestService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@AuthorizationFeatureDocumentation(name = EPersonRegistrationFeature.NAME, + description = "It can be used to register an eperson") +public class EPersonRegistrationFeature implements AuthorizationFeature { + + public static final String NAME = "epersonRegistration"; + + @Autowired + private ConfigurationService configurationService; + + @Autowired + private AuthenticationService authenticationService; + + @Autowired + private RequestService requestService; + + @Override + public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLException { + if (!(object instanceof SiteRest)) { + return false; + } + if (configurationService.getBooleanProperty("user.registration", true)) { + return authenticationService + .allowSetPassword(context, requestService.getCurrentRequest().getHttpServletRequest(), null); + } + return false; + } + + @Override + public String[] getSupportedTypes() { + return new String[] {SiteRest.CATEGORY + "." + SiteRest.NAME}; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java index 26abcb307506..0201eb284fb9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -65,7 +65,7 @@ public void registrationFlowTest() throws Exception { getClient().perform(post("/api/eperson/registrations") .content(mapper.writeValueAsBytes(registrationRest)) .contentType(contentType)) - .andExpect(status().is(500)); + .andExpect(status().is(401)); assertTrue(registrationDataList.size() == 2); assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java new file mode 100644 index 000000000000..a9bbdce3db23 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java @@ -0,0 +1,128 @@ +/** + * 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.authorization; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.dspace.app.rest.authorization.impl.EPersonRegistrationFeature; +import org.dspace.app.rest.converter.ConverterService; +import org.dspace.app.rest.converter.SiteConverter; +import org.dspace.app.rest.model.SiteRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.app.rest.utils.Utils; +import org.dspace.content.Site; +import org.dspace.content.service.SiteService; +import org.dspace.services.ConfigurationService; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class EPersonRegistrationFeatureIT extends AbstractControllerIntegrationTest { + + @Autowired + private AuthorizationFeatureService authorizationFeatureService; + + @Autowired + private ConverterService converterService; + + @Autowired + private ConfigurationService configurationService; + + @Autowired + private SiteService siteService; + + @Autowired + private SiteConverter siteConverter; + + @Autowired + private Utils utils; + + private AuthorizationFeature epersonRegistrationFeature; + + public static final String[] SHIB_ONLY = {"org.dspace.authenticate.ShibAuthentication"}; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + epersonRegistrationFeature = authorizationFeatureService.find(EPersonRegistrationFeature.NAME); + } + + @Test + public void userRegistrationEnabledSuccessTest() throws Exception { + + Site site = siteService.findSite(context); + SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT); + String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref(); + + // access the authorization for the admin user + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + .param("uri", siteUri) + .param("eperson", admin.getID().toString()) + .param("feature", epersonRegistrationFeature.getName())) + .andExpect(status().isOk()); + } + + @Test + public void userRegistrationDisabledUnAuthorizedTest() throws Exception { + + Site site = siteService.findSite(context); + SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT); + String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref(); + + context.turnOffAuthorisationSystem(); + configurationService.setProperty("user.registration", false); + context.restoreAuthSystemState(); + // access the authorization for the admin user + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + .param("uri", siteUri) + .param("eperson", admin.getID().toString()) + .param("feature", epersonRegistrationFeature.getName())) + .andExpect(status().isNoContent()); + + context.turnOffAuthorisationSystem(); + configurationService.setProperty("user.registration", true); + context.restoreAuthSystemState(); + } + + + @Test + public void userRegistrationEnabledShibTest() throws Exception { + + Site site = siteService.findSite(context); + SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT); + String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref(); + + // access the authorization for the admin user + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + .param("uri", siteUri) + .param("eperson", admin.getID().toString()) + .param("feature", epersonRegistrationFeature.getName())) + .andExpect(status().isOk()); + + context.turnOffAuthorisationSystem(); + //Enable Shibboleth and password login + configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY); + context.restoreAuthSystemState(); + + getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + .param("uri", siteUri) + .param("eperson", admin.getID().toString()) + .param("feature", epersonRegistrationFeature.getName())) + .andExpect(status().isNoContent()); + + } +} From b30f9d9338b9008e47442bf43e28f9313d58bdaa Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 16 Apr 2020 16:51:02 +0200 Subject: [PATCH 12/73] 70403: CC license (REST): New submission section --- .../license/CreativeCommonsServiceImpl.java | 54 +++++++++++++++++-- .../dspace/license/LicenseMetadataValue.java | 4 ++ .../service/CreativeCommonsService.java | 20 ++++++- .../app/rest/model/step/DataCCLicense.java | 44 +++++++++++++++ .../submit/AbstractRestProcessingStep.java | 1 + .../app/rest/submit/SubmissionService.java | 52 ++++++++++++------ .../app/rest/submit/step/CCLicenseStep.java | 44 +++++++++++++++ dspace/config/item-submission.xml | 8 +-- 8 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index c0c190307b08..928e4b689111 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -235,15 +235,51 @@ public Bitstream getLicenseTextBitstream(Item item) throws SQLException, @Override public String getLicenseURL(Context context, Item item) throws SQLException, IOException, AuthorizeException { - String licenseUri = getCCField("uri").ccItemValue(item); + String licenseUri = getCCField("uri"); if (StringUtils.isNotBlank(licenseUri)) { - return licenseUri; + return getLicenseURI(item); } // JSPUI backward compatibility see https://jira.duraspace.org/browse/DS-2604 return getStringFromBitstream(context, item, BSN_LICENSE_URL); } + /** + * Returns the stored license uri of the item + * + * @param item - The item for which to retrieve the stored license uri + * @return the stored license uri of the item + */ + @Override + public String getLicenseURI(Item item) { + String licenseUriField = getCCField("uri"); + if (StringUtils.isNotBlank(licenseUriField)) { + String metadata = itemService.getMetadata(item, licenseUriField); + if (StringUtils.isNotBlank(metadata)) { + return metadata; + } + } + return null; + } + + /** + * Returns the stored license name of the item + * + * @param item - The item for which to retrieve the stored license name + * @return the stored license name of the item + */ + @Override + public String getLicenseName( Item item) { + String licenseNameField = getCCField("name"); + if (StringUtils.isNotBlank(licenseNameField)) { + String metadata = itemService.getMetadata(item, licenseNameField); + if (StringUtils.isNotBlank(metadata)) { + return metadata; + } + } + return null; + } + @Override public String fetchLicenseRDF(Document license) { StringWriter result = new StringWriter(); @@ -374,8 +410,8 @@ protected byte[] getBytesFromBitstream(Context context, Item item, String bitstr * Returns a metadata field handle for given field Id */ @Override - public LicenseMetadataValue getCCField(String fieldId) { - return new LicenseMetadataValue(configurationService.getProperty("cc.license." + fieldId)); + public String getCCField(String fieldId) { + return configurationService.getProperty("cc.license." + fieldId); } @Override @@ -401,6 +437,7 @@ public void removeLicense(Context context, LicenseMetadataValue uriField, * * @return A list of available CC Licenses */ + @Override public List findAllCCLicenses() { return findAllCCLicenses(defaultLanguage); } @@ -411,6 +448,7 @@ public List findAllCCLicenses() { * @param language - the language for which to find the CC Licenses * @return A list of available CC Licenses for the provided language */ + @Override public List findAllCCLicenses(String language) { if (!ccLicenses.containsKey(language)) { @@ -425,6 +463,7 @@ public List findAllCCLicenses(String language) { * @param id - the ID of the license to be found * @return the corresponding license if found or null when not found */ + @Override public CCLicense findOne(String id) { return findOne(id, defaultLanguage); } @@ -436,6 +475,7 @@ public CCLicense findOne(String id) { * @param language - the language for which to find the CC License * @return the corresponding license if found or null when not found */ + @Override public CCLicense findOne(String id, String language) { if (!ccLicenses.containsKey(language)) { initLicenses(language); @@ -465,6 +505,7 @@ private void initLicenses(final String language) { * @param answerMap - the answers to the different field questions * @return the corresponding license URI */ + @Override public String retrieveLicenseUri(String licenseId, Map answerMap) { return retrieveLicenseUri(licenseId, defaultLanguage, answerMap); @@ -478,6 +519,7 @@ public String retrieveLicenseUri(String licenseId, Map answerMap * @param answerMap - the answers to the different field questions * @return the corresponding license URI */ + @Override public String retrieveLicenseUri(String licenseId, String language, Map answerMap) { return ccLicenseConnectorService.retrieveRightsByQuestion(licenseId, language, answerMap); @@ -491,6 +533,7 @@ public String retrieveLicenseUri(String licenseId, String language, Map fullAnswerMap) { return verifyLicenseInformation(licenseId, defaultLanguage, fullAnswerMap); } @@ -504,6 +547,7 @@ public boolean verifyLicenseInformation(String licenseId, Map fu * @param fullAnswerMap - the answers to the different field questions * @return whether the information is valid */ + @Override public boolean verifyLicenseInformation(String licenseId, String language, Map fullAnswerMap) { CCLicense ccLicense = findOne(licenseId, language); @@ -529,6 +573,7 @@ public boolean verifyLicenseInformation(String licenseId, String language, Map retrieveFullAnswerMap(String licenseId, Map answerMap) { return retrieveFullAnswerMap(licenseId, defaultLanguage, answerMap); } @@ -542,6 +587,7 @@ public Map retrieveFullAnswerMap(String licenseId, Map retrieveFullAnswerMap(String licenseId, String language, Map answerMap) { CCLicense ccLicense = findOne(licenseId, language); if (ccLicense == null) { diff --git a/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java b/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java index ec5c9e447b55..6c1d27d381c3 100644 --- a/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java +++ b/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java @@ -18,6 +18,8 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.core.Context; +import org.dspace.license.factory.LicenseServiceFactory; +import org.dspace.license.service.CreativeCommonsService; /** * Helper class for using CC-related Metadata fields @@ -27,6 +29,7 @@ public class LicenseMetadataValue { protected final ItemService itemService; + protected final CreativeCommonsService creativeCommonsService; // Shibboleth for Creative Commons license data - i.e. characters that reliably indicate CC in a URI protected static final String ccShib = "creativecommons"; @@ -41,6 +44,7 @@ public LicenseMetadataValue(String fieldName) { params[3] = Item.ANY; } itemService = ContentServiceFactory.getInstance().getItemService(); + creativeCommonsService = LicenseServiceFactory.getInstance().getCreativeCommonsService(); } /** diff --git a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java index cda546246f14..32253932482a 100644 --- a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java +++ b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java @@ -86,6 +86,24 @@ public boolean hasLicense(Context context, Item item) public String getLicenseURL(Context context, Item item) throws SQLException, IOException, AuthorizeException; + + /** + * Returns the stored license uri of the item + * + * @param item - The item for which to retrieve the stored license uri + * @return the stored license uri of the item + */ + public String getLicenseURI(Item item); + + /** + * Returns the stored license name of the item + * + * @param item - The item for which to retrieve the stored license name + * @return the stored license name of the item + */ + public String getLicenseName(Item item); + + public String getLicenseRDF(Context context, Item item) throws SQLException, IOException, AuthorizeException; @@ -124,7 +142,7 @@ public Bitstream getLicenseTextBitstream(Item item) * @param fieldId name of the property. * @return its value. */ - public LicenseMetadataValue getCCField(String fieldId); + public String getCCField(String fieldId); /** * Apply same transformation on the document to retrieve only the most diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java new file mode 100644 index 000000000000..a8cfaf64f5e8 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java @@ -0,0 +1,44 @@ +/** + * 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.step; + +import org.dspace.app.rest.model.BitstreamRest; + + +public class DataCCLicense implements SectionData { + + private String uri; + + private String rights; + + private BitstreamRest file; + + public String getUri() { + return uri; + } + + public void setUri(final String uri) { + this.uri = uri; + } + + public String getRights() { + return rights; + } + + public void setRights(final String rights) { + this.rights = rights; + } + + public BitstreamRest getFile() { + return file; + } + + public void setFile(final BitstreamRest file) { + this.file = file; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java index 58bfaba27b65..9989f6ca07f5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java @@ -35,6 +35,7 @@ public interface AbstractRestProcessingStep extends ListenerProcessingStep { public static final String UPLOAD_STEP_MOVE_OPERATION_ENTRY = "bitstreammove"; public static final String UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY = "accessConditions"; public static final String LICENSE_STEP_OPERATION_ENTRY = "granted"; + public static final String CCLICENSE_STEP_OPERATION_ENTRY = "ccLicense"; public static final String UPLOAD_STEP_METADATA_PATH = "metadata"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java index 2840035a7a08..660c360f333d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java @@ -25,6 +25,7 @@ import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.UploadBitstreamAccessConditionDTO; import org.dspace.app.rest.model.WorkspaceItemRest; +import org.dspace.app.rest.model.step.DataCCLicense; import org.dspace.app.rest.model.step.DataUpload; import org.dspace.app.rest.model.step.UploadBitstreamRest; import org.dspace.app.rest.projection.Projection; @@ -33,6 +34,8 @@ import org.dspace.authorize.ResourcePolicy; import org.dspace.content.Bitstream; import org.dspace.content.Collection; +import org.dspace.content.InProgressSubmission; +import org.dspace.content.Item; import org.dspace.content.MetadataValue; import org.dspace.content.WorkspaceItem; import org.dspace.content.service.CollectionService; @@ -41,6 +44,7 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.Utils; +import org.dspace.license.service.CreativeCommonsService; import org.dspace.services.ConfigurationService; import org.dspace.services.RequestService; import org.dspace.services.model.Request; @@ -75,6 +79,8 @@ public class SubmissionService { @Autowired protected WorkflowService workflowService; @Autowired + protected CreativeCommonsService creativeCommonsService; + @Autowired private RequestService requestService; @Autowired private ConverterService converter; @@ -84,10 +90,8 @@ public class SubmissionService { /** * Create a workspaceitem using the information in the request * - * @param context - * the dspace context - * @param request - * the request containing the details about the workspace to create + * @param context the dspace context + * @param request the request containing the details about the workspace to create * @return * @throws SQLException * @throws AuthorizeException @@ -136,19 +140,19 @@ public void saveWorkspaceItem(Context context, WorkspaceItem wsi) { } } -/** - * Build the rest representation of a bitstream as used in the upload section - * ({@link DataUpload}. It contains all its metadata and the list of applied - * access conditions (@link {@link UploadBitstreamAccessConditionDTO} - * - * @param configurationService the DSpace ConfigurationService - * @param source the bitstream to translate in its rest submission - * representation - * @return - * @throws SQLException - */ + /** + * Build the rest representation of a bitstream as used in the upload section + * ({@link DataUpload}. It contains all its metadata and the list of applied + * access conditions (@link {@link UploadBitstreamAccessConditionDTO} + * + * @param configurationService the DSpace ConfigurationService + * @param source the bitstream to translate in its rest submission + * representation + * @return + * @throws SQLException + */ public UploadBitstreamRest buildUploadBitstream(ConfigurationService configurationService, Bitstream source) - throws SQLException { + throws SQLException { UploadBitstreamRest data = new UploadBitstreamRest(); for (MetadataValue md : source.getMetadata()) { @@ -242,7 +246,7 @@ public XmlWorkflowItem createWorkflowItem(Context context, String requestUriList wi = workflowService.start(context, wsi); } catch (IOException e) { throw new RuntimeException("The workflow could not be started for workspaceItem with" + - "id: " + id); + "id: " + id); } return wi; @@ -268,4 +272,18 @@ private UploadBitstreamAccessConditionDTO createAccessConditionFromResourcePolic public void saveWorkflowItem(Context context, XmlWorkflowItem source) throws SQLException, AuthorizeException { workflowItemService.update(context, source); } + + public DataCCLicense getDataCCLicense(InProgressSubmission obj) + throws SQLException, IOException, AuthorizeException { + DataCCLicense result = new DataCCLicense(); + Item item = obj.getItem(); + + result.setUri(creativeCommonsService.getLicenseURI(item)); + result.setRights(creativeCommonsService.getLicenseName(item)); + + Bitstream licenseRdfBitstream = creativeCommonsService.getLicenseRdfBitstream(item); + result.setFile(converter.toRest(licenseRdfBitstream, Projection.DEFAULT)); + + return result; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java new file mode 100644 index 000000000000..4dcc319a3dc6 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java @@ -0,0 +1,44 @@ +/** + * 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.submit.step; + +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.model.step.DataCCLicense; +import org.dspace.app.rest.submit.AbstractRestProcessingStep; +import org.dspace.app.rest.submit.SubmissionService; +import org.dspace.app.rest.submit.factory.PatchOperationFactory; +import org.dspace.app.rest.submit.factory.impl.PatchOperation; +import org.dspace.app.util.SubmissionStepConfig; +import org.dspace.content.InProgressSubmission; +import org.dspace.core.Context; +import org.dspace.services.model.Request; + + +public class CCLicenseStep extends org.dspace.submit.step.CCLicenseStep implements AbstractRestProcessingStep { + + @Override + public DataCCLicense getData(SubmissionService submissionService, InProgressSubmission obj, + SubmissionStepConfig config) + throws Exception { + return submissionService.getDataCCLicense(obj); + } + + + @Override + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) + throws Exception { + + if (op.getPath().endsWith(CCLICENSE_STEP_OPERATION_ENTRY)) { + + PatchOperation patchOperation = new PatchOperationFactory() + .instanceOf(CCLICENSE_STEP_OPERATION_ENTRY, op.getOp()); + patchOperation.perform(context, currentRequest, source, op); + + } + } +} diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index af707162ae06..d1f755d2937f 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -115,9 +115,9 @@ - + submit.progressbar.CClicense + org.dspace.app.rest.submit.step.CCLicenseStep + cclicense + + From 5800bef325d38876b383fad6941a273e38069c4c Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 17 Apr 2020 13:13:40 +0200 Subject: [PATCH 13/73] 70403: Add javadocs, undo line adjustments, LicenseMetadataValue changes --- .../dspace/license/LicenseMetadataValue.java | 4 ---- .../app/rest/model/step/DataCCLicense.java | 4 +++- .../app/rest/submit/SubmissionService.java | 15 ++++++++++-- .../app/rest/submit/step/CCLicenseStep.java | 23 ++++++++++++++++++- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java b/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java index 6c1d27d381c3..ec5c9e447b55 100644 --- a/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java +++ b/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java @@ -18,8 +18,6 @@ import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.service.ItemService; import org.dspace.core.Context; -import org.dspace.license.factory.LicenseServiceFactory; -import org.dspace.license.service.CreativeCommonsService; /** * Helper class for using CC-related Metadata fields @@ -29,7 +27,6 @@ public class LicenseMetadataValue { protected final ItemService itemService; - protected final CreativeCommonsService creativeCommonsService; // Shibboleth for Creative Commons license data - i.e. characters that reliably indicate CC in a URI protected static final String ccShib = "creativecommons"; @@ -44,7 +41,6 @@ public LicenseMetadataValue(String fieldName) { params[3] = Item.ANY; } itemService = ContentServiceFactory.getInstance().getItemService(); - creativeCommonsService = LicenseServiceFactory.getInstance().getCreativeCommonsService(); } /** diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java index a8cfaf64f5e8..32b3710d7c5c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/step/DataCCLicense.java @@ -9,7 +9,9 @@ import org.dspace.app.rest.model.BitstreamRest; - +/** + * Java Bean to expose the section creativecommons representing the CC License during in progress submission. + */ public class DataCCLicense implements SectionData { private String uri; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java index 660c360f333d..0ac468448b23 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/SubmissionService.java @@ -90,8 +90,10 @@ public class SubmissionService { /** * Create a workspaceitem using the information in the request * - * @param context the dspace context - * @param request the request containing the details about the workspace to create + * @param context + * the dspace context + * @param request + * the request containing the details about the workspace to create * @return * @throws SQLException * @throws AuthorizeException @@ -273,6 +275,15 @@ public void saveWorkflowItem(Context context, XmlWorkflowItem source) throws SQL workflowItemService.update(context, source); } + /** + * Builds the CC License data of an inprogress submission based on the cc license info present in the metadata + * + * @param obj - the in progress submission + * @return an object representing the CC License data + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ public DataCCLicense getDataCCLicense(InProgressSubmission obj) throws SQLException, IOException, AuthorizeException { DataCCLicense result = new DataCCLicense(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java index 4dcc319a3dc6..75f194911655 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java @@ -18,9 +18,21 @@ import org.dspace.core.Context; import org.dspace.services.model.Request; - +/** + * CC License step for DSpace Spring Rest. Expose the creative commons license information about the in progress + * submission. + */ public class CCLicenseStep extends org.dspace.submit.step.CCLicenseStep implements AbstractRestProcessingStep { + /** + * Retrieves the CC License data of the in progress submission + * + * @param submissionService the submission service + * @param obj the in progress submission + * @param config the submission step configuration + * @return the CC License data of the in progress submission + * @throws Exception + */ @Override public DataCCLicense getData(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) @@ -29,6 +41,15 @@ public DataCCLicense getData(SubmissionService submissionService, InProgressSubm } + /** + * Processes a patch for the CC License data + * + * @param context the DSpace context + * @param currentRequest the http request + * @param source the in progress submission + * @param op the json patch operation + * @throws Exception + */ @Override public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) throws Exception { From fee24e3f96dba9f2a21b97aabae29485915a3fca Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Fri, 17 Apr 2020 14:14:25 +0200 Subject: [PATCH 14/73] [Task 70398] implementing change password with token patch and added ITs for the functionality --- .../repository/EPersonRestRepository.java | 14 ++ .../EPersonPasswordReplaceOperation.java | 39 ++++ .../app/rest/EPersonRestRepositoryIT.java | 213 ++++++++++++++++++ 3 files changed, 266 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index 073d1b25bd3c..aa2526c0fe9c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -19,6 +19,7 @@ import org.dspace.app.rest.SearchRestMethod; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.EPersonRest; +import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; @@ -28,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; @@ -167,6 +169,18 @@ public Page findByMetadata(@Parameter(value = "query", required = t @PreAuthorize("hasPermission(#uuid, 'EPERSON', #patch)") protected void patch(Context context, HttpServletRequest request, String apiCategory, String model, UUID uuid, Patch patch) throws AuthorizeException, SQLException { + if (StringUtils.isNotBlank(request.getParameter("token"))) { + boolean passwordChangeFound = false; + for (Operation operation : patch.getOperations()) { + if (StringUtils.equalsIgnoreCase(operation.getPath(), "/password")) { + passwordChangeFound = true; + } + } + if (!passwordChangeFound) { + throw new AccessDeniedException("Couldn't perform the patch as a token with provided without " + + "a password change"); + } + } patchDSpaceObject(apiCategory, model, uuid, patch); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java index 00b30e24f14d..387721915dbe 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java @@ -7,12 +7,21 @@ */ package org.dspace.app.rest.repository.patch.operation; +import java.sql.SQLException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.model.patch.Operation; +import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; +import org.dspace.services.RequestService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Component; /** @@ -27,18 +36,31 @@ @Component public class EPersonPasswordReplaceOperation extends PatchOperation { + private static final Logger log = org.apache.logging.log4j.LogManager + .getLogger(EPersonPasswordReplaceOperation.class); + /** * Path in json body of patch that uses this operation */ public static final String OPERATION_PASSWORD_CHANGE = "/password"; protected EPersonService ePersonService = EPersonServiceFactory.getInstance().getEPersonService(); + @Autowired + private RequestService requestService; + + @Autowired + private AccountService accountService; + @Override public R perform(Context context, R object, Operation operation) { checkOperationValue(operation.getValue()); if (supports(object, operation)) { EPerson eperson = (EPerson) object; + String token = requestService.getCurrentRequest().getHttpServletRequest().getParameter("token"); checkModelForExistingValue(eperson); + if (StringUtils.isNotBlank(token)) { + patchWithToken(context,eperson, token, operation); + } ePersonService.setPassword(eperson, (String) operation.getValue()); return object; } else { @@ -46,6 +68,23 @@ public R perform(Context context, R object, Operation operation) { } } + private void patchWithToken(Context context, EPerson eperson, String token, Operation operation) { + try { + EPerson ePersonFromToken = accountService.getEPerson(context, token); + if (ePersonFromToken == null) { + throw new AccessDeniedException("The token in the parameter: " + token + " couldn't" + + " be associated with an EPerson"); + } + if (!ePersonFromToken.getID().equals(eperson.getID())) { + throw new AccessDeniedException("The token in the parameter belongs to a different EPerson" + + " than the uri indicates"); + } + accountService.deleteToken(context, token); + } catch (SQLException | AuthorizeException e) { + log.error(e.getMessage(), e); + } + } + /** * Checks whether the ePerson has a password via the ePersonService to checking if it has a non null password hash * throws a DSpaceBadRequestException if not pw hash was present diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 9610287273c3..7b6513f766d6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -14,6 +14,8 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; @@ -28,6 +30,7 @@ import javax.ws.rs.core.MediaType; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.builder.CollectionBuilder; import org.dspace.app.rest.builder.CommunityBuilder; import org.dspace.app.rest.builder.EPersonBuilder; @@ -39,6 +42,7 @@ import org.dspace.app.rest.model.EPersonRest; import org.dspace.app.rest.model.MetadataRest; import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.RegistrationRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.ReplaceOperation; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; @@ -47,12 +51,25 @@ import org.dspace.content.Item; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; +import org.dspace.eperson.PasswordHash; +import org.dspace.eperson.service.AccountService; +import org.dspace.eperson.service.EPersonService; +import org.dspace.eperson.service.RegistrationDataService; import org.hamcrest.Matchers; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { + @Autowired + private AccountService accountService; + + @Autowired + private RegistrationDataService registrationDataService; + + @Autowired + private EPersonService ePersonService; @Test public void createTest() throws Exception { @@ -1590,4 +1607,200 @@ public void getDirectEpersonGroups() throws Exception { ); } + + @Test + public void patchReplacePasswordWithToken() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("John", "Doe") + .withEmail("Johndoe@fake-email.com") + .withPassword(password) + .build(); + + String newPassword = "newpassword"; + + context.restoreAuthSystemState(); + + List ops = new ArrayList(); + ReplaceOperation replaceOperation = new ReplaceOperation("/password", newPassword); + ops.add(replaceOperation); + String patchBody = getPatchContent(ops); + accountService.sendRegistrationInfo(context, ePerson.getEmail()); + String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); + String token = getAuthToken(admin.getEmail(), password); + PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); + // updates password + getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) + .param("token", tokenForEPerson)) + .andExpect(status().isOk()); + + PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); + assertFalse(oldPassword.equals(newPasswordHash)); + assertTrue(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + } + + + @Test + public void patchReplacePasswordWithRandomTokenPatchFail() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("John", "Doe") + .withEmail("Johndoe@fake-email.com") + .withPassword(password) + .build(); + + String newPassword = "newpassword"; + + context.restoreAuthSystemState(); + + List ops = new ArrayList(); + ReplaceOperation replaceOperation = new ReplaceOperation("/password", newPassword); + ops.add(replaceOperation); + String patchBody = getPatchContent(ops); + accountService.sendRegistrationInfo(context, ePerson.getEmail()); + String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); + String token = getAuthToken(admin.getEmail(), password); + PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); + // updates password + getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) + .param("token", "RandomToken")) + .andExpect(status().isForbidden()); + + PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); + assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); + assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + assertTrue(StringUtils.equals(registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(), + tokenForEPerson)); + } + + @Test + public void patchReplacePasswordWithOtherUserTokenFail() throws Exception { + context.turnOffAuthorisationSystem(); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("John", "Doe") + .withEmail("Johndoe@fake-email.com") + .withPassword(password) + .build(); + + + EPerson ePersonTwo = EPersonBuilder.createEPerson(context) + .withNameInMetadata("Smith", "Donald") + .withEmail("donaldSmith@fake-email.com") + .withPassword(password) + .build(); + + String newPassword = "newpassword"; + + context.restoreAuthSystemState(); + + List ops = new ArrayList(); + ReplaceOperation replaceOperation = new ReplaceOperation("/password", newPassword); + ops.add(replaceOperation); + String patchBody = getPatchContent(ops); + accountService.sendRegistrationInfo(context, ePerson.getEmail()); + accountService.sendRegistrationInfo(context, ePersonTwo.getEmail()); + String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); + String tokenForEPersonTwo = registrationDataService.findByEmail(context, ePersonTwo.getEmail()).getToken(); + + String token = getAuthToken(admin.getEmail(), password); + PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); + // updates password + getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) + .param("token", tokenForEPersonTwo)) + .andExpect(status().isForbidden()); + + PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); + assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); + assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + } + + @Test + public void patchReplaceEmailWithTokenFail() throws Exception { + context.turnOffAuthorisationSystem(); + + String originalEmail = "Johndoe@fake-email.com"; + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("John", "Doe") + .withEmail(originalEmail) + .withPassword(password) + .build(); + + String newEmail = "johnyandmaria@fake-email.com"; + + context.restoreAuthSystemState(); + + List ops = new ArrayList(); + ReplaceOperation replaceOperation = new ReplaceOperation("/email", newEmail); + ops.add(replaceOperation); + String patchBody = getPatchContent(ops); + accountService.sendRegistrationInfo(context, ePerson.getEmail()); + String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); + String token = getAuthToken(admin.getEmail(), password); + PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); + // updates password + getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) + .param("token", tokenForEPerson)) + .andExpect(status().isForbidden()); + + PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); + assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); + assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + assertTrue(StringUtils.equalsIgnoreCase(ePerson.getEmail(), originalEmail)); + } + + @Test + public void registerNewAccountPatchUpdatePasswordRandomUserUuidFail() throws Exception { + context.turnOffAuthorisationSystem(); + + ObjectMapper mapper = new ObjectMapper(); + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + + EPerson ePerson = EPersonBuilder.createEPerson(context) + .withNameInMetadata("John", "Doe") + .withEmail("Johndoe@fake-email.com") + .withPassword(password) + .build(); + + String newPassword = "newpassword"; + + context.restoreAuthSystemState(); + + List ops = new ArrayList(); + ReplaceOperation replaceOperation = new ReplaceOperation("/password", newPassword); + ops.add(replaceOperation); + String patchBody = getPatchContent(ops); + accountService.sendRegistrationInfo(context, ePerson.getEmail()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + String token = getAuthToken(admin.getEmail(), password); + PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); + // updates password + getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) + .param("token", newRegisterToken)) + .andExpect(status().isForbidden()); + + PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); + assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); + assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + assertFalse(registrationDataService.findByEmail(context, newRegisterEmail) == null); + } + } From cb8b5c8efc3baaa535b1545f31313c8a3364e02f Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Mon, 20 Apr 2020 11:31:34 +0200 Subject: [PATCH 15/73] [Task 70399] created the POST EPerson endpoint with token and added tests --- .../dspace/app/rest/model/EPersonRest.java | 6 +- .../repository/EPersonRestRepository.java | 95 ++++- .../app/rest/EPersonRestRepositoryIT.java | 365 ++++++++++++++++++ 3 files changed, 462 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java index 00881b9fd1ef..7b4c683322a9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/EPersonRest.java @@ -41,7 +41,7 @@ public class EPersonRest extends DSpaceObjectRest { private boolean requireCertificate = false; - private boolean selfRegistered = false; + private Boolean selfRegistered; @JsonProperty(access = Access.WRITE_ONLY) private String password; @@ -92,11 +92,11 @@ public void setRequireCertificate(boolean requireCertificate) { this.requireCertificate = requireCertificate; } - public boolean isSelfRegistered() { + public Boolean isSelfRegistered() { return selfRegistered; } - public void setSelfRegistered(boolean selfRegistered) { + public void setSelfRegistered(Boolean selfRegistered) { this.selfRegistered = selfRegistered; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index aa2526c0fe9c..1309e11713c5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -15,17 +15,29 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; import org.dspace.app.rest.Parameter; import org.dspace.app.rest.SearchRestMethod; +import org.dspace.app.rest.authorization.AuthorizationFeature; +import org.dspace.app.rest.authorization.AuthorizationFeatureService; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.EPersonRest; +import org.dspace.app.rest.model.MetadataRest; +import org.dspace.app.rest.model.MetadataValueRest; +import org.dspace.app.rest.model.SiteRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; +import org.dspace.app.rest.projection.Projection; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.Site; +import org.dspace.content.service.SiteService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; +import org.dspace.eperson.RegistrationData; +import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; +import org.dspace.eperson.service.RegistrationDataService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -43,9 +55,23 @@ @Component(EPersonRest.CATEGORY + "." + EPersonRest.NAME) public class EPersonRestRepository extends DSpaceObjectRestRepository { + private static final Logger log = Logger.getLogger(EPersonRestRepository.class); + @Autowired AuthorizeService authorizeService; + @Autowired + private AccountService accountService; + + @Autowired + private AuthorizationFeatureService authorizationFeatureService; + + @Autowired + private SiteService siteService; + + @Autowired + private RegistrationDataService registrationDataService; + private final EPersonService es; @@ -66,7 +92,21 @@ protected EPersonRest createAndReturn(Context context) } catch (IOException e1) { throw new UnprocessableEntityException("error parsing the body... maybe this is not the right error code"); } + String token = req.getParameter("token"); + if (StringUtils.isNotBlank(token)) { + try { + return createAndReturn(context, epersonRest, token); + } catch (SQLException e) { + log.error(e.getMessage(), e); + throw new RuntimeException("Something with wrong in the creation of an EPerson with token: " + token); + } + } + EPerson eperson = createEPersonFromRestObject(context, epersonRest); + + return converter.toRest(eperson, utils.obtainProjection()); + } + private EPerson createEPersonFromRestObject(Context context, EPersonRest epersonRest) throws AuthorizeException { EPerson eperson = null; try { eperson = es.create(context); @@ -84,8 +124,61 @@ protected EPersonRest createAndReturn(Context context) } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } + return eperson; + } - return converter.toRest(eperson, utils.obtainProjection()); + private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, String token) + throws AuthorizeException, SQLException { + RegistrationData registrationData = registrationDataService.findByToken(context, token); + if (registrationData == null) { + throw new AccessDeniedException("The token given as parameter: " + token + " does not exist" + + " in the database"); + } + if (es.findByEmail(context, registrationData.getEmail()) != null) { + throw new AccessDeniedException("The token given already contains an email address that resolves" + + "to an eperson"); + } + String emailFromJson = epersonRest.getEmail(); + if (StringUtils.isNotBlank(emailFromJson)) { + if (!StringUtils.equalsIgnoreCase(registrationData.getEmail(), emailFromJson)) { + throw new AccessDeniedException("The email resulting from the token does not match the email given" + + " in the json body. Email from token: " + + registrationData.getEmail() + " email from the json body: " + + emailFromJson); + } + } + if (epersonRest.isSelfRegistered() != null && !epersonRest.isSelfRegistered()) { + throw new AccessDeniedException("The self registered property cannot be set to false using this method" + + " with a token"); + } + checkRequiredProperties(epersonRest); + AuthorizationFeature epersonRegistration = authorizationFeatureService.find("epersonRegistration"); + Site site = siteService.findSite(context); + SiteRest siteRest = converter.toRest(site, Projection.DEFAULT); + if (!authorizationFeatureService.isAuthorized(context, epersonRegistration, siteRest)) { + throw new AccessDeniedException( + "Registration is disabled, you are not authorized to create a new Authorization"); + } + EPerson ePerson = createEPersonFromRestObject(context, epersonRest); + accountService.deleteToken(context, token); + return converter.toRest(ePerson, utils.obtainProjection()); + } + + private void checkRequiredProperties(EPersonRest epersonRest) { + MetadataRest metadataRest = epersonRest.getMetadata(); + if (metadataRest != null) { + List epersonFirstName = metadataRest.getMap().get("eperson.firstname"); + List epersonLastName = metadataRest.getMap().get("eperson.lastname"); + if (epersonFirstName == null || epersonLastName == null || + epersonFirstName.isEmpty() || epersonLastName.isEmpty()) { + throw new AccessDeniedException("The eperson.firstname and eperson.lastname values need to be " + + "filled in"); + } + } + String password = epersonRest.getPassword(); + if (StringUtils.isBlank(password)) { + throw new AccessDeniedException("the password cannot be left blank"); + } } @Override diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 7b6513f766d6..5a1d47680c52 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -15,6 +15,8 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -26,6 +28,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; import javax.ws.rs.core.MediaType; @@ -58,6 +61,7 @@ import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MvcResult; public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { @@ -1803,4 +1807,365 @@ public void registerNewAccountPatchUpdatePasswordRandomUserUuidFail() throws Exc assertFalse(registrationDataService.findByEmail(context, newRegisterEmail) == null); } + @Test + public void postEPersonWithTokenWithoutEmailProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"password\":\"somePassword\"," + + "\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + MvcResult mvcResult = getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andReturn(); + + String content = mvcResult.getResponse().getContentAsString(); + Map map = mapper.readValue(content, Map.class); + String epersonUuid = String.valueOf(map.get("uuid")); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + context.turnOffAuthorisationSystem(); + ePersonService.delete(context, createdEPerson); + context.restoreAuthSystemState(); + } + + @Test + public void postEPersonWithTokenWithEmailProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + MvcResult mvcResult = getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.email", is(newRegisterEmail)), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andReturn(); + + String content = mvcResult.getResponse().getContentAsString(); + Map map = mapper.readValue(content, Map.class); + String epersonUuid = String.valueOf(map.get("uuid")); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + ePersonService.delete(context, createdEPerson); + context.restoreAuthSystemState(); + } + + @Test + public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"email\":\"" + newRegisterEmail + + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + MvcResult mvcResult = getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.email", is(newRegisterEmail)), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andReturn(); + + String content = mvcResult.getResponse().getContentAsString(); + Map map = mapper.readValue(content, Map.class); + String epersonUuid = String.valueOf(map.get("uuid")); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + ePersonService.delete(context, createdEPerson); + context.restoreAuthSystemState(); + } + + @Test + public void postEPersonWithTokenWithTwoTokensDifferentEmailProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String newRegisterEmailTwo = "new-register-two@fake-email.com"; + RegistrationRest registrationRestTwo = new RegistrationRest(); + registrationRestTwo.setEmail(newRegisterEmailTwo); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRestTwo))) + .andExpect(status().isCreated()); + String newRegisterTokenTwo = registrationDataService.findByEmail(context, newRegisterEmailTwo).getToken(); + + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmailTwo + + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmailTwo); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + assertNotNull(registrationDataService.findByToken(context, newRegisterTokenTwo)); + } + + @Test + public void postEPersonWithRandomTokenWithEmailProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", "randomToken") + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } + + @Test + public void postEPersonWithTokenWithEmailAndSelfRegisteredFalseProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":false,\"email\":\"" + newRegisterEmail + + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } + + @Test + public void postEPersonWithTokenWithoutLastNameProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]},\"selfRegistered\":true," + + "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } + + @Test + public void postEPersonWithTokenWithoutFirstNameProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true," + + "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } + + @Test + public void postEPersonWithTokenWithoutPasswordProperty() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]}," + + "\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } + + @Test + public void postEPersonWithWrongToken() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + String newEmail = "new-email@fake-email.com"; + + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(eperson.getEmail()); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String forgotPasswordToken = registrationDataService.findByEmail(context, eperson.getEmail()).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"password\":\"somePassword\"," + + "\"type\":\"eperson\"}"; + + String token = getAuthToken(admin.getEmail(), password); + getClient(token).perform(post("/api/eperson/epersons") + .param("token", forgotPasswordToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isForbidden()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, forgotPasswordToken)); + } } From 0920de7b21e7f5ccb6d88ae2128e6b533e0910bd Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Mon, 20 Apr 2020 12:57:49 +0200 Subject: [PATCH 16/73] 70404: CC license (REST): Patch submission (Add) --- .../license/CCLicenseConnectorService.java | 21 ++++ .../CCLicenseConnectorServiceImpl.java | 55 +++++++- .../license/CreativeCommonsServiceImpl.java | 118 ++++++++++++++++-- .../service/CreativeCommonsService.java | 44 ++++++- .../submit/AbstractRestProcessingStep.java | 2 +- .../impl/CCLicenseAddPatchOperation.java | 66 ++++++++++ .../spring/spring-dspace-core-services.xml | 4 + dspace/config/item-submission.xml | 4 +- 8 files changed, 296 insertions(+), 18 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java index 48ed5f7200d3..0c061d2d6428 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorService.java @@ -7,8 +7,11 @@ */ package org.dspace.license; +import java.io.IOException; import java.util.Map; +import org.jdom.Document; + /** * Service interface class for the Creative commons license connector service. * The implementation of this class is responsible for all the calls to the CC license API and parsing the response @@ -27,6 +30,7 @@ public interface CCLicenseConnectorService { /** * Retrieve the CC License URI based on the provided license id, language and answers to the field questions from * the CC License API + * * @param licenseId - the ID of the license * @param language - the language for which to retrieve the full answerMap * @param answerMap - the answers to the different field questions @@ -36,4 +40,21 @@ public String retrieveRightsByQuestion(String licenseId, String language, Map answerMap); + /** + * Retrieve the license RDF document based on the license URI + * + * @param licenseURI - The license URI for which to retrieve the license RDF document + * @return the license RDF document + * @throws IOException + */ + public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException; + + /** + * Retrieve the license Name from the license document + * + * @param doc - The license document from which to retrieve the license name + * @return the license name + */ + public String retrieveLicenseName(final Document doc); + } diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 7fedc7e2e1f3..9813a5c31bec 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -8,7 +8,11 @@ package org.dspace.license; import java.io.IOException; +import java.io.InputStream; import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; @@ -31,6 +35,7 @@ import org.jaxen.JaxenException; import org.jaxen.jdom.JDOMXPath; import org.jdom.Attribute; +import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; @@ -237,6 +242,7 @@ private String getSingleNodeValue(final Object t, String query) throws JaxenExce /** * Retrieve the CC License URI based on the provided license id, language and answers to the field questions from * the CC License API + * * @param licenseId - the ID of the license * @param language - the language for which to retrieve the full answerMap * @param answerMap - the answers to the different field questions @@ -273,7 +279,7 @@ public String retrieveRightsByQuestion(String licenseId, /** * Parse the response for the CC License URI request and return the corresponding CC License URI * - * @param response for a specific CC License URI response + * @param response for a specific CC License URI response * @return the corresponding CC License URI as a string * @throws IOException * @throws JaxenException @@ -314,5 +320,52 @@ private String createAnswerString(final Map parameterMap) { return sb.toString(); } + /** + * Retrieve the license RDF document based on the license URI + * + * @param licenseURI - The license URI for which to retrieve the license RDF document + * @return the license RDF document + * @throws IOException + */ + @Override + public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { + String ccLicenseUrl = configurationService.getProperty("cc.api.rooturl"); + + String issueUrl = ccLicenseUrl + "/details?license-uri=" + licenseURI; + + URL request_url; + try { + request_url = new URL(issueUrl); + } catch (MalformedURLException e) { + return null; + } + URLConnection connection = request_url.openConnection(); + connection.setDoOutput(true); + try { + // parsing document from input stream + InputStream stream = connection.getInputStream(); + Document doc = parser.build(stream); + return doc; + + } catch (Exception e) { + log.error("Error while retrieving the license document for URI: " + licenseURI, e); + } + return null; + } + + /** + * Retrieve the license Name from the license document + * + * @param doc - The license document from which to retrieve the license name + * @return the license name + */ + public String retrieveLicenseName(final Document doc) { + try { + return getSingleNodeValue(doc, "//result/license-uri"); + } catch (JaxenException e) { + log.error("Error while retrieving the license name from the license document", e); + } + return null; + } } diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index 928e4b689111..a1077af1efd3 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -181,8 +181,17 @@ public void setLicense(Context context, Item item, } + /** + * Removes the license file from the item + * + * @param context - The relevant DSpace Context + * @param item - The item from which the license file needs to be removed + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ @Override - public void removeLicense(Context context, Item item) + public void removeLicenseFile(Context context, Item item) throws SQLException, IOException, AuthorizeException { // remove CC license bundle if one exists List bundles = itemService.getBundles(item, CC_BUNDLE_NAME); @@ -414,24 +423,49 @@ public String getCCField(String fieldId) { return configurationService.getProperty("cc.license." + fieldId); } + /** + * Remove license information, delete also the bitstream + * + * @param context - DSpace Context + * @param item - the item + * @throws AuthorizeException Exception indicating the current user of the context does not have permission + * to perform a particular action. + * @throws IOException A general class of exceptions produced by failed or interrupted I/O operations. + * @throws SQLException An exception that provides information on a database access error or other errors. + */ @Override - public void removeLicense(Context context, LicenseMetadataValue uriField, - LicenseMetadataValue nameField, Item item) + public void removeLicense(Context context, Item item) throws AuthorizeException, IOException, SQLException { + + String uriField = getCCField("uri"); + String nameField = getCCField("name"); + + String licenseUri = itemService.getMetadata(item, uriField); + // only remove any previous licenses - String licenseUri = uriField.ccItemValue(item); if (licenseUri != null) { - uriField.removeItemValue(context, item, licenseUri); + removeLicenseField(context, item, uriField); if (configurationService.getBooleanProperty("cc.submit.setname")) { - String licenseName = nameField.keyedItemValue(item, licenseUri); - nameField.removeItemValue(context, item, licenseName); + removeLicenseField(context, item, nameField); } if (configurationService.getBooleanProperty("cc.submit.addbitstream")) { - removeLicense(context, item); + removeLicenseFile(context, item); } } } + private void removeLicenseField(Context context, Item item, String field) throws SQLException { + String[] params = splitField(field); + itemService.clearMetadata(context, item, params[0], params[1], params[2], params[3]); + + } + + private void addLicenseField(Context context, Item item, String field, String value) throws SQLException { + String[] params = splitField(field); + itemService.addMetadata(context, item, params[0], params[1], params[2], params[3], value); + + } + /** * Find all CC Licenses using the default language found in the configuration * @@ -623,4 +657,72 @@ private CCLicenseField findCCLicenseField(final String field, final List fullAnswerMap); + /** + * Update the license of the item with a new one based on the provided license URI + * + * @param context - The relevant DSpace context + * @param licenseUri - The license URI to be used in the update + * @param item - The item for which to update the license + * @return true when the update was successful, false when not + * @throws AuthorizeException + * @throws SQLException + */ + public boolean updateLicense(final Context context, String licenseUri, final Item item) + throws AuthorizeException, SQLException; + + /** + * Add a new license to the item + * + * @param context - The relevant Dspace context + * @param item - The item to which the license will be added + * @param licenseUri - The license URI to add + * @param licenseName - The license name to add + * @param doc - The license to document to add + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ + public void addLicense(Context context, Item item, String licenseUri, String licenseName, Document doc) + throws SQLException, IOException, AuthorizeException; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java index 9989f6ca07f5..2dea6a2ce5db 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java @@ -35,7 +35,7 @@ public interface AbstractRestProcessingStep extends ListenerProcessingStep { public static final String UPLOAD_STEP_MOVE_OPERATION_ENTRY = "bitstreammove"; public static final String UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY = "accessConditions"; public static final String LICENSE_STEP_OPERATION_ENTRY = "granted"; - public static final String CCLICENSE_STEP_OPERATION_ENTRY = "ccLicense"; + public static final String CCLICENSE_STEP_OPERATION_ENTRY = "cclicense/uri"; public static final String UPLOAD_STEP_METADATA_PATH = "metadata"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java new file mode 100644 index 000000000000..46a0f5ce95a4 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java @@ -0,0 +1,66 @@ +/** + * 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.submit.factory.impl; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.content.InProgressSubmission; +import org.dspace.content.Item; +import org.dspace.core.Context; +import org.dspace.license.service.CreativeCommonsService; +import org.dspace.services.model.Request; +import org.springframework.beans.factory.annotation.Autowired; + + +/** + * Submission "add" PATCH operation + * + * To add or update the Creative Commons License of a workspace item. + * When the item already has a Creative Commons License, the license will be replaced with a new one. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/31599 -H "Content-Type: + * application/json" -d '[{ "op": "add", "path": "/sections/cclicense/uri", + * "value":"http://creativecommons.org/licenses/by-nc-sa/3.0/us/"}]' + * + * + */ +public class CCLicenseAddPatchOperation extends AddPatchOperation { + + @Autowired + CreativeCommonsService creativeCommonsService; + + @Override + protected Class getArrayClassForEvaluation() { + return String[].class; + } + + @Override + protected Class getClassForEvaluation() { + return String.class; + } + + @Override + void add(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) + throws Exception { + + + String licenseUri = null; + if (value instanceof String) { + licenseUri = (String) value; + } + + if (StringUtils.isBlank(licenseUri)) { + throw new IllegalArgumentException( + "Value is not a valid license URI"); + } + + Item item = source.getItem(); + creativeCommonsService.updateLicense(context, licenseUri, item); + } + +} diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml index 61459f11d64f..2b37ea5bddf7 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml @@ -52,6 +52,10 @@ + + + diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index d1f755d2937f..481b5081768b 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -115,7 +115,7 @@ - submit.progressbar.CClicense + submit.progressbar.CClicense org.dspace.app.rest.submit.step.CCLicenseStep cclicense @@ -203,7 +203,7 @@ - + From 09c0f62561795f3fefd22cac929d1cc9e6036f21 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 22 Apr 2020 14:39:53 +0200 Subject: [PATCH 17/73] 70505: Patch submission (Add): Bugfixing & tests --- .../CCLicenseConnectorServiceImpl.java | 4 +- .../license/CreativeCommonsServiceImpl.java | 3 + .../dspaceFolder/config/item-submission.xml | 8 +- .../MockCCLicenseConnectorServiceImpl.java | 32 +++++- .../impl/CCLicenseAddPatchOperation.java | 11 ++- .../rest/CCLicenseAddPatchOperationIT.java | 99 +++++++++++++++++++ .../SubmissionDefinitionsControllerIT.java | 2 +- .../MockCCLicenseConnectorServiceImpl.java | 31 ++++++ .../org/dspace/license/cc-license-rdf.xml | 31 ++++++ 9 files changed, 209 insertions(+), 12 deletions(-) create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java create mode 100644 dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index 9813a5c31bec..a237a9198405 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -52,7 +52,7 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, private Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLicenseConnectorServiceImpl.class); private CloseableHttpClient client; - private SAXBuilder parser = new SAXBuilder(); + protected SAXBuilder parser = new SAXBuilder(); private String postArgument = "answers"; private String postAnswerFormat = @@ -361,7 +361,7 @@ public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { */ public String retrieveLicenseName(final Document doc) { try { - return getSingleNodeValue(doc, "//result/license-uri"); + return getSingleNodeValue(doc, "//result/license-name"); } catch (JaxenException e) { log.error("Error while retrieving the license name from the license document", e); } diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index a1077af1efd3..67a9e4e06d2b 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -672,6 +672,9 @@ public boolean updateLicense(final Context context, final String licenseUri, fin throws AuthorizeException, SQLException { try { Document doc = ccLicenseConnectorService.retrieveLicenseRDFDoc(licenseUri); + if (doc == null) { + return false; + } String licenseName = ccLicenseConnectorService.retrieveLicenseName(doc); if (StringUtils.isBlank(licenseName)) { return false; diff --git a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml index de19ef728705..d78b14c43776 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -82,9 +82,9 @@ - + submit.progressbar.CClicense + org.dspace.app.rest.submit.step.CCLicenseStep + cclicense - + diff --git a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index 226e2aa77ba2..41cd9805f6fe 100644 --- a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -7,11 +7,17 @@ */ package org.dspace.license; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.jdom.Document; +import org.jdom.JDOMException; + /** * Mock implementation for the Creative commons license connector service. * This class will return a structure of CC Licenses similar to the CC License API but without having to contact it @@ -20,7 +26,6 @@ public class MockCCLicenseConnectorServiceImpl extends CCLicenseConnectorService /** * Retrieves mock CC Licenses for the provided language - * * @param language - the language * @return a map of mocked licenses with the id and the license */ @@ -90,4 +95,29 @@ public String retrieveRightsByQuestion(final String licenseId, return "mock-license-uri"; } + + /** + * Retrieve a mock license RDF document. + * When the uri contains "invalid", null will be returned to simulate that no document was found for the provided + * URI + * + * @param licenseURI - The license URI for which to retrieve the license RDF document + * @return a mock license RDF document or null when the URI contains invalid + * @throws IOException + */ + public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { + if (!StringUtils.contains(licenseURI, "invalid")) { + try { + + InputStream cclicense = getClass().getResourceAsStream("cc-license-rdf.xml"); + + Document doc = parser.build(cclicense); + return doc; + } catch (JDOMException e) { + e.printStackTrace(); + } + } + return null; + } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java index 46a0f5ce95a4..e3286551d54f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseAddPatchOperation.java @@ -27,7 +27,6 @@ * application/json" -d '[{ "op": "add", "path": "/sections/cclicense/uri", * "value":"http://creativecommons.org/licenses/by-nc-sa/3.0/us/"}]' * - * */ public class CCLicenseAddPatchOperation extends AddPatchOperation { @@ -46,7 +45,7 @@ protected Class getClassForEvaluation() { @Override void add(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) - throws Exception { + throws Exception { String licenseUri = null; @@ -56,11 +55,15 @@ void add(Context context, Request currentRequest, InProgressSubmission source, S if (StringUtils.isBlank(licenseUri)) { throw new IllegalArgumentException( - "Value is not a valid license URI"); + "Value is not a valid license URI"); } Item item = source.getItem(); - creativeCommonsService.updateLicense(context, licenseUri, item); + boolean updateLicense = creativeCommonsService.updateLicense(context, licenseUri, item); + if (!updateLicense) { + throw new IllegalArgumentException("The license uri: " + licenseUri + ", could not be resolved to a " + + "CC license"); + } } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java new file mode 100644 index 000000000000..ba58e5f4893f --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java @@ -0,0 +1,99 @@ +/** + * 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; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.MediaType; + +import org.dspace.app.rest.builder.CollectionBuilder; +import org.dspace.app.rest.builder.CommunityBuilder; +import org.dspace.app.rest.builder.WorkspaceItemBuilder; +import org.dspace.app.rest.model.patch.AddOperation; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.WorkspaceItem; +import org.junit.Test; + +/** + * Class to the methods from the CCLicenseAddPatchOperation + * Since the CC Licenses are obtained from the CC License API, a mock service has been implemented + * This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the + * CC License API. + * Refer to {@link org.dspace.license.MockCCLicenseConnectorServiceImpl} for more information + */ +public class CCLicenseAddPatchOperationIT extends AbstractControllerIntegrationTest { + + + @Test + public void patchSubmissionCCLicense() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("Community") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Collection") + .build(); + + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Workspace Item") + .build(); + + String adminToken = getAuthToken(admin.getEmail(), password); + + List ops = new ArrayList(); + AddOperation addOperation = new AddOperation("/sections/cclicense/uri", "license-uri"); + + ops.add(addOperation); + String patchBody = getPatchContent(ops); + + + getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()); + } + + @Test + public void patchSubmissionCCLicenseInvalid() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("Community") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Collection") + .build(); + + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Workspace Item") + .build(); + + String adminToken = getAuthToken(admin.getEmail(), password); + + List ops = new ArrayList(); + AddOperation addOperation = new AddOperation("/sections/cclicense/uri", "invalid-license-uri"); + + ops.add(addOperation); + String patchBody = getPatchContent(ops); + + + getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isInternalServerError()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java index 38d22fdf04eb..dbee267740f1 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionDefinitionsControllerIT.java @@ -204,7 +204,7 @@ public void findSections() throws Exception { // We expect the content type to be "application/hal+json;charset=UTF-8" .andExpect(content().contentType(contentType)) // Match only that a section exists with a submission configuration behind - .andExpect(jsonPath("$._embedded.submissionsections", hasSize(5))) + .andExpect(jsonPath("$._embedded.submissionsections", hasSize(6))) .andExpect(jsonPath("$._embedded.submissionsections", Matchers.hasItem( allOf( diff --git a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index 23d395ec2b56..41cd9805f6fe 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -7,11 +7,17 @@ */ package org.dspace.license; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.jdom.Document; +import org.jdom.JDOMException; + /** * Mock implementation for the Creative commons license connector service. * This class will return a structure of CC Licenses similar to the CC License API but without having to contact it @@ -89,4 +95,29 @@ public String retrieveRightsByQuestion(final String licenseId, return "mock-license-uri"; } + + /** + * Retrieve a mock license RDF document. + * When the uri contains "invalid", null will be returned to simulate that no document was found for the provided + * URI + * + * @param licenseURI - The license URI for which to retrieve the license RDF document + * @return a mock license RDF document or null when the URI contains invalid + * @throws IOException + */ + public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { + if (!StringUtils.contains(licenseURI, "invalid")) { + try { + + InputStream cclicense = getClass().getResourceAsStream("cc-license-rdf.xml"); + + Document doc = parser.build(cclicense); + return doc; + } catch (JDOMException e) { + e.printStackTrace(); + } + } + return null; + } + } diff --git a/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml b/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml new file mode 100644 index 000000000000..5ff75ee4c747 --- /dev/null +++ b/dspace-server-webapp/src/test/resources/org/dspace/license/cc-license-rdf.xml @@ -0,0 +1,31 @@ + + + http://creativecommons.org/licenses/by-nc-sa/4.0/ + Attribution-NonCommercial-ShareAlike 4.0 International + false + + + + + + + + + + + + + + + + + + + + + + + + + Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. +
From a27b64c88f98732b3c4db7b62567ef6078f82ba5 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 22 Apr 2020 18:00:43 +0200 Subject: [PATCH 18/73] 70506: CC license (REST): Patch submission (Remove) --- .../impl/CCLicenseRemovePatchOperation.java | 50 ++++++++++ .../spring/spring-dspace-core-services.xml | 4 + .../rest/CCLicenseAddPatchOperationIT.java | 17 +++- .../rest/CCLicenseRemovePatchOperationIT.java | 99 +++++++++++++++++++ 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java new file mode 100644 index 000000000000..19229a4f72b5 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java @@ -0,0 +1,50 @@ +/** + * 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.submit.factory.impl; + +import org.dspace.content.InProgressSubmission; +import org.dspace.content.Item; +import org.dspace.core.Context; +import org.dspace.license.service.CreativeCommonsService; +import org.dspace.services.model.Request; +import org.springframework.beans.factory.annotation.Autowired; + + +/** + * Submission "remove" PATCH operation + * + * To remove the Creative Commons License of a workspace item. + * + * Example: + * curl -X PATCH http://${dspace.server.url}/api/submission/workspaceitems/31599 -H "Content-Type: + * application/json" -d '[{ "op": "remove", "path": "/sections/cclicense/uri"}]' + * + */ +public class CCLicenseRemovePatchOperation extends RemovePatchOperation { + + @Autowired + CreativeCommonsService creativeCommonsService; + + @Override + protected Class getArrayClassForEvaluation() { + return String[].class; + } + + @Override + protected Class getClassForEvaluation() { + return String.class; + } + + @Override + void remove(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) + throws Exception { + Item item = source.getItem(); + creativeCommonsService.removeLicense(context, item); + } + +} diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml index 2b37ea5bddf7..9faad9198501 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml @@ -81,6 +81,10 @@
+ + + diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java index ba58e5f4893f..76b260f194de 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java @@ -7,7 +7,11 @@ */ package org.dspace.app.rest; +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.ArrayList; @@ -26,7 +30,7 @@ import org.junit.Test; /** - * Class to the methods from the CCLicenseAddPatchOperation + * Class to test the methods from the CCLicenseAddPatchOperation * Since the CC Licenses are obtained from the CC License API, a mock service has been implemented * This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the * CC License API. @@ -54,7 +58,8 @@ public void patchSubmissionCCLicense() throws Exception { String adminToken = getAuthToken(admin.getEmail(), password); List ops = new ArrayList(); - AddOperation addOperation = new AddOperation("/sections/cclicense/uri", "license-uri"); + AddOperation addOperation = new AddOperation("/sections/cclicense/uri", + "http://creativecommons.org/licenses/by-nc-sa/4.0/"); ops.add(addOperation); String patchBody = getPatchContent(ops); @@ -63,7 +68,13 @@ public void patchSubmissionCCLicense() throws Exception { getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()); + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.cclicense", allOf( + hasJsonPath("$.uri", is("http://creativecommons.org/licenses/by-nc-sa/4.0/")), + hasJsonPath("$.rights", + is("Attribution-NonCommercial-ShareAlike 4.0 International")), + hasJsonPath("$.file.name", is("license_rdf")) + ))); } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java new file mode 100644 index 000000000000..b908b7acd7a2 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java @@ -0,0 +1,99 @@ +/** + * 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; + +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.MediaType; + +import org.dspace.app.rest.builder.CollectionBuilder; +import org.dspace.app.rest.builder.CommunityBuilder; +import org.dspace.app.rest.builder.WorkspaceItemBuilder; +import org.dspace.app.rest.model.patch.AddOperation; +import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.model.patch.RemoveOperation; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.WorkspaceItem; +import org.junit.Test; + +/** + * Class to test the methods from the CCLicenseRemovePatchOperation + * Since the CC Licenses are obtained from the CC License API, a mock service has been implemented + * This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the + * CC License API. + * Refer to {@link org.dspace.license.MockCCLicenseConnectorServiceImpl} for more information + */ +public class CCLicenseRemovePatchOperationIT extends AbstractControllerIntegrationTest { + + + @Test + public void patchRemoveSubmissionCCLicense() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("Community") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Collection") + .build(); + + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Workspace Item") + .build(); + + String adminToken = getAuthToken(admin.getEmail(), password); + + // First add a license and verify it is added + List ops = new ArrayList(); + AddOperation addOperation = new AddOperation("/sections/cclicense/uri", + "http://creativecommons.org/licenses/by-nc-sa/4.0/"); + + ops.add(addOperation); + String patchBody = getPatchContent(ops); + + + getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.cclicense", allOf( + hasJsonPath("$.uri", is("http://creativecommons.org/licenses/by-nc-sa/4.0/")), + hasJsonPath("$.rights", + is("Attribution-NonCommercial-ShareAlike 4.0 International")), + hasJsonPath("$.file.name", is("license_rdf")) + ))); + + + + // Remove the license again and verify it is removed + + List removeOps = new ArrayList(); + RemoveOperation removeOperation = new RemoveOperation("/sections/cclicense/uri"); + + removeOps.add(removeOperation); + String removePatch = getPatchContent(removeOps); + + + getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + .content(removePatch) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense")))); + } +} From 36264cca50439249f97b7a3ebbdc3459f8c07f8a Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Thu, 23 Apr 2020 14:57:03 +0200 Subject: [PATCH 19/73] Submission CreativeCommons license rest evaluator plugin addition so that it plays nicely with the access restrictions --- ...ubmissionCCLicenseRestEvaluatorPlugin.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java new file mode 100644 index 000000000000..251717d79991 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java @@ -0,0 +1,21 @@ +package org.dspace.app.rest.security; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.model.SubmissionCCLicenseRest; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import java.io.Serializable; + +@Component +public class SubmissionCCLicenseRestEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin { + + @Override + public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType, + DSpaceRestPermission restPermission) { + if (!StringUtils.equalsIgnoreCase(SubmissionCCLicenseRest.NAME, targetType)) { + return false; + } + return true; + } +} From 94d9f623587bd0f48fb64ee8c62706dfd8031ac1 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Mon, 27 Apr 2020 14:52:00 +0200 Subject: [PATCH 20/73] [Task 70399] added context authorizations modifications for eperson post with token and added its to verify functionality --- .../repository/EPersonRestRepository.java | 2 + .../app/rest/EPersonRestRepositoryIT.java | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index 1309e11713c5..fa69d152539c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -159,7 +159,9 @@ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, St throw new AccessDeniedException( "Registration is disabled, you are not authorized to create a new Authorization"); } + context.turnOffAuthorisationSystem(); EPerson ePerson = createEPersonFromRestObject(context, epersonRest); + context.restoreAuthSystemState(); accountService.deleteToken(context, token); return converter.toRest(ePerson, utils.obtainProjection()); } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 5a1d47680c52..53a054e2f0da 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -127,6 +127,34 @@ public void createTest() throws Exception { // TODO cleanup the context!!! } + @Test + public void createAnonAccessDeniedTest() throws Exception { + context.turnOffAuthorisationSystem(); + // we should check how to get it from Spring + ObjectMapper mapper = new ObjectMapper(); + EPersonRest data = new EPersonRest(); + EPersonRest dataFull = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + data.setEmail("createtest@fake-email.com"); + data.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + data.setMetadata(metadataRest); + dataFull.setEmail("createtestFull@fake-email.com"); + dataFull.setCanLogIn(true); + dataFull.setMetadata(metadataRest); + + getClient().perform(post("/api/eperson/epersons") + .content(mapper.writeValueAsBytes(data)) + .contentType(contentType) + .param("projection", "full")) + .andExpect(status().isUnauthorized()); + } + @Test public void findAllTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -2168,4 +2196,51 @@ public void postEPersonWithWrongToken() throws Exception { assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, forgotPasswordToken)); } + + @Test + public void postEPersonWithTokenWithEmailPropertyAnonUser() throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + + String newRegisterEmail = "new-register@fake-email.com"; + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(newRegisterEmail); + getClient().perform(post("/api/eperson/registrations") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsBytes(registrationRest))) + .andExpect(status().isCreated()); + String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + + String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.email", is(newRegisterEmail)), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andReturn(); + + String content = mvcResult.getResponse().getContentAsString(); + Map map = mapper.readValue(content, Map.class); + String epersonUuid = String.valueOf(map.get("uuid")); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + ePersonService.delete(context, createdEPerson); + context.restoreAuthSystemState(); + } } From b271ae662130405aa7b24c5a0cb6dbcec25aba68 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Tue, 28 Apr 2020 17:17:48 +0200 Subject: [PATCH 21/73] 70338: Cleanup CC license code --- .../java/org/dspace/license/CCLookup.java | 435 ------------------ .../license/CreativeCommonsServiceImpl.java | 33 -- .../dspace/license/LicenseMetadataValue.java | 129 ------ .../service/CreativeCommonsService.java | 13 - .../MockCCLicenseConnectorServiceImpl.java | 10 +- ...ubmissionCCLicenseRestEvaluatorPlugin.java | 11 +- .../rest/CCLicenseAddPatchOperationIT.java | 15 + .../rest/CCLicenseRemovePatchOperationIT.java | 3 +- .../MockCCLicenseConnectorServiceImpl.java | 10 +- 9 files changed, 40 insertions(+), 619 deletions(-) delete mode 100644 dspace-api/src/main/java/org/dspace/license/CCLookup.java delete mode 100644 dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java diff --git a/dspace-api/src/main/java/org/dspace/license/CCLookup.java b/dspace-api/src/main/java/org/dspace/license/CCLookup.java deleted file mode 100644 index b7ddfa2314b4..000000000000 --- a/dspace-api/src/main/java/org/dspace/license/CCLookup.java +++ /dev/null @@ -1,435 +0,0 @@ -/** - * 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.license; - -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; - -import org.apache.logging.log4j.Logger; -import org.dspace.license.factory.LicenseServiceFactory; -import org.dspace.license.service.CreativeCommonsService; -import org.dspace.services.ConfigurationService; -import org.dspace.services.factory.DSpaceServicesFactory; -import org.jaxen.JaxenException; -import org.jaxen.jdom.JDOMXPath; -import org.jdom.Attribute; -import org.jdom.Document; -import org.jdom.Element; -import org.jdom.JDOMException; -import org.jdom.input.SAXBuilder; - - -/** - * A wrapper around Creative Commons REST web services. - * - * @author Wendy Bossons - */ -public class CCLookup { - - /** - * log4j logger - */ - private static Logger log = org.apache.logging.log4j.LogManager.getLogger(CCLookup.class); - - private String cc_root; - private String jurisdiction; - private List lcFilter = new ArrayList(); - - private Document license_doc = null; - private String rdfString = null; - private String errorMessage = null; - private boolean success = false; - - private SAXBuilder parser = new SAXBuilder(); - private List licenses = new ArrayList(); - private List licenseFields = new ArrayList(); - - protected CreativeCommonsService creativeCommonsService = LicenseServiceFactory.getInstance() - .getCreativeCommonsService(); - - /** - * Constructs a new instance with the default web services root. - */ - public CCLookup() { - super(); - - ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); - - cc_root = configurationService.getProperty("cc.api.rooturl"); - - String jurisProp = configurationService.getProperty("cc.license.jurisdiction"); - jurisdiction = (jurisProp != null) ? jurisProp : ""; - - String[] filters = configurationService.getArrayProperty("cc.license.classfilter"); - if (filters != null) { - for (String name : filters) { - lcFilter.add(name.trim()); - } - } - } - - /** - * Returns the id for a particular CCLicense label. Returns an - * empty string if no match is found. - * - * @param class_label The CCLicense label to find. - * @return Returns a String containing the License class ID if the label - * is found; if not found, returns an empty string. - * @see CCLicense - */ - public String getLicenseId(String class_label) { - for (int i = 0; i < this.licenses.size(); i++) { - if (((CCLicense) this.licenses.get(i)).getLicenseName().equals(class_label)) { - return ((CCLicense) this.licenses.get(i)).getLicenseId(); - } - } - - return ""; - } - - /** - * Queries the web service for the available licenses. - * - * @param language The language to request labels and description strings in. - * @return Returns a Map of CCLicense objects. - * @see Map - * @see CCLicense - */ - public Collection getLicenses(String language) { - - // create XPath expressions - try { - JDOMXPath xp_Licenses = new JDOMXPath("//licenses/license"); - JDOMXPath xp_LicenseID = new JDOMXPath("@id"); - URL classUrl = new URL(this.cc_root + "/?locale=" + language); - Document classDoc = this.parser.build(classUrl); - // extract the identifiers and labels using XPath - List results = xp_Licenses.selectNodes(classDoc); - // populate licenses container - this.licenses.clear(); - for (int i = 0; i < results.size(); i++) { - Element license = results.get(i); - // add if not filtered - String liD = ((Attribute) xp_LicenseID.selectSingleNode(license)).getValue(); - if (!lcFilter.contains(liD)) { -// this.licenses.add(new CCLicense(liD, license.getText(), i)); - } - } - } catch (JaxenException jaxen_e) { - return null; - } catch (JDOMException jdom_e) { - return null; - } catch (IOException io_e) { - return null; - } catch (Exception e) { - // do nothing... but we should - return null; - } - - return licenses; - } - - - /** - * Queries the web service for a set of licenseFields for a particular license class. - * - * @param license A String specifying the CCLicense identifier to - * retrieve fields for. - * @param language the locale string - * @return A Collection of LicenseField objects. - * @see CCLicense - */ - public Collection getLicenseFields(String license, String language) { - - JDOMXPath xp_LicenseField; - JDOMXPath xp_LicenseID; - JDOMXPath xp_FieldType; - JDOMXPath xp_Description; - JDOMXPath xp_Label; - JDOMXPath xp_Enum; - - Document fieldDoc; - - URL classUrl; - List results = null; - List enumOptions = null; - - // create XPath expressions - try { - xp_LicenseField = new JDOMXPath("//field"); - xp_LicenseID = new JDOMXPath("@id"); - xp_Description = new JDOMXPath("description"); - xp_Label = new JDOMXPath("label"); - xp_FieldType = new JDOMXPath("type"); - xp_Enum = new JDOMXPath("enum"); - - } catch (JaxenException e) { - return null; - } - - // retrieve and parse the license class document - try { - classUrl = new URL(this.cc_root + "/license/" + license + "?locale=" + language); - } catch (Exception err) { - // do nothing... but we should - return null; - } - - // parse the licenses document - try { - fieldDoc = this.parser.build(classUrl); - } catch (JDOMException e) { - return null; - } catch (IOException e) { - return null; - } - - // reset the field definition container - this.licenseFields.clear(); - - // extract the identifiers and labels using XPath - try { - results = xp_LicenseField.selectNodes(fieldDoc); - } catch (JaxenException e) { - return null; - } - - for (int i = 0; i < results.size(); i++) { - Element field = (Element) results.get(i); - -// try { -// // create the field object -// CCLicenseField cclicensefield = new CCLicenseField( -// ((Attribute) xp_LicenseID.selectSingleNode(field)).getValue(), -// ((Element) xp_Label.selectSingleNode(field)).getText()); -// -// // extract additional properties -// cclicensefield.setDescription(((Element) xp_Description.selectSingleNode(field)).getText()); -// cclicensefield.setType(((Element) xp_FieldType.selectSingleNode(field)).getText()); -// -// enumOptions = xp_Enum.selectNodes(field); -// -// for (int j = 0; j < enumOptions.size(); j++) { -// String id = ((Attribute) xp_LicenseID.selectSingleNode(enumOptions.get(j))).getValue(); -// String label = ((Element) xp_Label.selectSingleNode(enumOptions.get(j))).getText(); -// -//// cclicensefield.getEnum().put(id, label); -// -// } // for each enum option -// -// this.licenseFields.add(cclicensefield); -// } catch (JaxenException e) { -// return null; -// } - } - - return licenseFields; - } // licenseFields - - /** - * Passes a set of "answers" to the web service and retrieves a license. - * - * @param licenseId The identifier of the license class being requested. - * @param answers A Map containing the answers to the license fields; - * each key is the identifier of a LicenseField, with the value - * containing the user-supplied answer. - * @param lang The language to request localized elements in. - * @throws IOException if IO error - * @see CCLicense - * @see Map - */ - public void issue(String licenseId, Map answers, String lang) - throws IOException { - - // Determine the issue URL - String issueUrl = this.cc_root + "/license/" + licenseId + "/issue"; - // Assemble the "answers" document - String answer_doc = "\n" + lang + "\n" + "\n"; - Iterator keys = answers.keySet().iterator(); - - try { - String current = (String) keys.next(); - - while (true) { - answer_doc += "<" + current + ">" + (String) answers.get(current) + "\n"; - current = (String) keys.next(); - } - - - } catch (NoSuchElementException e) { - // exception indicates we've iterated through the - // entire collection; just swallow and continue - } - // answer_doc += "\n"; FAILS with jurisdiction argument - answer_doc += "\n\n"; - String post_data; - - try { - post_data = URLEncoder.encode("answers", "UTF-8") + "=" + URLEncoder.encode(answer_doc, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return; - } - - URL post_url; - try { - post_url = new URL(issueUrl); - } catch (MalformedURLException e) { - return; - } - URLConnection connection = post_url.openConnection(); - // this will not be needed after I'm done TODO: remove - connection.setDoOutput(true); - OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); - writer.write(post_data); - writer.flush(); - // end TODO - try { - // parsing document from input stream - java.io.InputStream stream = connection.getInputStream(); - this.license_doc = this.parser.build(stream); - } catch (JDOMException jde) { - log.warn(jde.getMessage()); - } catch (Exception e) { - log.warn(e.getCause()); - } - return; - } // issue - - /** - * Passes a set of "answers" to the web service and retrieves a license. - * - * @param licenseURI The uri of the license. - * - * Note: does not support localization in 1.5 -- not yet - * @throws IOException if IO error - * @see CCLicense - * @see Map - */ - public void issue(String licenseURI) - throws IOException { - - // Determine the issue URL - // Example: http://api.creativecommons.org/rest/1.5/details? - // license-uri=http://creativecommons.org/licenses/by-nc-sa/3.0/ - String issueUrl = cc_root + "/details?license-uri=" + licenseURI; - - URL request_url; - try { - request_url = new URL(issueUrl); - } catch (MalformedURLException e) { - return; - } - URLConnection connection = request_url.openConnection(); - // this will not be needed after I'm done TODO: remove - connection.setDoOutput(true); - try { - // parsing document from input stream - java.io.InputStream stream = connection.getInputStream(); - license_doc = this.parser.build(stream); - } catch (JDOMException jde) { - log.warn(jde.getMessage()); - } catch (Exception e) { - log.warn(e.getCause()); - } - return; - } // issue - - /** - * Retrieves the URI for the license issued. - * - * @return A String containing the URI for the license issued. - */ - public String getLicenseUrl() { - String text = null; - try { - JDOMXPath xp_LicenseName = new JDOMXPath("//result/license-uri"); - text = ((Element) xp_LicenseName.selectSingleNode(this.license_doc)).getText(); - } catch (Exception e) { - log.warn(e.getMessage()); - setSuccess(false); - text = "An error occurred getting the license - uri."; - } finally { - return text; - } - } // getLicenseUrl - - /** - * Retrieves the human readable name for the license issued. - * - * @return A String containing the license name. - */ - public String getLicenseName() { - String text = null; - try { - JDOMXPath xp_LicenseName = new JDOMXPath("//result/license-name"); - text = ((Element) xp_LicenseName.selectSingleNode(this.license_doc)).getText(); - } catch (Exception e) { - log.warn(e.getMessage()); - setSuccess(false); - text = "An error occurred on the license name."; - } finally { - return text; - } - } // getLicenseName - - - public org.jdom.Document getLicenseDocument() { - return this.license_doc; - } - - public String getRdf() - throws IOException { - String result = ""; - try { - result = creativeCommonsService.fetchLicenseRDF(license_doc); - } catch (Exception e) { - log.warn("An error occurred getting the rdf . . ." + e.getMessage()); - setSuccess(false); - } - return result; - } - - public boolean isSuccess() { - setSuccess(false); - JDOMXPath xp_Success; - String text = null; - try { - xp_Success = new JDOMXPath("//message"); - text = ((Element) xp_Success.selectSingleNode(this.license_doc)).getText(); - setErrorMessage(text); - } catch (Exception e) { - log.warn("There was an issue . . . " + text); - setSuccess(true); - } - return this.success; - } - - private void setSuccess(boolean success) { - this.success = success; - } - - public String getErrorMessage() { - return this.errorMessage; - } - - private void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } - -} diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index 67a9e4e06d2b..7a48b6f03b64 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -125,11 +125,6 @@ public void afterPropertiesSet() throws Exception { } - @Override - public boolean isEnabled() { - return true; - } - // create the CC bundle if it doesn't exist // If it does, remove it and create a new one. protected Bundle getCcBundle(Context context, Item item) @@ -201,34 +196,6 @@ public void removeLicenseFile(Context context, Item item) } } - @Override - public boolean hasLicense(Context context, Item item) - throws SQLException, IOException { - // try to find CC license bundle - List bundles = itemService.getBundles(item, CC_BUNDLE_NAME); - - if (bundles.size() == 0) { - return false; - } - - // verify it has correct contents - try { - if ((getLicenseURL(context, item) == null)) { - return false; - } - } catch (AuthorizeException ae) { - return false; - } - - return true; - } - - @Override - public String getLicenseRDF(Context context, Item item) throws SQLException, - IOException, AuthorizeException { - return getStringFromBitstream(context, item, BSN_LICENSE_RDF); - } - @Override public Bitstream getLicenseRdfBitstream(Item item) throws SQLException, IOException, AuthorizeException { diff --git a/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java b/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java deleted file mode 100644 index ec5c9e447b55..000000000000 --- a/dspace-api/src/main/java/org/dspace/license/LicenseMetadataValue.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * 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.license; - -import java.io.IOException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import org.dspace.authorize.AuthorizeException; -import org.dspace.content.Item; -import org.dspace.content.MetadataValue; -import org.dspace.content.factory.ContentServiceFactory; -import org.dspace.content.service.ItemService; -import org.dspace.core.Context; - -/** - * Helper class for using CC-related Metadata fields - * - * @author kevinvandevelde at atmire.com - */ -public class LicenseMetadataValue { - - protected final ItemService itemService; - // Shibboleth for Creative Commons license data - i.e. characters that reliably indicate CC in a URI - protected static final String ccShib = "creativecommons"; - - private String[] params = new String[4]; - - public LicenseMetadataValue(String fieldName) { - if (fieldName != null && fieldName.length() > 0) { - String[] fParams = fieldName.split("\\."); - for (int i = 0; i < fParams.length; i++) { - params[i] = fParams[i]; - } - params[3] = Item.ANY; - } - itemService = ContentServiceFactory.getInstance().getItemService(); - } - - /** - * Returns first value that matches Creative Commons 'shibboleth', - * or null if no matching values. - * NB: this method will succeed only for metadata fields holding CC URIs - * - * @param item - the item to read - * @return value - the first CC-matched value, or null if no such value - */ - public String ccItemValue(Item item) { - List dcvalues = itemService.getMetadata(item, params[0], params[1], params[2], params[3]); - for (MetadataValue dcvalue : dcvalues) { - if ((dcvalue.getValue()).indexOf(ccShib) != -1) { - // return first value that matches the shib - return dcvalue.getValue(); - } - } - return null; - } - - /** - * Returns the value that matches the value mapped to the passed key if any. - * NB: this only delivers a license name (if present in field) given a license URI - * - * @param item - the item to read - * @param key - the key for desired value - * @return value - the value associated with key or null if no such value - * @throws IOException A general class of exceptions produced by failed or interrupted I/O operations. - * @throws SQLException An exception that provides information on a database access error or other errors. - * @throws AuthorizeException Exception indicating the current user of the context does not have permission - * to perform a particular action. - */ - public String keyedItemValue(Item item, String key) - throws AuthorizeException, IOException, SQLException { - CCLookup ccLookup = new CCLookup(); - ccLookup.issue(key); - String matchValue = ccLookup.getLicenseName(); - List dcvalues = itemService.getMetadata(item, params[0], params[1], params[2], params[3]); - for (MetadataValue dcvalue : dcvalues) { - if (dcvalue.getValue().equals(matchValue)) { - return dcvalue.getValue(); - } - } - return null; - } - - /** - * Removes the passed value from the set of values for the field in passed item. - * - * @param context The relevant DSpace Context. - * @param item - the item to update - * @param value - the value to remove - * @throws IOException A general class of exceptions produced by failed or interrupted I/O operations. - * @throws SQLException An exception that provides information on a database access error or other errors. - * @throws AuthorizeException Exception indicating the current user of the context does not have permission - * to perform a particular action. - */ - public void removeItemValue(Context context, Item item, String value) - throws AuthorizeException, IOException, SQLException { - if (value != null) { - List dcvalues = itemService.getMetadata(item, params[0], params[1], params[2], params[3]); - ArrayList arrayList = new ArrayList(); - for (MetadataValue dcvalue : dcvalues) { - if (!dcvalue.getValue().equals(value)) { - arrayList.add(dcvalue.getValue()); - } - } - itemService.clearMetadata(context, item, params[0], params[1], params[2], params[3]); - itemService.addMetadata(context, item, params[0], params[1], params[2], params[3], arrayList); - } - } - - /** - * Adds passed value to the set of values for the field in passed item. - * - * @param context The relevant DSpace Context. - * @param item - the item to update - * @param value - the value to add in this field - * @throws SQLException An exception that provides information on a database access error or other errors. - */ - public void addItemValue(Context context, Item item, String value) throws SQLException { - itemService.addMetadata(context, item, params[0], params[1], params[2], params[3], value); - } - -} diff --git a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java index c5ea6475bbf2..fa32cb75ca76 100644 --- a/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java +++ b/dspace-api/src/main/java/org/dspace/license/service/CreativeCommonsService.java @@ -31,13 +31,6 @@ public interface CreativeCommonsService { public static final String CC_BUNDLE_NAME = "CC-LICENSE"; - /** - * Simple accessor for enabling of CC - * - * @return is CC enabled? - */ - public boolean isEnabled(); - /** * setLicenseRDF * @@ -88,8 +81,6 @@ public void setLicense(Context context, Item item, public void removeLicenseFile(Context context, Item item) throws SQLException, IOException, AuthorizeException; - public boolean hasLicense(Context context, Item item) - throws SQLException, IOException; public String getLicenseURL(Context context, Item item) throws SQLException, IOException, AuthorizeException; @@ -111,10 +102,6 @@ public String getLicenseURL(Context context, Item item) */ public String getLicenseName(Item item); - - public String getLicenseRDF(Context context, Item item) - throws SQLException, IOException, AuthorizeException; - /** * Get Creative Commons license RDF, returning Bitstream object. * diff --git a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index 41cd9805f6fe..bb443ab4a41d 100644 --- a/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -107,14 +107,18 @@ public String retrieveRightsByQuestion(final String licenseId, */ public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { if (!StringUtils.contains(licenseURI, "invalid")) { + InputStream cclicense = null; try { - - InputStream cclicense = getClass().getResourceAsStream("cc-license-rdf.xml"); + cclicense = getClass().getResourceAsStream("cc-license-rdf.xml"); Document doc = parser.build(cclicense); return doc; } catch (JDOMException e) { - e.printStackTrace(); + throw new RuntimeException(e); + } finally { + if (cclicense != null) { + cclicense.close(); + } } } return null; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java index 251717d79991..ae925fe8b104 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseRestEvaluatorPlugin.java @@ -1,12 +1,19 @@ +/** + * 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 java.io.Serializable; + import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.SubmissionCCLicenseRest; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; -import java.io.Serializable; - @Component public class SubmissionCCLicenseRestEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java index 76b260f194de..4f9c75304714 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseAddPatchOperationIT.java @@ -10,6 +10,8 @@ import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -55,6 +57,8 @@ public void patchSubmissionCCLicense() throws Exception { .withTitle("Workspace Item") .build(); + context.restoreAuthSystemState(); + String adminToken = getAuthToken(admin.getEmail(), password); List ops = new ArrayList(); @@ -93,6 +97,9 @@ public void patchSubmissionCCLicenseInvalid() throws Exception { .withTitle("Workspace Item") .build(); + context.restoreAuthSystemState(); + + String adminToken = getAuthToken(admin.getEmail(), password); List ops = new ArrayList(); @@ -106,5 +113,13 @@ public void patchSubmissionCCLicenseInvalid() throws Exception { .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isInternalServerError()); + + getClient(adminToken).perform(get("/api/submission/workspaceitems/" + workspaceItem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense")))); + + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java index b908b7acd7a2..afee0aa88250 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java @@ -57,6 +57,8 @@ public void patchRemoveSubmissionCCLicense() throws Exception { .withTitle("Workspace Item") .build(); + context.restoreAuthSystemState(); + String adminToken = getAuthToken(admin.getEmail(), password); // First add a license and verify it is added @@ -80,7 +82,6 @@ public void patchRemoveSubmissionCCLicense() throws Exception { ))); - // Remove the license again and verify it is removed List removeOps = new ArrayList(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java index 41cd9805f6fe..bb443ab4a41d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java +++ b/dspace-server-webapp/src/test/java/org/dspace/license/MockCCLicenseConnectorServiceImpl.java @@ -107,14 +107,18 @@ public String retrieveRightsByQuestion(final String licenseId, */ public Document retrieveLicenseRDFDoc(String licenseURI) throws IOException { if (!StringUtils.contains(licenseURI, "invalid")) { + InputStream cclicense = null; try { - - InputStream cclicense = getClass().getResourceAsStream("cc-license-rdf.xml"); + cclicense = getClass().getResourceAsStream("cc-license-rdf.xml"); Document doc = parser.build(cclicense); return doc; } catch (JDOMException e) { - e.printStackTrace(); + throw new RuntimeException(e); + } finally { + if (cclicense != null) { + cclicense.close(); + } } } return null; From 8e0b22ad6ca165a16fc608f8d424c66d2b69100d Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 29 Apr 2020 11:17:17 +0200 Subject: [PATCH 22/73] 70415: jurisdiction --- .../dspace/license/CreativeCommonsServiceImpl.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java index 7a48b6f03b64..40e727d9df3d 100644 --- a/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CreativeCommonsServiceImpl.java @@ -91,6 +91,8 @@ public class CreativeCommonsServiceImpl implements CreativeCommonsService, Initi protected ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); private String defaultLanguage; + private String jurisdiction; + private static final String JURISDICTION_KEY = "jurisdiction"; private Map> ccLicenses; @@ -113,6 +115,7 @@ public void afterPropertiesSet() throws Exception { ccLicenses = new HashMap<>(); defaultLanguage = configurationService.getProperty("cc.license.locale", "en"); + jurisdiction = configurationService.getProperty("cc.license.jurisdiction", ""); try { templates = TransformerFactory.newInstance().newTemplates( @@ -601,9 +604,18 @@ public Map retrieveFullAnswerMap(String licenseId, String langua fullParamMap.put(ccLicenseField.getId(), ""); } } + + updateJurisdiction(fullParamMap); + return fullParamMap; } + private void updateJurisdiction(final Map fullParamMap) { + if (fullParamMap.containsKey(JURISDICTION_KEY)) { + fullParamMap.put(JURISDICTION_KEY, jurisdiction); + } + } + private boolean containsAnswerEnum(final String enumAnswer, final CCLicenseField ccLicenseField) { List fieldEnums = ccLicenseField.getFieldEnum(); for (CCLicenseFieldEnum fieldEnum : fieldEnums) { From b59aeecf0e09f5d215fd8b583d27659bbe66a80a Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Wed, 29 Apr 2020 14:03:03 +0200 Subject: [PATCH 23/73] Added extra cleanup to the EPersonRestRepositoryIT and optimized test in RegistrationRestControllerIT --- .../app/rest/EPersonRestRepositoryIT.java | 76 +++++++++++++++++++ .../rest/RegistrationRestControllerIT.java | 7 +- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 53a054e2f0da..43a790b8af27 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -14,6 +14,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -55,6 +56,8 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.PasswordHash; +import org.dspace.eperson.RegistrationData; +import org.dspace.eperson.dao.RegistrationDataDAO; import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.RegistrationDataService; @@ -75,6 +78,9 @@ public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { @Autowired private EPersonService ePersonService; + @Autowired + private RegistrationDataDAO registrationDataDAO; + @Test public void createTest() throws Exception { context.turnOffAuthorisationSystem(); @@ -1672,6 +1678,10 @@ public void patchReplacePasswordWithToken() throws Exception { PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); assertFalse(oldPassword.equals(newPasswordHash)); assertTrue(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, tokenForEPerson); + context.restoreAuthSystemState(); } @@ -1709,6 +1719,10 @@ public void patchReplacePasswordWithRandomTokenPatchFail() throws Exception { assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); assertTrue(StringUtils.equals(registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(), tokenForEPerson)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, tokenForEPerson); + context.restoreAuthSystemState(); } @Test @@ -1753,6 +1767,11 @@ public void patchReplacePasswordWithOtherUserTokenFail() throws Exception { PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, tokenForEPerson); + registrationDataService.deleteByToken(context, tokenForEPersonTwo); + context.restoreAuthSystemState(); } @Test @@ -1789,6 +1808,12 @@ public void patchReplaceEmailWithTokenFail() throws Exception { assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); assertTrue(StringUtils.equalsIgnoreCase(ePerson.getEmail(), originalEmail)); + + context.turnOffAuthorisationSystem(); + registrationDataService.delete(context, registrationDataService.findByEmail(context, ePerson.getEmail())); + registrationDataService.deleteByToken(context, tokenForEPerson); + context.restoreAuthSystemState(); + } @Test @@ -1833,6 +1858,11 @@ public void registerNewAccountPatchUpdatePasswordRandomUserUuidFail() throws Exc assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); assertFalse(registrationDataService.findByEmail(context, newRegisterEmail) == null); + + context.turnOffAuthorisationSystem(); + registrationDataService.delete(context, registrationDataService.findByEmail(context, ePerson.getEmail())); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); } @Test @@ -1880,6 +1910,10 @@ public void postEPersonWithTokenWithoutEmailProperty() throws Exception { context.turnOffAuthorisationSystem(); ePersonService.delete(context, createdEPerson); context.restoreAuthSystemState(); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); } @Test @@ -1928,6 +1962,11 @@ public void postEPersonWithTokenWithEmailProperty() throws Exception { context.turnOffAuthorisationSystem(); ePersonService.delete(context, createdEPerson); context.restoreAuthSystemState(); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } @Test @@ -1976,6 +2015,11 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce context.turnOffAuthorisationSystem(); ePersonService.delete(context, createdEPerson); context.restoreAuthSystemState(); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } @Test @@ -2017,6 +2061,11 @@ public void postEPersonWithTokenWithTwoTokensDifferentEmailProperty() throws Exc assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); assertNotNull(registrationDataService.findByToken(context, newRegisterTokenTwo)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + registrationDataService.deleteByToken(context, newRegisterTokenTwo); + context.restoreAuthSystemState(); } @Test @@ -2047,6 +2096,10 @@ public void postEPersonWithRandomTokenWithEmailProperty() throws Exception { EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); } @Test @@ -2077,6 +2130,10 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredFalseProperty() throws EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); } @Test @@ -2106,6 +2163,10 @@ public void postEPersonWithTokenWithoutLastNameProperty() throws Exception { EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); } @Test @@ -2135,6 +2196,10 @@ public void postEPersonWithTokenWithoutFirstNameProperty() throws Exception { EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); } @Test @@ -2165,6 +2230,11 @@ public void postEPersonWithTokenWithoutPasswordProperty() throws Exception { EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } @Test @@ -2195,6 +2265,11 @@ public void postEPersonWithWrongToken() throws Exception { EPerson createdEPerson = ePersonService.findByEmail(context, newEmail); assertNull(createdEPerson); assertNotNull(registrationDataService.findByToken(context, forgotPasswordToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, forgotPasswordToken); + context.restoreAuthSystemState(); + } @Test @@ -2241,6 +2316,7 @@ public void postEPersonWithTokenWithEmailPropertyAnonUser() throws Exception { context.turnOffAuthorisationSystem(); ePersonService.delete(context, createdEPerson); + registrationDataService.deleteByToken(context, newRegisterToken); context.restoreAuthSystemState(); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java index 0201eb284fb9..d3147d461178 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -35,7 +36,7 @@ public class RegistrationRestControllerIT extends AbstractControllerIntegrationT @Test public void registrationFlowTest() throws Exception { List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationDataList.isEmpty()); + assertEquals(0, registrationDataList.size()); ObjectMapper mapper = new ObjectMapper(); RegistrationRest registrationRest = new RegistrationRest(); @@ -45,7 +46,7 @@ public void registrationFlowTest() throws Exception { .contentType(contentType)) .andExpect(status().isCreated()); registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationDataList.size() == 1); + assertEquals(1, registrationDataList.size()); assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); String newEmail = "newEPersonTest@gmail.com"; @@ -67,7 +68,7 @@ public void registrationFlowTest() throws Exception { .contentType(contentType)) .andExpect(status().is(401)); - assertTrue(registrationDataList.size() == 2); + assertEquals(2, registrationDataList.size()); assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && !StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); From 91d70d85cec585cae299aac972d2f1c7181957bf Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 30 Apr 2020 17:40:29 +0200 Subject: [PATCH 24/73] added ITs to prove that patch request with wrong path or metadata must return 422 --- .../rest/WorkspaceItemRestRepositoryIT.java | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 35b4956846a6..b2f4de1d0e6b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -3160,4 +3160,341 @@ public void findByItemUuidUnAuthenticatedTest() throws Exception { .andExpect(status().isUnauthorized()); } + @Test + public void patchAddMetadataOnSectionNotExistentTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withIssueDate("2019-04-25") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addTitle = new ArrayList(); + List> values = new ArrayList>(); + Map value = new HashMap(); + value.put("value", "New Title"); + values.add(value); + addTitle.add(new AddOperation("/sections/not-existing-section/dc.title", values)); + + String patchBody = getPatchContent(addTitle); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchAddMetadataWrongAttributeTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withIssueDate("2019-04-25") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addTitle = new ArrayList(); + List> values = new ArrayList>(); + Map value = new HashMap(); + value.put("value", "New Title"); + values.add(value); + addTitle.add(new AddOperation("/sections/traditionalpageone/dc.not.existing", values)); + + String patchBody = getPatchContent(addTitle); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + // try to add Title on tradiotionalpagetwo, but attribute title is placed on tradiotionalpageone + public void patchAddTitleOnSectionThatNotContainAttributeTitleTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withIssueDate("2019-11-21") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addTitle = new ArrayList(); + List> values = new ArrayList>(); + Map value = new HashMap(); + value.put("value", "New Title"); + values.add(value); + addTitle.add(new AddOperation("/sections/traditionalpagetwo/dc.title", values)); + + String patchBody = getPatchContent(addTitle); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchAcceptLicenseWrontPathTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Example Title") + .withIssueDate("2019-11-21") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List replaceGrant = new ArrayList(); + replaceGrant.add(new ReplaceOperation("/sections/license/not-existing", true)); + + String patchBody = getPatchContent(replaceGrant); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchAcceptLicenseTryToChangeLicenseUrlTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Example Title") + .withIssueDate("2019-11-21") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List replaceGrant = new ArrayList(); + replaceGrant.add(new ReplaceOperation("/sections/license/granted", true)); + + String patchBody = getPatchContent(replaceGrant); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()); + + List replaceLicenseUrl = new ArrayList(); + replaceLicenseUrl.add(new ReplaceOperation("/sections/license/url", "not.existing")); + + patchBody = getPatchContent(replaceLicenseUrl); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchAcceptLicenseTryToChangeDateAccepteLicenseTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Example Title") + .withIssueDate("2019-11-21") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List replaceGrant = new ArrayList(); + replaceGrant.add(new ReplaceOperation("/sections/license/granted", true)); + + String patchBody = getPatchContent(replaceGrant); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()); + + List replaceLicenseUrl = new ArrayList(); + replaceLicenseUrl.add(new ReplaceOperation("/sections/license/acceptanceDate", "2020-02-14")); + + patchBody = getPatchContent(replaceLicenseUrl); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchUploadMetadatoNotExistTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2019-10-25") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addOpts = new ArrayList(); + Map value = new HashMap(); + value.put("value", "some text"); + addOpts.add(new AddOperation("/sections/upload/files/0/metadata/dc.not.existing", value)); + + String patchBody = getPatchContent(addOpts); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchUploadWrongPathTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2017-10-17") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addOpts = new ArrayList(); + Map value = new HashMap(); + value.put("value", "2020-01-25"); + addOpts.add(new AddOperation("/sections/upload/files/0/metadata/dc.date.issued", value)); + + String patchBody = getPatchContent(addOpts); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } } From 676648568dd07360b29489210fc2df659833db52 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 30 Apr 2020 18:58:52 +0200 Subject: [PATCH 25/73] fix return http status for wrong patch request to the submission --- .../WorkspaceItemRestRepository.java | 13 ++++++- .../submit/AbstractRestProcessingStep.java | 4 +- .../app/rest/submit/step/CollectionStep.java | 4 +- .../app/rest/submit/step/DescribeStep.java | 17 ++++++--- .../app/rest/submit/step/LicenseStep.java | 7 +++- .../app/rest/submit/step/UploadStep.java | 37 +++++++++++++++++-- 6 files changed, 67 insertions(+), 15 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java index 91d6f0c652d9..0b5217405058 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java @@ -27,6 +27,7 @@ import org.dspace.app.rest.converter.WorkspaceItemConverter; 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.ErrorRest; import org.dspace.app.rest.model.WorkspaceItemRest; import org.dspace.app.rest.model.patch.Operation; @@ -294,6 +295,7 @@ public void patch(Context context, HttpServletRequest request, String apiCategor private void evaluatePatch(Context context, HttpServletRequest request, WorkspaceItem source, WorkspaceItemRest wsi, String section, Operation op) { + boolean sectionExist = false; SubmissionConfig submissionConfig = submissionConfigReader .getSubmissionConfigByName(wsi.getSubmissionDefinition().getName()); for (int stepNum = 0; stepNum < submissionConfig.getNumberOfSteps(); stepNum++) { @@ -301,6 +303,7 @@ private void evaluatePatch(Context context, HttpServletRequest request, Workspac SubmissionStepConfig stepConfig = submissionConfig.getStep(stepNum); if (section.equals(stepConfig.getId())) { + sectionExist = true; /* * First, load the step processing class (using the current * class loader) @@ -317,7 +320,8 @@ private void evaluatePatch(Context context, HttpServletRequest request, Workspac AbstractRestProcessingStep stepProcessing = (AbstractRestProcessingStep) stepClass.newInstance(); stepProcessing.doPreProcessing(context, source); - stepProcessing.doPatchProcessing(context, getRequestService().getCurrentRequest(), source, op); + stepProcessing.doPatchProcessing(context, + getRequestService().getCurrentRequest(), source, op, stepConfig); stepProcessing.doPostProcessing(context, source); } else { throw new DSpaceBadRequestException( @@ -326,12 +330,19 @@ private void evaluatePatch(Context context, HttpServletRequest request, Workspac " Therefore it cannot be used by the Configurable Submission as the !"); } + } catch (UnprocessableEntityException e) { + log.error(e.getMessage(), e); + throw new UnprocessableEntityException("Error processing the patch request", e); } catch (Exception e) { log.error(e.getMessage(), e); throw new PatchException("Error processing the patch request", e); } } } + if (!sectionExist) { + throw new UnprocessableEntityException("The section with name " + section + + " does not exist in this submission!"); + } } @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'DELETE')") diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java index 58bfaba27b65..49f4d818a341 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/AbstractRestProcessingStep.java @@ -94,7 +94,7 @@ default public List validate(SubmissionService submissionService, InP * the json patch operation * @throws Exception */ - public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) - throws Exception; + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, + SubmissionStepConfig stepConf) throws Exception; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CollectionStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CollectionStep.java index 450c214d7911..0835f4cde360 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CollectionStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CollectionStep.java @@ -36,8 +36,8 @@ public UUID getData(SubmissionService submissionService, InProgressSubmission ob } @Override - public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) - throws Exception { + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, + SubmissionStepConfig stepConf) throws Exception { PatchOperation patchOperation = new PatchOperationFactory() .instanceOf(COLLECTION_STEP_OPERATION_ENTRY, op.getOp()); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java index 2fa8e264c787..b0134973a9d8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java @@ -11,6 +11,7 @@ import java.util.List; import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.step.DataDescribe; @@ -110,13 +111,19 @@ private void readField(InProgressSubmission obj, SubmissionStepConfig config, Da } @Override - public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) - throws Exception { + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, + SubmissionStepConfig stepConf) throws Exception { PatchOperation patchOperation = new PatchOperationFactory() - .instanceOf(DESCRIBE_STEP_METADATA_OPERATION_ENTRY, op.getOp()); - patchOperation.perform(context, currentRequest, source, op); - + .instanceOf(DESCRIBE_STEP_METADATA_OPERATION_ENTRY, op.getOp()); + DCInputSet inputConfig = inputReader.getInputsByFormName(stepConf.getId()); + String[] split = patchOperation.getAbsolutePath(op.getPath()).split("/"); + if (inputConfig.isFieldPresent(split[0])) { + patchOperation.perform(context, currentRequest, source, op); + } else { + throw new UnprocessableEntityException("The attribute " + split[0] + " does not present in section " + + inputConfig.getFormName()); + } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java index 7ed0ffd746c0..a57951d09aba 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.submit.step; import org.atteo.evo.inflector.English; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.step.DataLicense; @@ -50,8 +51,8 @@ public DataLicense getData(SubmissionService submissionService, InProgressSubmis } @Override - public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) - throws Exception { + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, + SubmissionStepConfig stepConf) throws Exception { if (op.getPath().endsWith(LICENSE_STEP_OPERATION_ENTRY)) { @@ -59,6 +60,8 @@ public void doPatchProcessing(Context context, Request currentRequest, InProgres .instanceOf(LICENSE_STEP_OPERATION_ENTRY, op.getOp()); patchOperation.perform(context, currentRequest, source, op); + } else { + throw new UnprocessableEntityException("This path : " + op.getPath() + " can not to be replaced"); } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java index ca46e5608b09..f32d686efe8f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java @@ -12,6 +12,7 @@ import java.util.List; import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.ErrorRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.step.DataUpload; @@ -23,6 +24,9 @@ import org.dspace.app.rest.submit.factory.PatchOperationFactory; import org.dspace.app.rest.submit.factory.impl.PatchOperation; import org.dspace.app.rest.utils.Utils; +import org.dspace.app.util.DCInputSet; +import org.dspace.app.util.DCInputsReader; +import org.dspace.app.util.DCInputsReaderException; import org.dspace.app.util.SubmissionStepConfig; import org.dspace.content.Bitstream; import org.dspace.content.BitstreamFormat; @@ -45,6 +49,13 @@ public class UploadStep extends org.dspace.submit.step.UploadStep private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(UploadStep.class); + public static final String UPLOAD_STEP_METADATA_SECTION = "bitstream-metadata"; + + private DCInputsReader inputReader; + + public UploadStep() throws DCInputsReaderException { + inputReader = new DCInputsReader(); + } @Override public DataUpload getData(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws Exception { @@ -61,8 +72,8 @@ public DataUpload getData(SubmissionService submissionService, InProgressSubmiss } @Override - public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) - throws Exception { + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, + SubmissionStepConfig stepConf) throws Exception { String instance = ""; if ("remove".equals(op.getOp())) { @@ -87,8 +98,28 @@ public void doPatchProcessing(Context context, Request currentRequest, InProgres } } PatchOperation patchOperation = new PatchOperationFactory().instanceOf(instance, op.getOp()); - patchOperation.perform(context, currentRequest, source, op); + if (instance.equals(AbstractRestProcessingStep.UPLOAD_STEP_METADATA_OPERATION_ENTRY)) { + DCInputSet inputConfig = inputReader.getInputsByFormName(UploadStep.UPLOAD_STEP_METADATA_SECTION); + String[] split = patchOperation.getAbsolutePath(op.getPath()).split("/"); + String metadata = findMetadata(split); + if (inputConfig.isFieldPresent(metadata)) { + patchOperation.perform(context, currentRequest, source, op); + } else { + throw new UnprocessableEntityException("The attribute " + metadata + " does not present in section " + + UploadStep.UPLOAD_STEP_METADATA_SECTION); + } + } else { + patchOperation.perform(context, currentRequest, source, op); + } + } + private String findMetadata(String[] metadata) { + for (String s : metadata) { + if (s.contains("dc.")) { + return s; + } + } + return null; } @Override From 6ef11e491962dc5dbf2281752b13ca67ef9a923d Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 4 May 2020 09:18:22 +0200 Subject: [PATCH 26/73] added the missing parameter --- .../dspace/app/rest/repository/WorkflowItemRestRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java index d2fdb092802c..dca3bd354b17 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkflowItemRestRepository.java @@ -292,7 +292,8 @@ private void evaluatePatch(Context context, HttpServletRequest request, XmlWorkf AbstractRestProcessingStep stepProcessing = (AbstractRestProcessingStep) stepClass.newInstance(); stepProcessing.doPreProcessing(context, source); - stepProcessing.doPatchProcessing(context, getRequestService().getCurrentRequest(), source, op); + stepProcessing.doPatchProcessing(context, getRequestService().getCurrentRequest(), + source, op, stepConfig); stepProcessing.doPostProcessing(context, source); } else { throw new DSpaceBadRequestException( From fc91b0b858a47519cb5e9aaab6738a259c1dab05 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 5 May 2020 11:59:05 +0200 Subject: [PATCH 27/73] Fixed checkstyle in the EPersonRestRepositoryIT class --- .../test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 43a790b8af27..8f48ac4a90da 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -14,7 +14,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -56,7 +55,6 @@ import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.eperson.PasswordHash; -import org.dspace.eperson.RegistrationData; import org.dspace.eperson.dao.RegistrationDataDAO; import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; From b5bbc72eceb1fcf3b323007dfb57affe32109524 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 8 May 2020 12:57:07 +0200 Subject: [PATCH 28/73] 70815: Angular feedback --- .../SubmissionCCLicenseSearchController.java | 12 +++---- .../app/rest/model/PlainTextValueRest.java | 36 +++++++++++++++++++ ...SubmissionCCLicenseSearchControllerIT.java | 9 +++-- 3 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java index 185fb7e8ebaf..26e52c893305 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java @@ -12,9 +12,8 @@ import javax.servlet.ServletRequest; import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.model.PlainTextValueRest; import org.dspace.app.rest.model.SubmissionCCLicenseRest; import org.dspace.app.rest.utils.Utils; import org.dspace.license.service.CreativeCommonsService; @@ -24,7 +23,6 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; /** @@ -35,8 +33,6 @@ "/rightsByQuestions") public class SubmissionCCLicenseSearchController { - private static final Logger log = LogManager.getLogger(); - @Autowired protected Utils utils; @@ -52,8 +48,7 @@ public class SubmissionCCLicenseSearchController { * @return the CC License URI as a string */ @RequestMapping(method = RequestMethod.GET) - @ResponseBody - public String findByRightsByQuestions() { + public PlainTextValueRest findByRightsByQuestions() { ServletRequest servletRequest = requestService.getCurrentRequest() .getServletRequest(); Map requestParameterMap = servletRequest @@ -90,6 +85,7 @@ public String findByRightsByQuestions() { if (StringUtils.isBlank(licenseUri)) { throw new ResourceNotFoundException("No CC License URI could be found for ID: " + licenseId); } - return licenseUri; + PlainTextValueRest plainTextValueRest = new PlainTextValueRest(licenseUri); + return plainTextValueRest; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java new file mode 100644 index 000000000000..6f02aa928680 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java @@ -0,0 +1,36 @@ +/** + * 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; + +/** + * Rest object used to represent a plain text value + */ +public class PlainTextValueRest { + public static final String TYPE = "plaintextvalue"; + + private String value; + + public PlainTextValueRest() { + } + + public PlainTextValueRest(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(final String value) { + this.value = value; + } + + public String getType() { + return TYPE; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java index 402f4c3a698b..5c35bb5acf27 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java @@ -8,8 +8,9 @@ package org.dspace.app.rest; +import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; @@ -32,14 +33,16 @@ public void searchRightsByQuestionsTest() throws Exception { "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + "=license2-field0-enum1")) .andExpect(status().isOk()) - .andExpect(content().string("mock-license-uri")); + .andExpect(jsonPath("$.value", is("mock-license-uri"))) + .andExpect(jsonPath("$.type", is("plaintextvalue"))); } @Test public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception { getClient().perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3")) .andExpect(status().isOk()) - .andExpect(content().string("mock-license-uri")); + .andExpect(jsonPath("$.value", is("mock-license-uri"))) + .andExpect(jsonPath("$.type", is("plaintextvalue"))); } @Test From 3dc9f511d4a8797079a8d08fa84b93a949401127 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Fri, 8 May 2020 15:43:18 +0200 Subject: [PATCH 29/73] [Task 70808] changed the link the mail to include the token in the url instead of as a parameter --- .../src/main/java/org/dspace/eperson/AccountServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java index 9282fc116e82..2121cd669dae 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java @@ -239,8 +239,8 @@ protected void sendEmail(Context context, String email, boolean isRegister, Regi // Note change from "key=" to "token=" String specialLink = new StringBuffer().append(base).append( base.endsWith("/") ? "" : "/").append( - isRegister ? "register" : "forgot").append("?") - .append("token=").append(rd.getToken()) + isRegister ? "register" : "forgot").append("/") + .append(rd.getToken()) .toString(); Locale locale = context.getCurrentLocale(); Email bean = Email.getEmail(I18nUtil.getEmailFilename(locale, isRegister ? "register" From b18b52a621f9b75d92417c4a33730e56343a19b4 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Mon, 11 May 2020 08:59:47 +0200 Subject: [PATCH 30/73] [Task 70808] added javadocs --- .../app/rest/RegistrationRestController.java | 19 +++++++++++++++++++ .../impl/EPersonRegistrationFeature.java | 4 ++++ .../app/rest/model/RegistrationRest.java | 5 +++++ .../model/hateoas/RegistrationResource.java | 4 ++++ .../repository/EPersonRestRepository.java | 4 ++++ .../RegistrationRestRepository.java | 10 ++++++++++ 6 files changed, 46 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java index b03eda36f076..f24b723e360e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java @@ -40,6 +40,9 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +/** + * This will be the Controller class that handles calls to the /api/eperson/registrations endpoints + */ @RestController @RequestMapping("/api/" + RegistrationRest.CATEGORY + "/" + RegistrationRest.NAME_PLURAL) public class RegistrationRestController { @@ -59,6 +62,22 @@ public class RegistrationRestController { @Autowired private EPersonService ePersonService; + /** + * This method will be used to either register a new user or to send forgotten password info in a mail. + * It can be called by doing a POST request to the /api/eperson/registrations endpoint. + * It'll create a RegistrationRest object from the inputstream in the request and it'll check whether the email + * defined in that object is in the DB or not. + * If it is in the db then we'll send the forgotten password info, if it wasn't in the database then we'll send + * registration info. + * + * @param request The current request + * @param response The current response + * @return An empty response containing a 201 status code + * @throws SQLException If something goes wrong + * @throws IOException If something goes wrong + * @throws MessagingException If something goes wrong + * @throws AuthorizeException If something goes wrong + */ @RequestMapping(method = RequestMethod.POST) public ResponseEntity register(HttpServletRequest request, HttpServletResponse response) throws SQLException, IOException, MessagingException, AuthorizeException { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java index 44de9ce6d740..d230d2ef972a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java @@ -20,6 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +/** + * The EPerson Registration feature. It's able to be used on site objects if the user.registration property is set to + * true. If it's set to true, it'll check if the current context is allowed to set the password. + */ @Component @AuthorizationFeatureDocumentation(name = EPersonRegistrationFeature.NAME, description = "It can be used to register an eperson") diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java index b84699f140d8..69ed1d5653b4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java @@ -13,6 +13,11 @@ import org.dspace.app.rest.RegistrationRestController; +/** + * This class acts as the REST representation of the RegistrationData model class. + * This class acts as a data holder for the RegistrationResource + * Refer to {@link org.dspace.eperson.RegistrationData} for explanation about the properties + */ public class RegistrationRest extends RestAddressableModel { public static final String NAME = "registration"; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java index 0d92b0d41e1e..da53b680d629 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/RegistrationResource.java @@ -10,6 +10,10 @@ import org.dspace.app.rest.model.RegistrationRest; import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +/** + * Registration HAL Resource. This resource adds the data from the REST object together with embedded objects + * and a set of links if applicable + */ @RelNameDSpaceResource(RegistrationRest.NAME) public class RegistrationResource extends HALResource { public RegistrationResource(RegistrationRest content) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index fa69d152539c..43e46a85c9f0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -93,6 +93,7 @@ protected EPersonRest createAndReturn(Context context) throw new UnprocessableEntityException("error parsing the body... maybe this is not the right error code"); } String token = req.getParameter("token"); + // If a token is available, we'll swap to the execution that is token based if (StringUtils.isNotBlank(token)) { try { return createAndReturn(context, epersonRest, token); @@ -101,6 +102,7 @@ protected EPersonRest createAndReturn(Context context) throw new RuntimeException("Something with wrong in the creation of an EPerson with token: " + token); } } + // If no token is present, we simply do the admin execution EPerson eperson = createEPersonFromRestObject(context, epersonRest); return converter.toRest(eperson, utils.obtainProjection()); @@ -159,9 +161,11 @@ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, St throw new AccessDeniedException( "Registration is disabled, you are not authorized to create a new Authorization"); } + // We'll turn off authorisation system because this call isn't admin based as it's token based context.turnOffAuthorisationSystem(); EPerson ePerson = createEPersonFromRestObject(context, epersonRest); context.restoreAuthSystemState(); + // Restoring authorisation state right after the creation call accountService.deleteToken(context, token); return converter.toRest(ePerson, utils.obtainProjection()); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java index f9ff62316314..2578b8eebfaf 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java @@ -25,6 +25,9 @@ import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.stereotype.Component; +/** + * This is the repository that is responsible for managing Registration Rest objects + */ @Component(RegistrationRest.CATEGORY + "." + RegistrationRest.NAME) public class RegistrationRestRepository extends DSpaceRestRepository { @@ -49,6 +52,13 @@ public Class getDomainClass() { return RegistrationRest.class; } + /** + * This method will find the RegistrationRest object that is associated with the token given + * @param token The token to be found and for which a RegistrationRest object will be found + * @return A RegistrationRest object for the given token + * @throws SQLException If something goes wrong + * @throws AuthorizeException If something goes wrong + */ @SearchRestMethod(name = "findByToken") public RegistrationRest findByToken(@Parameter(value = "token", required = true) String token) throws SQLException, AuthorizeException { From ea47ef0c924f973aa4ec3090fac163e9881b142e Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Mon, 11 May 2020 11:49:57 +0200 Subject: [PATCH 31/73] [Task 70808] applied feedback to the registration implementation --- .../org/dspace/app/util/AuthorizeUtil.java | 44 +++++++++++++++++++ .../dspace/eperson/AccountServiceImpl.java | 12 +++++ .../eperson/service/AccountService.java | 2 + .../app/rest/RegistrationRestController.java | 25 ++++++----- .../impl/EPersonRegistrationFeature.java | 17 +++---- .../repository/EPersonRestRepository.java | 32 ++++++-------- .../EPersonPasswordReplaceOperation.java | 5 +++ .../app/rest/EPersonRestRepositoryIT.java | 44 +++++++------------ .../rest/RegistrationRestControllerIT.java | 26 +++++++++++ .../EPersonRegistrationFeatureIT.java | 18 ++------ 10 files changed, 142 insertions(+), 83 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index 6c4271e1f28e..d5dbf5a514b4 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -9,7 +9,10 @@ import java.sql.SQLException; import java.util.List; +import javax.servlet.http.HttpServletRequest; +import org.apache.logging.log4j.Logger; +import org.dspace.authenticate.factory.AuthenticateServiceFactory; import org.dspace.authorize.AuthorizeConfiguration; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; @@ -25,6 +28,8 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; +import org.dspace.eperson.factory.EPersonServiceFactory; +import org.dspace.services.factory.DSpaceServicesFactory; /** * This class is an addition to the AuthorizeManager that perform authorization @@ -34,6 +39,7 @@ */ public class AuthorizeUtil { + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(AuthorizeUtil.class); /** * Default constructor */ @@ -525,4 +531,42 @@ public static void authorizeReinstateItem(Context context, Item item) } } } + + /** + * This method will return a boolean indicating whether the current user is allowed to register a new + * account or not + * @param context The relevant DSpace context + * @param request The current request + * @return A boolean indicating whether the current user can register a new account or not + * @throws SQLException If something goes wrong + */ + public static boolean authorizeNewAccountRegistration(Context context, HttpServletRequest request) + throws SQLException { + if (DSpaceServicesFactory.getInstance().getConfigurationService() + .getBooleanProperty("user.registration", true)) { + return AuthenticateServiceFactory.getInstance().getAuthenticationService() + .allowSetPassword(context, request, null); + } + return false; + } + + /** + * This method will return a boolean indicating whether it's allowed to update the password for the EPerson + * with the given email and canLogin property + * @param context The relevant DSpace context + * @param email The email to be checked + * @param canLogin The boolean canLogin property + * @return A boolean indicating if the password can be updated or not + */ + public static boolean authorizeUpdatePassword(Context context, String email, boolean canLogin) { + try { + if (EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, email) != null + && canLogin) { + return true; + } + } catch (SQLException e) { + log.error("Something went wrong trying to retrieve EPerson for email: " + email, e); + } + return false; + } } diff --git a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java index 2121cd669dae..1aeaeaef8e34 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java @@ -12,6 +12,7 @@ import java.util.Locale; import javax.mail.MessagingException; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.core.ConfigurationManager; @@ -161,6 +162,17 @@ public void deleteToken(Context context, String token) registrationDataService.deleteByToken(context, token); } + @Override + public boolean verifyPasswordStructure(String password) { + if (StringUtils.isBlank(password)) { + return false; + } + if (StringUtils.length(password) < 6) { + return false; + } + return true; + } + /** * THIS IS AN INTERNAL METHOD. THE SEND PARAMETER ALLOWS IT TO BE USED FOR * TESTING PURPOSES. diff --git a/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java b/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java index c8ecb0cc67d4..23d46ea00a1d 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java +++ b/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java @@ -46,4 +46,6 @@ public String getEmail(Context context, String token) public void deleteToken(Context context, String token) throws SQLException; + + public boolean verifyPasswordStructure(String password); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java index f24b723e360e..741efa995b02 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java @@ -16,18 +16,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; -import org.dspace.app.rest.authorization.AuthorizationFeature; import org.dspace.app.rest.authorization.AuthorizationFeatureService; import org.dspace.app.rest.converter.ConverterService; +import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.RegistrationRest; -import org.dspace.app.rest.model.SiteRest; -import org.dspace.app.rest.projection.Projection; import org.dspace.app.rest.utils.ContextUtil; +import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; -import org.dspace.content.Site; import org.dspace.content.service.SiteService; import org.dspace.core.Context; +import org.dspace.eperson.EPerson; import org.dspace.eperson.service.AccountService; import org.dspace.eperson.service.EPersonService; import org.springframework.beans.factory.annotation.Autowired; @@ -83,13 +82,6 @@ public ResponseEntity register(HttpServletRequest request, Http throws SQLException, IOException, MessagingException, AuthorizeException { Context context = ContextUtil.obtainContext(request); - AuthorizationFeature epersonRegistration = authorizationFeatureService.find("epersonRegistration"); - Site site = siteService.findSite(context); - SiteRest siteRest = converterService.toRest(site, Projection.DEFAULT); - if (!authorizationFeatureService.isAuthorized(context, epersonRegistration, siteRest)) { - throw new AccessDeniedException( - "Registration is disabled, you are not authorized to create a new Authorization"); - } ObjectMapper mapper = new ObjectMapper(); RegistrationRest registrationRest; try { @@ -101,9 +93,18 @@ public ResponseEntity register(HttpServletRequest request, Http if (StringUtils.isBlank(registrationRest.getEmail())) { throw new UnprocessableEntityException("The email cannot be omitted from the Registration endpoint"); } - if (ePersonService.findByEmail(context, registrationRest.getEmail()) != null) { + EPerson eperson = ePersonService.findByEmail(context, registrationRest.getEmail()); + if (eperson != null) { + if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail(), eperson.canLogIn())) { + throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + + eperson.getEmail()); + } accountService.sendForgotPasswordInfo(context, registrationRest.getEmail()); } else { + if (!AuthorizeUtil.authorizeNewAccountRegistration(context, request)) { + throw new AccessDeniedException( + "Registration is disabled, you are not authorized to create a new Authorization"); + } accountService.sendRegistrationInfo(context, registrationRest.getEmail()); } context.complete(); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java index d230d2ef972a..a03d68fcc9a9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/authorization/impl/EPersonRegistrationFeature.java @@ -13,9 +13,8 @@ import org.dspace.app.rest.authorization.AuthorizationFeatureDocumentation; import org.dspace.app.rest.model.BaseObjectRest; import org.dspace.app.rest.model.SiteRest; -import org.dspace.authenticate.service.AuthenticationService; +import org.dspace.app.util.AuthorizeUtil; import org.dspace.core.Context; -import org.dspace.services.ConfigurationService; import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -31,12 +30,6 @@ public class EPersonRegistrationFeature implements AuthorizationFeature { public static final String NAME = "epersonRegistration"; - @Autowired - private ConfigurationService configurationService; - - @Autowired - private AuthenticationService authenticationService; - @Autowired private RequestService requestService; @@ -45,11 +38,11 @@ public boolean isAuthorized(Context context, BaseObjectRest object) throws SQLEx if (!(object instanceof SiteRest)) { return false; } - if (configurationService.getBooleanProperty("user.registration", true)) { - return authenticationService - .allowSetPassword(context, requestService.getCurrentRequest().getHttpServletRequest(), null); + if (!AuthorizeUtil.authorizeNewAccountRegistration(context, + requestService.getCurrentRequest().getHttpServletRequest())) { + return false; } - return false; + return true; } @Override diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index 43e46a85c9f0..4226ff6ab4c1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -18,19 +18,17 @@ import org.apache.log4j.Logger; import org.dspace.app.rest.Parameter; import org.dspace.app.rest.SearchRestMethod; -import org.dspace.app.rest.authorization.AuthorizationFeature; import org.dspace.app.rest.authorization.AuthorizationFeatureService; +import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.EPersonRest; import org.dspace.app.rest.model.MetadataRest; import org.dspace.app.rest.model.MetadataValueRest; -import org.dspace.app.rest.model.SiteRest; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; -import org.dspace.app.rest.projection.Projection; +import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.service.AuthorizeService; -import org.dspace.content.Site; import org.dspace.content.service.SiteService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -133,32 +131,30 @@ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, St throws AuthorizeException, SQLException { RegistrationData registrationData = registrationDataService.findByToken(context, token); if (registrationData == null) { - throw new AccessDeniedException("The token given as parameter: " + token + " does not exist" + + throw new DSpaceBadRequestException("The token given as parameter: " + token + " does not exist" + " in the database"); } if (es.findByEmail(context, registrationData.getEmail()) != null) { - throw new AccessDeniedException("The token given already contains an email address that resolves" + + throw new DSpaceBadRequestException("The token given already contains an email address that resolves" + "to an eperson"); } String emailFromJson = epersonRest.getEmail(); if (StringUtils.isNotBlank(emailFromJson)) { if (!StringUtils.equalsIgnoreCase(registrationData.getEmail(), emailFromJson)) { - throw new AccessDeniedException("The email resulting from the token does not match the email given" + - " in the json body. Email from token: " + + throw new DSpaceBadRequestException("The email resulting from the token does not match the email given" + + " in the json body. Email from token: " + registrationData.getEmail() + " email from the json body: " + emailFromJson); } } if (epersonRest.isSelfRegistered() != null && !epersonRest.isSelfRegistered()) { - throw new AccessDeniedException("The self registered property cannot be set to false using this method" + - " with a token"); + throw new DSpaceBadRequestException("The self registered property cannot be set to false using this method" + + " with a token"); } checkRequiredProperties(epersonRest); - AuthorizationFeature epersonRegistration = authorizationFeatureService.find("epersonRegistration"); - Site site = siteService.findSite(context); - SiteRest siteRest = converter.toRest(site, Projection.DEFAULT); - if (!authorizationFeatureService.isAuthorized(context, epersonRegistration, siteRest)) { - throw new AccessDeniedException( + if (!AuthorizeUtil.authorizeNewAccountRegistration(context, requestService + .getCurrentRequest().getHttpServletRequest())) { + throw new DSpaceBadRequestException( "Registration is disabled, you are not authorized to create a new Authorization"); } // We'll turn off authorisation system because this call isn't admin based as it's token based @@ -177,13 +173,13 @@ private void checkRequiredProperties(EPersonRest epersonRest) { List epersonLastName = metadataRest.getMap().get("eperson.lastname"); if (epersonFirstName == null || epersonLastName == null || epersonFirstName.isEmpty() || epersonLastName.isEmpty()) { - throw new AccessDeniedException("The eperson.firstname and eperson.lastname values need to be " + + throw new DSpaceBadRequestException("The eperson.firstname and eperson.lastname values need to be " + "filled in"); } } String password = epersonRest.getPassword(); - if (StringUtils.isBlank(password)) { - throw new AccessDeniedException("the password cannot be left blank"); + if (!accountService.verifyPasswordStructure(password)) { + throw new DSpaceBadRequestException("the password cannot be left blank"); } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java index 387721915dbe..5bac19ba963c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java @@ -13,6 +13,7 @@ import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -61,6 +62,10 @@ public R perform(Context context, R object, Operation operation) { if (StringUtils.isNotBlank(token)) { patchWithToken(context,eperson, token, operation); } + if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail(), eperson.canLogIn())) { + throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + + eperson.getEmail()); + } ePersonService.setPassword(eperson, (String) operation.getValue()); return object; } else { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 8f48ac4a90da..24ecd00b2269 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -1881,8 +1881,7 @@ public void postEPersonWithTokenWithoutEmailProperty() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"password\":\"somePassword\"," + "\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - MvcResult mvcResult = getClient(token).perform(post("/api/eperson/epersons") + MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) @@ -1932,8 +1931,7 @@ public void postEPersonWithTokenWithEmailProperty() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - MvcResult mvcResult = getClient(token).perform(post("/api/eperson/epersons") + MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) @@ -1985,8 +1983,7 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - MvcResult mvcResult = getClient(token).perform(post("/api/eperson/epersons") + MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) @@ -2048,12 +2045,11 @@ public void postEPersonWithTokenWithTwoTokensDifferentEmailProperty() throws Exc "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmailTwo + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmailTwo); assertNull(createdEPerson); @@ -2084,12 +2080,11 @@ public void postEPersonWithRandomTokenWithEmailProperty() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", "randomToken") .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); @@ -2118,12 +2113,11 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredFalseProperty() throws "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":false,\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); @@ -2151,12 +2145,11 @@ public void postEPersonWithTokenWithoutLastNameProperty() throws Exception { String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]},\"selfRegistered\":true," + "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); @@ -2184,12 +2177,11 @@ public void postEPersonWithTokenWithoutFirstNameProperty() throws Exception { String json = "{\"metadata\":{\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true," + "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); @@ -2218,12 +2210,11 @@ public void postEPersonWithTokenWithoutPasswordProperty() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]}," + "\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); assertNull(createdEPerson); @@ -2253,12 +2244,11 @@ public void postEPersonWithWrongToken() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"password\":\"somePassword\"," + "\"type\":\"eperson\"}"; - String token = getAuthToken(admin.getEmail(), password); - getClient(token).perform(post("/api/eperson/epersons") + getClient().perform(post("/api/eperson/epersons") .param("token", forgotPasswordToken) .content(json) .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isForbidden()); + .andExpect(status().isBadRequest()); EPerson createdEPerson = ePersonService.findByEmail(context, newEmail); assertNull(createdEPerson); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java index d3147d461178..f50b4c9d65e5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -78,4 +78,30 @@ public void registrationFlowTest() throws Exception { registrationDataDAO.delete(context, registrationData); } } + + @Test + public void forgotPasswordTest() throws Exception { + context.turnOffAuthorisationSystem(); + configurationService.setProperty("user.registration", false); + context.restoreAuthSystemState(); + + List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(0, registrationDataList.size()); + + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(eperson.getEmail()); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(1, registrationDataList.size()); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); + Iterator iterator = registrationDataList.iterator(); + while (iterator.hasNext()) { + RegistrationData registrationData = iterator.next(); + registrationDataDAO.delete(context, registrationData); + } + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java index a9bbdce3db23..754804edf436 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java @@ -62,12 +62,9 @@ public void userRegistrationEnabledSuccessTest() throws Exception { SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT); String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref(); - // access the authorization for the admin user - String adminToken = getAuthToken(admin.getEmail(), password); - getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + getClient().perform(get("/api/authz/authorizations/search/objectAndFeature") .param("uri", siteUri) - .param("eperson", admin.getID().toString()) .param("feature", epersonRegistrationFeature.getName())) .andExpect(status().isOk()); } @@ -82,12 +79,9 @@ public void userRegistrationDisabledUnAuthorizedTest() throws Exception { context.turnOffAuthorisationSystem(); configurationService.setProperty("user.registration", false); context.restoreAuthSystemState(); - // access the authorization for the admin user - String adminToken = getAuthToken(admin.getEmail(), password); - getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + getClient().perform(get("/api/authz/authorizations/search/objectAndFeature") .param("uri", siteUri) - .param("eperson", admin.getID().toString()) .param("feature", epersonRegistrationFeature.getName())) .andExpect(status().isNoContent()); @@ -104,12 +98,9 @@ public void userRegistrationEnabledShibTest() throws Exception { SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT); String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref(); - // access the authorization for the admin user - String adminToken = getAuthToken(admin.getEmail(), password); - getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + getClient().perform(get("/api/authz/authorizations/search/objectAndFeature") .param("uri", siteUri) - .param("eperson", admin.getID().toString()) .param("feature", epersonRegistrationFeature.getName())) .andExpect(status().isOk()); @@ -118,9 +109,8 @@ public void userRegistrationEnabledShibTest() throws Exception { configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY); context.restoreAuthSystemState(); - getClient(adminToken).perform(get("/api/authz/authorizations/search/objectAndFeature") + getClient().perform(get("/api/authz/authorizations/search/objectAndFeature") .param("uri", siteUri) - .param("eperson", admin.getID().toString()) .param("feature", epersonRegistrationFeature.getName())) .andExpect(status().isNoContent()); From 8ff77a9e6d289e06e133d6e0dfd694337140f4a0 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Mon, 11 May 2020 11:51:38 +0200 Subject: [PATCH 32/73] [Task 70808] added javadoc --- .../main/java/org/dspace/eperson/service/AccountService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java b/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java index 23d46ea00a1d..45fa6d26b1b2 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java +++ b/dspace-api/src/main/java/org/dspace/eperson/service/AccountService.java @@ -47,5 +47,10 @@ public String getEmail(Context context, String token) public void deleteToken(Context context, String token) throws SQLException; + /** + * This method verifies that a certain String adheres to the password rules for DSpace + * @param password The String to be checked + * @return A boolean indicating whether or not the given String adheres to the password rules + */ public boolean verifyPasswordStructure(String password); } From 6fbc15486d14a0584c64dc51ce7e635d920f8289 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Mon, 11 May 2020 14:52:02 +0200 Subject: [PATCH 33/73] dc.source metadata is not present in the upload section, so we con not apply patch remove request --- .../dspace/app/rest/WorkspaceItemRestRepositoryIT.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index b2f4de1d0e6b..d77818cdfbee 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -2422,9 +2422,8 @@ public void patchUploadTest() throws Exception { is("Description"))) ; - // try to remove the description and the source now + // try to remove the description List removeOpts = new ArrayList(); - removeOpts.add(new RemoveOperation("/sections/upload/files/0/metadata/dc.source/0")); removeOpts.add(new RemoveOperation("/sections/upload/files/0/metadata/dc.description")); patchBody = getPatchContent(removeOpts); @@ -2432,8 +2431,6 @@ public void patchUploadTest() throws Exception { .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isOk()) - // check the removed source - .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source']").doesNotExist()) // check the filename still here .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", is("newfilename.pdf"))) @@ -2444,7 +2441,8 @@ public void patchUploadTest() throws Exception { // check that changes persist getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) .andExpect(status().isOk()) - .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source']").doesNotExist()) + .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value", + is("/local/path/simple-article.pdf"))) .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", is("newfilename.pdf"))) .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.description']").doesNotExist()) ; From 7b7e7cd14e79c074ae6e739b609070388d28f83e Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 14 May 2020 10:44:14 +0200 Subject: [PATCH 34/73] added IT for patch request on metadata with qualdrop_value --- .../rest/WorkspaceItemRestRepositoryIT.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index d77818cdfbee..bcb0879a5dbd 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -1650,15 +1650,20 @@ public void patchAddMetadataTest() throws Exception { context.restoreAuthSystemState(); // try to add the title - List addTitle = new ArrayList(); + List operations = new ArrayList(); // create a list of values to use in add operation - List> values = new ArrayList>(); + List> titelValues = new ArrayList>(); + List> uriValues = new ArrayList>(); Map value = new HashMap(); + Map value2 = new HashMap(); value.put("value", "New Title"); - values.add(value); - addTitle.add(new AddOperation("/sections/traditionalpageone/dc.title", values)); + value2.put("value", "https://www.dspace.org"); + titelValues.add(value); + uriValues.add(value2); + operations.add(new AddOperation("/sections/traditionalpageone/dc.title", titelValues)); + operations.add(new AddOperation("/sections/traditionalpageone/dc.identifier.uri", uriValues)); - String patchBody = getPatchContent(addTitle); + String patchBody = getPatchContent(operations); getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) @@ -1667,7 +1672,10 @@ public void patchAddMetadataTest() throws Exception { .andExpect(jsonPath("$", // check if the new title if back and the other values untouched Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, - "New Title", "2017-10-17", "ExtraEntry")))); + "New Title", "2017-10-17", "ExtraEntry")))) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.sections.traditionalpageone['dc.identifier.uri'][0].value", + is("https://www.dspace.org"))))); // verify that the patch changes have been persisted getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) @@ -1676,6 +1684,9 @@ public void patchAddMetadataTest() throws Exception { .andExpect(jsonPath("$", Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, "New Title", "2017-10-17", "ExtraEntry")))) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.sections.traditionalpageone['dc.identifier.uri'][0].value", + is("https://www.dspace.org"))))) ; } From 3a1982656a0dc418e2903fc44eab7b2034e390e4 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 14 May 2020 10:48:15 +0200 Subject: [PATCH 35/73] this method should also check metadata with qualdrop_value --- .../java/org/dspace/app/util/DCInputSet.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java index faa3fb719047..e6a88895fdbb 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; +import org.dspace.core.Utils; /** * Class representing all DC inputs required for a submission, organized into pages * @@ -107,9 +108,20 @@ public boolean isFieldPresent(String fieldName) { for (int i = 0; i < inputs.length; i++) { for (int j = 0; j < inputs[i].length; j++) { DCInput field = inputs[i][j]; - String fullName = field.getFieldName(); - if (fullName.equals(fieldName)) { - return true; + if (field.getInputType().equals("qualdrop_value")) { + List pairs = field.getPairs(); + for (int k = 0; k < pairs.size(); k += 2) { + String qualifier = pairs.get(k + 1); + String fullName = Utils.standardize(field.getSchema(), field.getElement(), qualifier, "."); + if (fullName.equals(fieldName)) { + return true; + } + } + } else { + String fullName = field.getFieldName(); + if (fullName.equals(fieldName)) { + return true; + } } } } From d3cc506b260470125c4690e73bd3f1fbca86f139 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 19 May 2020 13:42:24 +0200 Subject: [PATCH 36/73] [Task 70937] removed the canLogin parameter from the AuthorizeUtil#authorizeUpdatePassword method --- .../src/main/java/org/dspace/app/util/AuthorizeUtil.java | 8 ++++---- .../org/dspace/app/rest/RegistrationRestController.java | 2 +- .../patch/operation/EPersonPasswordReplaceOperation.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index d5dbf5a514b4..92f4fc931188 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -28,6 +28,7 @@ import org.dspace.content.service.ItemService; import org.dspace.core.Constants; import org.dspace.core.Context; +import org.dspace.eperson.EPerson; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.services.factory.DSpaceServicesFactory; @@ -555,13 +556,12 @@ public static boolean authorizeNewAccountRegistration(Context context, HttpServl * with the given email and canLogin property * @param context The relevant DSpace context * @param email The email to be checked - * @param canLogin The boolean canLogin property * @return A boolean indicating if the password can be updated or not */ - public static boolean authorizeUpdatePassword(Context context, String email, boolean canLogin) { + public static boolean authorizeUpdatePassword(Context context, String email) { try { - if (EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, email) != null - && canLogin) { + EPerson eperson = EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, email); + if (eperson != null && eperson.canLogIn()) { return true; } } catch (SQLException e) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java index 741efa995b02..34c5a8055cd4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java @@ -95,7 +95,7 @@ public ResponseEntity register(HttpServletRequest request, Http } EPerson eperson = ePersonService.findByEmail(context, registrationRest.getEmail()); if (eperson != null) { - if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail(), eperson.canLogIn())) { + if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail())) { throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + eperson.getEmail()); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java index 5bac19ba963c..ae02200d98b7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java @@ -62,7 +62,7 @@ public R perform(Context context, R object, Operation operation) { if (StringUtils.isNotBlank(token)) { patchWithToken(context,eperson, token, operation); } - if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail(), eperson.canLogIn())) { + if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail())) { throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + eperson.getEmail()); } From 2a0971d8fbb4f27e07efe73476243c3e3c03133c Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 19 May 2020 14:27:40 +0200 Subject: [PATCH 37/73] [Task 70937] fixes after merge --- .../src/main/java/org/dspace/app/util/AuthorizeUtil.java | 3 +-- .../java/org/dspace/app/rest/RegistrationRestController.java | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index d2089d617000..521eff6fc1ed 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -30,11 +30,10 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; -import org.dspace.eperson.factory.EPersonServiceFactory; -import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.eperson.Group; import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.GroupService; +import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; import org.dspace.xmlworkflow.storedcomponents.CollectionRole; import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java index 34c5a8055cd4..c79c2a0969a6 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java @@ -31,7 +31,7 @@ import org.dspace.eperson.service.EPersonService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.webmvc.ControllerUtils; -import org.springframework.hateoas.ResourceSupport; +import org.springframework.hateoas.RepresentationModel; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.AccessDeniedException; @@ -78,7 +78,7 @@ public class RegistrationRestController { * @throws AuthorizeException If something goes wrong */ @RequestMapping(method = RequestMethod.POST) - public ResponseEntity register(HttpServletRequest request, HttpServletResponse response) + public ResponseEntity> register(HttpServletRequest request, HttpServletResponse response) throws SQLException, IOException, MessagingException, AuthorizeException { Context context = ContextUtil.obtainContext(request); From f33bfac1958d9195674244d038f13af91fd37a71 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 12:54:08 +0200 Subject: [PATCH 38/73] added ITs for upload patch request --- .../rest/WorkspaceItemRestRepositoryIT.java | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index bcb0879a5dbd..ac6a617773f9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -3506,4 +3506,130 @@ public void patchUploadWrongPathTest() throws Exception { .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isUnprocessableEntity()); } + + @Test + public void patchUploadMissingFieldTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2017-10-17") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addOpts = new ArrayList(); + Map value = new HashMap(); + value.put("value", "test text"); + + addOpts.add(new AddOperation("/sections/upload/files/0/metadata", value)); + + String patchBody = getPatchContent(addOpts); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchUploadNotExistingPropertyTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2017-10-17") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addOpts = new ArrayList(); + Map value = new HashMap(); + value.put("value", "test text"); + + addOpts.add(new AddOperation("/sections/upload/files/0/not-existing-property/dc.title", value)); + + String patchBody = getPatchContent(addOpts); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + + @Test + public void patchUploadWithWrongPathTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2017-10-17") + .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) + .build(); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List addOpts = new ArrayList(); + Map value = new HashMap(); + value.put("value", "test text"); + + addOpts.add(new AddOperation("/sections/upload/files/0", value)); + + String patchBody = getPatchContent(addOpts); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + + addOpts.add(new AddOperation("/sections/upload/files", value)); + patchBody = getPatchContent(addOpts); + + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } + } From 212985bed68611f7818cba689755af69fbc4c8a7 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 13:02:08 +0200 Subject: [PATCH 39/73] refactoring doPatchProcessing of UploadStep --- ...tstreamMetadataValueAddPatchOperation.java | 8 ++- ...streamMetadataValueMovePatchOperation.java | 8 ++- ...reamMetadataValueRemovePatchOperation.java | 8 ++- ...eamMetadataValueReplacePatchOperation.java | 8 ++- .../app/rest/submit/step/UploadStep.java | 39 +++------------ .../BitstreamMetadataValuePathUtils.java | 50 +++++++++++++++++++ dspace/config/spring/api/core-services.xml | 1 + 7 files changed, 86 insertions(+), 36 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java index 466207ca571c..c7531e981029 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java @@ -11,6 +11,7 @@ import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.LateObjectEvaluator; +import org.dspace.app.rest.utils.BitstreamMetadataValuePathUtils; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.InProgressSubmission; @@ -39,12 +40,17 @@ public class BitstreamMetadataValueAddPatchOperation extends MetadataValueAddPat @Autowired ItemService itemService; + @Autowired + BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; + @Override void add(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) throws Exception { //"path": "/sections/upload/files/0/metadata/dc.title/2" //"abspath": "/files/0/metadata/dc.title/2" - String[] split = getAbsolutePath(path).split("/"); + String absolutePath = getAbsolutePath(path); + String[] split = absolutePath.split("/"); + bitstreamMetadataValuePathUtils.validate(absolutePath); Item item = source.getItem(); List bundle = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME); ; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java index d5ade7b9c252..d03d47c91eb5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java @@ -9,6 +9,7 @@ import java.util.List; +import org.dspace.app.rest.utils.BitstreamMetadataValuePathUtils; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.InProgressSubmission; @@ -35,12 +36,17 @@ public class BitstreamMetadataValueMovePatchOperation extends MetadataValueMoveP @Autowired ItemService itemService; + @Autowired + BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; + @Override void move(Context context, Request currentRequest, InProgressSubmission source, String path, String from) throws Exception { //"path": "/sections/upload/files/0/metadata/dc.title/2" //"abspath": "/files/0/metadata/dc.title/2" - String[] splitTo = getAbsolutePath(path).split("/"); + String absolutePath = getAbsolutePath(path); + String[] splitTo = absolutePath.split("/"); + bitstreamMetadataValuePathUtils.validate(absolutePath); Item item = source.getItem(); List bundle = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME); for (Bundle bb : bundle) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java index 1f811aeaeae6..0341192b21bc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java @@ -9,6 +9,7 @@ import java.util.List; +import org.dspace.app.rest.utils.BitstreamMetadataValuePathUtils; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.InProgressSubmission; @@ -35,12 +36,17 @@ public class BitstreamMetadataValueRemovePatchOperation extends MetadataValueRem @Autowired ItemService itemService; + @Autowired + BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; + @Override void remove(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) throws Exception { //"path": "/sections/upload/files/0/metadata/dc.title/2" //"abspath": "/files/0/metadata/dc.title/2" - String[] split = getAbsolutePath(path).split("/"); + String absolutePath = getAbsolutePath(path); + String[] split = absolutePath.split("/"); + bitstreamMetadataValuePathUtils.validate(absolutePath); Item item = source.getItem(); List bundle = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME); ; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java index de228918223d..fd5b39515710 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java @@ -12,6 +12,7 @@ import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.LateObjectEvaluator; +import org.dspace.app.rest.utils.BitstreamMetadataValuePathUtils; import org.dspace.content.Bitstream; import org.dspace.content.Bundle; import org.dspace.content.InProgressSubmission; @@ -38,12 +39,17 @@ public class BitstreamMetadataValueReplacePatchOperation extends MetadataValueRe @Autowired ItemService itemService; + @Autowired + BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; + @Override void replace(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) throws Exception { //"path": "/sections/upload/files/0/metadata/dc.title/2" //"abspath": "/files/0/metadata/dc.title/2" - String[] split = getAbsolutePath(path).split("/"); + String absolutePath = getAbsolutePath(path); + String[] split = absolutePath.split("/"); + bitstreamMetadataValuePathUtils.validate(absolutePath); Item item = source.getItem(); List bundle = itemService.getBundles(item, Constants.CONTENT_BUNDLE_NAME); for (Bundle bb : bundle) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java index f32d686efe8f..11fd36935cd3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/UploadStep.java @@ -24,9 +24,6 @@ import org.dspace.app.rest.submit.factory.PatchOperationFactory; import org.dspace.app.rest.submit.factory.impl.PatchOperation; import org.dspace.app.rest.utils.Utils; -import org.dspace.app.util.DCInputSet; -import org.dspace.app.util.DCInputsReader; -import org.dspace.app.util.DCInputsReaderException; import org.dspace.app.util.SubmissionStepConfig; import org.dspace.content.Bitstream; import org.dspace.content.BitstreamFormat; @@ -51,11 +48,6 @@ public class UploadStep extends org.dspace.submit.step.UploadStep public static final String UPLOAD_STEP_METADATA_SECTION = "bitstream-metadata"; - private DCInputsReader inputReader; - - public UploadStep() throws DCInputsReaderException { - inputReader = new DCInputsReader(); - } @Override public DataUpload getData(SubmissionService submissionService, InProgressSubmission obj, SubmissionStepConfig config) throws Exception { @@ -75,7 +67,7 @@ public DataUpload getData(SubmissionService submissionService, InProgressSubmiss public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, SubmissionStepConfig stepConf) throws Exception { - String instance = ""; + String instance = null; if ("remove".equals(op.getOp())) { if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; @@ -93,33 +85,16 @@ public void doPatchProcessing(Context context, Request currentRequest, InProgres } else { if (op.getPath().contains(UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY)) { instance = UPLOAD_STEP_ACCESSCONDITIONS_OPERATION_ENTRY; - } else { + } else if (op.getPath().contains(UPLOAD_STEP_METADATA_PATH)) { instance = UPLOAD_STEP_METADATA_OPERATION_ENTRY; } } - PatchOperation patchOperation = new PatchOperationFactory().instanceOf(instance, op.getOp()); - if (instance.equals(AbstractRestProcessingStep.UPLOAD_STEP_METADATA_OPERATION_ENTRY)) { - DCInputSet inputConfig = inputReader.getInputsByFormName(UploadStep.UPLOAD_STEP_METADATA_SECTION); - String[] split = patchOperation.getAbsolutePath(op.getPath()).split("/"); - String metadata = findMetadata(split); - if (inputConfig.isFieldPresent(metadata)) { - patchOperation.perform(context, currentRequest, source, op); - } else { - throw new UnprocessableEntityException("The attribute " + metadata + " does not present in section " - + UploadStep.UPLOAD_STEP_METADATA_SECTION); - } - } else { - patchOperation.perform(context, currentRequest, source, op); + if (instance == null) { + throw new UnprocessableEntityException("The path " + op.getPath() + " is not supported by the operation " + + op.getOp()); } - } - - private String findMetadata(String[] metadata) { - for (String s : metadata) { - if (s.contains("dc.")) { - return s; - } - } - return null; + PatchOperation patchOperation = new PatchOperationFactory().instanceOf(instance, op.getOp()); + patchOperation.perform(context, currentRequest, source, op); } @Override diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java new file mode 100644 index 000000000000..d856538328ec --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java @@ -0,0 +1,50 @@ +/** + * 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.utils; + +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.submit.step.UploadStep; +import org.dspace.app.util.DCInputSet; +import org.dspace.app.util.DCInputsReader; +import org.dspace.app.util.DCInputsReaderException; + +/** + * Utils class offering methods to validate patch operations for bitstream metadata in the submission + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.it) + */ +public class BitstreamMetadataValuePathUtils { + + private DCInputsReader inputReader; + + BitstreamMetadataValuePathUtils() throws DCInputsReaderException { + inputReader = new DCInputsReader(); + } + + /** + * Method to verify that the path included in the patch operation is supported + * by the submission configuration of the upload section + * + * @param absolutePath the path in the json patch operation + * @throws DCInputsReaderException if an error occurs reading the + * submission configuration + * @throws UnprocessableEntityException if the path is invalid + */ + public void validate(String absolutePath) throws DCInputsReaderException { + String[] split = absolutePath.split("/"); + DCInputSet inputConfig = inputReader.getInputsByFormName(UploadStep.UPLOAD_STEP_METADATA_SECTION); + if (split.length >= 4) { + if (!inputConfig.isFieldPresent(split[3])) { + throw new UnprocessableEntityException("The field " + split[3] + " is not present in section " + + UploadStep.UPLOAD_STEP_METADATA_SECTION); + } + } else { + throw new UnprocessableEntityException("The path " + split.toString() + " cannot be patched "); + } + } +} diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index 316f81fd18a2..455d57c5213f 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -62,6 +62,7 @@ + From bfa83b75993edd7d428c6e5d25aa49586b754394 Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Wed, 20 May 2020 13:09:33 +0200 Subject: [PATCH 40/73] Disabling the CC License step in the default configuration --- .../dspaceFolder/config/item-submission.xml | 282 ++++++++++++++++++ dspace/config/item-submission.xml | 8 +- 2 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 dspace-server-webapp/src/test/data/dspaceFolder/config/item-submission.xml diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/item-submission.xml new file mode 100644 index 000000000000..481b5081768b --- /dev/null +++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/item-submission.xml @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.dspace.app.rest.submit.step.CollectionStep + collection + submission + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.steptwo + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + + submit.progressbar.upload + org.dspace.app.rest.submit.step.UploadStep + upload + + + submit.progressbar.license + org.dspace.app.rest.submit.step.LicenseStep + license + submission + + + + + + + + submit.progressbar.CClicense + org.dspace.app.rest.submit.step.CCLicenseStep + cclicense + + + + + + + + + + + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + submit.progressbar.describe.stepone + org.dspace.app.rest.submit.step.DescribeStep + submission-form + + + + + Sample + org.dspace.submit.step.SampleStep + sample + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dspace/config/item-submission.xml b/dspace/config/item-submission.xml index 481b5081768b..25162341e4b4 100644 --- a/dspace/config/item-submission.xml +++ b/dspace/config/item-submission.xml @@ -115,9 +115,9 @@ - submit.progressbar.CClicense + - + + + From 98f2447cbb938e08ae3b805443747b8ef10555cb Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 13:13:36 +0200 Subject: [PATCH 41/73] Implement community feedbacks --- dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java | 1 + .../main/java/org/dspace/app/rest/submit/step/LicenseStep.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java b/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java index e6a88895fdbb..0f2333004c43 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java +++ b/dspace-api/src/main/java/org/dspace/app/util/DCInputSet.java @@ -108,6 +108,7 @@ public boolean isFieldPresent(String fieldName) { for (int i = 0; i < inputs.length; i++) { for (int j = 0; j < inputs[i].length; j++) { DCInput field = inputs[i][j]; + // If this is a "qualdrop_value" field, then the full field name is the field + dropdown qualifier if (field.getInputType().equals("qualdrop_value")) { List pairs = field.getPairs(); for (int k = 0; k < pairs.size(); k += 2) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java index a57951d09aba..fd4ccc64207e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/LicenseStep.java @@ -61,7 +61,7 @@ public void doPatchProcessing(Context context, Request currentRequest, InProgres patchOperation.perform(context, currentRequest, source, op); } else { - throw new UnprocessableEntityException("This path : " + op.getPath() + " can not to be replaced"); + throw new UnprocessableEntityException("The path " + op.getPath() + " cannot be patched"); } } } From ab0aec46984d15b466a19cc582f9a1468a79817e Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 15:30:53 +0200 Subject: [PATCH 42/73] move bean to the right file --- .../src/main/resources/spring/spring-dspace-core-services.xml | 1 + dspace/config/spring/api/core-services.xml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml index 61459f11d64f..0358049b18c5 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml @@ -108,4 +108,5 @@ + diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index 455d57c5213f..316f81fd18a2 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -62,7 +62,6 @@ - From 9404e24c4e8d8f4774e7eb0b667deee47351bb3b Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 15:51:12 +0200 Subject: [PATCH 43/73] fix: wrong calling toString --- .../dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java index d856538328ec..4ff42d70cd72 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java @@ -44,7 +44,7 @@ public void validate(String absolutePath) throws DCInputsReaderException { + UploadStep.UPLOAD_STEP_METADATA_SECTION); } } else { - throw new UnprocessableEntityException("The path " + split.toString() + " cannot be patched "); + throw new UnprocessableEntityException("The path " + absolutePath + " cannot be patched "); } } } From ccfbbfce9957cd15a2cdde5a281ee49b908026c0 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 15:55:13 +0200 Subject: [PATCH 44/73] added IT patchDeleteSection --- .../rest/WorkspaceItemRestRepositoryIT.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index ac6a617773f9..e50348891266 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -3632,4 +3632,44 @@ public void patchUploadWithWrongPathTest() throws Exception { .andExpect(status().isUnprocessableEntity()); } + @Test + public void patchDeleteSectionTest() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test WorkspaceItem") + .withIssueDate("2020-01-21") + .withSubject("Subject 1") + .withSubject("Subject 2") + .build(); + + context.restoreAuthSystemState(); + + List operations = new ArrayList(); + operations.add(new RemoveOperation("/sections/traditionalpagetwo")); + String patchBody = getPatchContent(operations); + + String authToken = getAuthToken(eperson.getEmail(), password); + + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist()) + .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.description.abstract']").doesNotExist()); + + // verify that the patch changes have been persisted + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist()) + .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.description.abstract']").doesNotExist()); + } } From 76ff785b076cbb10e38892c7a60f885520534e9d Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 20 May 2020 16:02:32 +0200 Subject: [PATCH 45/73] added implementation for delete section --- .../app/rest/submit/step/DescribeStep.java | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java index b0134973a9d8..26e904f04432 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/DescribeStep.java @@ -10,10 +10,12 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.MetadataValueRest; import org.dspace.app.rest.model.patch.Operation; +import org.dspace.app.rest.model.patch.RemoveOperation; import org.dspace.app.rest.model.step.DataDescribe; import org.dspace.app.rest.submit.AbstractRestProcessingStep; import org.dspace.app.rest.submit.SubmissionService; @@ -114,16 +116,53 @@ private void readField(InProgressSubmission obj, SubmissionStepConfig config, Da public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, SubmissionStepConfig stepConf) throws Exception { - PatchOperation patchOperation = new PatchOperationFactory() - .instanceOf(DESCRIBE_STEP_METADATA_OPERATION_ENTRY, op.getOp()); + String[] pathParts = op.getPath().substring(1).split("/"); DCInputSet inputConfig = inputReader.getInputsByFormName(stepConf.getId()); - String[] split = patchOperation.getAbsolutePath(op.getPath()).split("/"); - if (inputConfig.isFieldPresent(split[0])) { - patchOperation.perform(context, currentRequest, source, op); + if ("remove".equals(op.getOp()) && pathParts.length < 3) { + // manage delete all step fields + String[] path = op.getPath().substring(1).split("/", 3); + String configId = path[1]; + List fieldsName = getInputFieldsName(inputConfig, configId); + for (String fieldName : fieldsName) { + String fieldPath = op.getPath() + "/" + fieldName; + Operation fieldRemoveOp = new RemoveOperation(fieldPath); + PatchOperation patchOperation = new PatchOperationFactory() + .instanceOf(DESCRIBE_STEP_METADATA_OPERATION_ENTRY, fieldRemoveOp.getOp()); + patchOperation.perform(context, currentRequest, source, fieldRemoveOp); + } } else { - throw new UnprocessableEntityException("The attribute " + split[0] + " does not present in section " - + inputConfig.getFormName()); + PatchOperation patchOperation = new PatchOperationFactory() + .instanceOf(DESCRIBE_STEP_METADATA_OPERATION_ENTRY, op.getOp()); + String[] split = patchOperation.getAbsolutePath(op.getPath()).split("/"); + if (inputConfig.isFieldPresent(split[0])) { + patchOperation.perform(context, currentRequest, source, op); + } else { + throw new UnprocessableEntityException("The field " + split[0] + " is not present in section " + + inputConfig.getFormName()); + } } } + private List getInputFieldsName(DCInputSet inputConfig, String configId) throws DCInputsReaderException { + List fieldsName = new ArrayList(); + for (DCInput[] row : inputConfig.getFields()) { + for (DCInput input : row) { + if (input.isQualdropValue()) { + for (Object qualifier : input.getPairs()) { + fieldsName.add(input.getFieldName() + "." + (String) qualifier); + } + } else if (StringUtils.equalsIgnoreCase(input.getInputType(), "group") || + StringUtils.equalsIgnoreCase(input.getInputType(), "inline-group")) { + log.info("Called child form:" + configId + "-" + + Utils.standardize(input.getSchema(), input.getElement(), input.getQualifier(), "-")); + DCInputSet inputConfigChild = inputReader.getInputsByFormName(configId + "-" + Utils + .standardize(input.getSchema(), input.getElement(), input.getQualifier(), "-")); + fieldsName.addAll(getInputFieldsName(inputConfigChild, configId)); + } else { + fieldsName.add(input.getFieldName()); + } + } + } + return fieldsName; + } } From ffce2e4299595055ba4d0d5e8b0d9e7719c02de8 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Wed, 27 May 2020 13:25:56 +0200 Subject: [PATCH 46/73] 70815: Angular feedback - add self link --- .../SubmissionCCLicenseSearchController.java | 17 +++-- .../SubmissionCCLicenseUrlConverter.java | 41 ++++++++++++ ...ionCCLicenseUrlResourceHalLinkFactory.java | 64 +++++++++++++++++++ .../rest/model/SubmissionCCLicenseRest.java | 2 +- .../model/SubmissionCCLicenseUrlRest.java | 52 +++++++++++++++ .../SubmissionCCLicenseUrlResource.java | 23 +++++++ ...SubmissionCCLicenseSearchControllerIT.java | 15 +++-- 7 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseUrlResource.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java index 26e52c893305..f47376757c21 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java @@ -12,9 +12,11 @@ import javax.servlet.ServletRequest; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.PlainTextValueRest; import org.dspace.app.rest.model.SubmissionCCLicenseRest; +import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; +import org.dspace.app.rest.model.hateoas.SubmissionCCLicenseUrlResource; import org.dspace.app.rest.utils.Utils; import org.dspace.license.service.CreativeCommonsService; import org.dspace.services.RequestService; @@ -39,16 +41,19 @@ public class SubmissionCCLicenseSearchController { @Autowired protected CreativeCommonsService creativeCommonsService; + @Autowired + protected ConverterService converter; + protected RequestService requestService = new DSpace().getRequestService(); /** * Retrieves the CC License URI based on the license ID and answers in the field questions, provided as parameters * to this request * - * @return the CC License URI as a string + * @return the CC License URI as a SubmissionCCLicenseUrlResource */ @RequestMapping(method = RequestMethod.GET) - public PlainTextValueRest findByRightsByQuestions() { + public SubmissionCCLicenseUrlResource findByRightsByQuestions() { ServletRequest servletRequest = requestService.getCurrentRequest() .getServletRequest(); Map requestParameterMap = servletRequest @@ -85,7 +90,9 @@ public PlainTextValueRest findByRightsByQuestions() { if (StringUtils.isBlank(licenseUri)) { throw new ResourceNotFoundException("No CC License URI could be found for ID: " + licenseId); } - PlainTextValueRest plainTextValueRest = new PlainTextValueRest(licenseUri); - return plainTextValueRest; + + SubmissionCCLicenseUrlRest submissionCCLicenseUrlRest = converter.toRest(licenseUri, utils.obtainProjection()); + return converter.toResource(submissionCCLicenseUrlRest); + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java new file mode 100644 index 000000000000..5411ac306cd0 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java @@ -0,0 +1,41 @@ +/** + * 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 org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; +import org.dspace.app.rest.projection.Projection; +import org.springframework.stereotype.Component; + + +/** + * This converter is responsible for transforming a Submission CC License Url String to the REST + * representation SubmissionCCLicenseUrlRest and vice versa + */ +@Component +public class SubmissionCCLicenseUrlConverter implements DSpaceConverter { + + /** + * Convert a Submission CC License Url String to its REST representation + * @param modelObject - the CC License Url String to convert + * @param projection - the projection + * @return the corresponding SubmissionCCLicenseUrlRest object + */ + @Override + public SubmissionCCLicenseUrlRest convert(final String modelObject, final Projection projection) { + SubmissionCCLicenseUrlRest submissionCCLicenseUrlRest = new SubmissionCCLicenseUrlRest(); + submissionCCLicenseUrlRest.setUrl(modelObject); + + return submissionCCLicenseUrlRest; + } + + @Override + public Class getModelClass() { + return String.class; + } + +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java new file mode 100644 index 000000000000..39911faad038 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java @@ -0,0 +1,64 @@ +/** + * 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.link.process; + +import java.util.LinkedList; +import java.util.Map; + +import org.dspace.app.rest.SubmissionCCLicenseSearchController; +import org.dspace.app.rest.link.HalLinkFactory; +import org.dspace.app.rest.model.hateoas.SubmissionCCLicenseUrlResource; +import org.dspace.services.RequestService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.hateoas.Link; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * This class will provide the SubmissionCCLicenseUrlResource with links + */ +@Component +public class SubmissionCCLicenseUrlResourceHalLinkFactory + extends HalLinkFactory { + + @Autowired + RequestService requestService; + + /** + * Add a self link based on the search parameters + * @param halResource - The halResource + * @param pageable - The page information + * @param list - The list of present links + * @throws Exception + */ + protected void addLinks(SubmissionCCLicenseUrlResource halResource, final Pageable pageable, + LinkedList list) + throws Exception { + + halResource.removeLinks(); + Map parameterMap = requestService.getCurrentRequest().getHttpServletRequest() + .getParameterMap(); + + UriComponentsBuilder uriComponentsBuilder = uriBuilder(getMethodOn().findByRightsByQuestions()); + for (String key : parameterMap.keySet()) { + uriComponentsBuilder.queryParam(key, parameterMap.get(key)); + } + + list.add(buildLink("self", uriComponentsBuilder.build().toUriString())); + } + + + protected Class getControllerClass() { + return SubmissionCCLicenseSearchController.class; + } + + protected Class getResourceClass() { + return SubmissionCCLicenseUrlResource.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java index 611d532039cf..23589d5a4655 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseRest.java @@ -15,7 +15,7 @@ /** * This class is the REST representation of the CCLicense model object and acts as a data object - * * for the SubmissionCCLicenseResource class. + * for the SubmissionCCLicenseResource class. * Refer to {@link org.dspace.license.CCLicense} for explanation of the properties */ public class SubmissionCCLicenseRest extends BaseObjectRest { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java new file mode 100644 index 000000000000..14d430b228c9 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java @@ -0,0 +1,52 @@ +/** + * 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.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dspace.app.rest.SubmissionCCLicenseSearchController; + +/** + * This class is the REST representation of the CCLicense URL String object and acts as a data object + * for the SubmissionCCLicenseUrlRest class. + */ +public class SubmissionCCLicenseUrlRest extends BaseObjectRest { + public static final String NAME = "submissioncclicenseUrl"; + + private String url; + + @JsonIgnore + @Override + public String getId() { + return id; + } + + public String getUrl() { + return url; + } + + public void setUrl(final String url) { + this.url = url; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } + + public String getCategory() { + return SubmissionCCLicenseRest.CATEGORY; + } + + @Override + @JsonIgnore + public Class getController() { + return SubmissionCCLicenseSearchController.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseUrlResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseUrlResource.java new file mode 100644 index 000000000000..29ce7cf669f6 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SubmissionCCLicenseUrlResource.java @@ -0,0 +1,23 @@ +/** + * 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.SubmissionCCLicenseUrlRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * SubmissionCCLicenseUrl HAL Resource. This resource adds the data from the REST object together with embedded objects + * and a set of links if applicable + */ +@RelNameDSpaceResource(SubmissionCCLicenseUrlRest.NAME) +public class SubmissionCCLicenseUrlResource extends DSpaceResource { + public SubmissionCCLicenseUrlResource(SubmissionCCLicenseUrlRest submissionCCLicenseUrlRest, Utils utils) { + super(submissionCCLicenseUrlRest, utils); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java index 5c35bb5acf27..30734c8cac30 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java @@ -33,16 +33,23 @@ public void searchRightsByQuestionsTest() throws Exception { "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + "=license2-field0-enum1")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.value", is("mock-license-uri"))) - .andExpect(jsonPath("$.type", is("plaintextvalue"))); + .andExpect(jsonPath("$.url", is("mock-license-uri"))) + .andExpect(jsonPath("$.type", is("submissioncclicenseUrl"))) + .andExpect(jsonPath("$._links.self.href", + is("http://localhost/api/config/submissioncclicenses/search/rightsByQuestions" + + "?license=license2" + + "&answer_license2-field0=license2-field0-enum1"))); } @Test public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception { getClient().perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.value", is("mock-license-uri"))) - .andExpect(jsonPath("$.type", is("plaintextvalue"))); + .andExpect(jsonPath("$.url", is("mock-license-uri"))) + .andExpect(jsonPath("$.type", is("submissioncclicenseUrl"))) + .andExpect(jsonPath("$._links.self.href", + is("http://localhost/api/config/submissioncclicenses/search/rightsByQuestions" + + "?license=license3"))); } @Test From df01297539dd6dd0d8e81dacb96eb25b75c58a74 Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Wed, 27 May 2020 17:11:52 +0200 Subject: [PATCH 47/73] Submission CC license: Adding overrides & removing unused class --- ...ionCCLicenseUrlResourceHalLinkFactory.java | 3 ++ .../app/rest/model/PlainTextValueRest.java | 36 ------------------- .../model/SubmissionCCLicenseUrlRest.java | 1 + 3 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java index 39911faad038..cb44d68e73e9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java @@ -37,6 +37,7 @@ public class SubmissionCCLicenseUrlResourceHalLinkFactory * @param list - The list of present links * @throws Exception */ + @Override protected void addLinks(SubmissionCCLicenseUrlResource halResource, final Pageable pageable, LinkedList list) throws Exception { @@ -54,10 +55,12 @@ protected void addLinks(SubmissionCCLicenseUrlResource halResource, final Pageab } + @Override protected Class getControllerClass() { return SubmissionCCLicenseSearchController.class; } + @Override protected Class getResourceClass() { return SubmissionCCLicenseUrlResource.class; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java deleted file mode 100644 index 6f02aa928680..000000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PlainTextValueRest.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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; - -/** - * Rest object used to represent a plain text value - */ -public class PlainTextValueRest { - public static final String TYPE = "plaintextvalue"; - - private String value; - - public PlainTextValueRest() { - } - - public PlainTextValueRest(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public void setValue(final String value) { - this.value = value; - } - - public String getType() { - return TYPE; - } -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java index 14d430b228c9..5a54a8078f09 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java @@ -40,6 +40,7 @@ public String getType() { return NAME; } + @Override public String getCategory() { return SubmissionCCLicenseRest.CATEGORY; } From 614ad3d054e09046112caec89fd6bb68b3ffacee Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 29 May 2020 11:51:26 +0200 Subject: [PATCH 48/73] 71199: Fix PR issues --- .../SubmissionCCLicenseSearchController.java | 2 ++ .../SubmissionCCLicenseUrlConverter.java | 1 + .../model/SubmissionCCLicenseUrlRest.java | 4 +++ .../SubmissionCCLicenseRestRepository.java | 5 ++- ...censeUrlRestPermissionEvaluatorPlugin.java | 32 +++++++++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java index f47376757c21..17116884cfb5 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java @@ -23,6 +23,7 @@ import org.dspace.utils.DSpace; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @@ -33,6 +34,7 @@ @RestController @RequestMapping("/api/" + SubmissionCCLicenseRest.CATEGORY + "/" + SubmissionCCLicenseRest.PLURAL + "/search" + "/rightsByQuestions") +@PreAuthorize("permitAll()") public class SubmissionCCLicenseSearchController { @Autowired diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java index 5411ac306cd0..99945661c52e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java @@ -29,6 +29,7 @@ public class SubmissionCCLicenseUrlConverter implements DSpaceConverter findAll(final Context context, final Pageable pageable) { List allCCLicenses = creativeCommonsService.findAllCCLicenses(); - return converter.toRestPage(utils.getPage(allCCLicenses, pageable), utils.obtainProjection()); + return converter.toRestPage(allCCLicenses, pageable, utils.obtainProjection()); } @Override diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java new file mode 100644 index 000000000000..284333e0202f --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java @@ -0,0 +1,32 @@ +/** + * 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 java.io.Serializable; + +import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.model.DiscoveryResultsRest; +import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +/** + * This class will handle calls made to SubmissionCCLicenseUrlRest endpoints. + * It will return true because access can be granted anytime it's linked from another resource + */ +@Component +public class SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin extends RestObjectPermissionEvaluatorPlugin { + @Override + public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType, + DSpaceRestPermission restPermission) { + if (!StringUtils.equalsIgnoreCase(SubmissionCCLicenseUrlRest.NAME, targetType)) { + return false; + } + return true; + } +} From 72ac35025054b6f2ea4da4ca15c7e877ba4506c7 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 29 May 2020 13:19:20 +0200 Subject: [PATCH 49/73] Remove unused import --- .../SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java index 284333e0202f..2fd8c7647ff9 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/SubmissionCCLicenseUrlRestPermissionEvaluatorPlugin.java @@ -10,7 +10,6 @@ import java.io.Serializable; import org.apache.commons.lang3.StringUtils; -import org.dspace.app.rest.model.DiscoveryResultsRest; import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; From 4ea4f9112948d28858fb9b8f727da75fe4312032 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Wed, 3 Jun 2020 14:42:40 +0200 Subject: [PATCH 50/73] [Task 71213] applied feedback to the new registration creation functionality --- .../org/dspace/app/util/AuthorizeUtil.java | 6 +- .../dspace/eperson/AccountServiceImpl.java | 3 - .../app/rest/RegistrationRestController.java | 114 ------------------ .../app/rest/model/RegistrationRest.java | 4 +- .../repository/EPersonRestRepository.java | 35 ++++-- .../RegistrationRestRepository.java | 67 ++++++++++ .../EPersonPasswordReplaceOperation.java | 14 +-- .../app/rest/EPersonRestRepositoryIT.java | 63 +++++++--- 8 files changed, 150 insertions(+), 156 deletions(-) delete mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index 521eff6fc1ed..56891f961b49 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -34,6 +34,7 @@ import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.service.GroupService; import org.dspace.services.factory.DSpaceServicesFactory; +import org.dspace.utils.DSpace; import org.dspace.xmlworkflow.factory.XmlWorkflowServiceFactory; import org.dspace.xmlworkflow.storedcomponents.CollectionRole; import org.dspace.xmlworkflow.storedcomponents.service.CollectionRoleService; @@ -637,7 +638,10 @@ public static boolean authorizeUpdatePassword(Context context, String email) { try { EPerson eperson = EPersonServiceFactory.getInstance().getEPersonService().findByEmail(context, email); if (eperson != null && eperson.canLogIn()) { - return true; + HttpServletRequest request = new DSpace().getRequestService().getCurrentRequest() + .getHttpServletRequest(); + return AuthenticateServiceFactory.getInstance().getAuthenticationService() + .allowSetPassword(context, request, null); } } catch (SQLException e) { log.error("Something went wrong trying to retrieve EPerson for email: " + email, e); diff --git a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java index 1aeaeaef8e34..40da31a0f9e9 100644 --- a/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/eperson/AccountServiceImpl.java @@ -164,9 +164,6 @@ public void deleteToken(Context context, String token) @Override public boolean verifyPasswordStructure(String password) { - if (StringUtils.isBlank(password)) { - return false; - } if (StringUtils.length(password) < 6) { return false; } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java deleted file mode 100644 index c79c2a0969a6..000000000000 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/RegistrationRestController.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * 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; - -import java.io.IOException; -import java.sql.SQLException; -import javax.mail.MessagingException; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.lang3.StringUtils; -import org.dspace.app.rest.authorization.AuthorizationFeatureService; -import org.dspace.app.rest.converter.ConverterService; -import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.exception.UnprocessableEntityException; -import org.dspace.app.rest.model.RegistrationRest; -import org.dspace.app.rest.utils.ContextUtil; -import org.dspace.app.util.AuthorizeUtil; -import org.dspace.authorize.AuthorizeException; -import org.dspace.content.service.SiteService; -import org.dspace.core.Context; -import org.dspace.eperson.EPerson; -import org.dspace.eperson.service.AccountService; -import org.dspace.eperson.service.EPersonService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.rest.webmvc.ControllerUtils; -import org.springframework.hateoas.RepresentationModel; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -/** - * This will be the Controller class that handles calls to the /api/eperson/registrations endpoints - */ -@RestController -@RequestMapping("/api/" + RegistrationRest.CATEGORY + "/" + RegistrationRest.NAME_PLURAL) -public class RegistrationRestController { - - @Autowired - private AuthorizationFeatureService authorizationFeatureService; - - @Autowired - private SiteService siteService; - - @Autowired - private ConverterService converterService; - - @Autowired - private AccountService accountService; - - @Autowired - private EPersonService ePersonService; - - /** - * This method will be used to either register a new user or to send forgotten password info in a mail. - * It can be called by doing a POST request to the /api/eperson/registrations endpoint. - * It'll create a RegistrationRest object from the inputstream in the request and it'll check whether the email - * defined in that object is in the DB or not. - * If it is in the db then we'll send the forgotten password info, if it wasn't in the database then we'll send - * registration info. - * - * @param request The current request - * @param response The current response - * @return An empty response containing a 201 status code - * @throws SQLException If something goes wrong - * @throws IOException If something goes wrong - * @throws MessagingException If something goes wrong - * @throws AuthorizeException If something goes wrong - */ - @RequestMapping(method = RequestMethod.POST) - public ResponseEntity> register(HttpServletRequest request, HttpServletResponse response) - throws SQLException, IOException, MessagingException, AuthorizeException { - - Context context = ContextUtil.obtainContext(request); - ObjectMapper mapper = new ObjectMapper(); - RegistrationRest registrationRest; - try { - ServletInputStream input = request.getInputStream(); - registrationRest = mapper.readValue(input, RegistrationRest.class); - } catch (IOException e1) { - throw new UnprocessableEntityException("Error parsing request body.", e1); - } - if (StringUtils.isBlank(registrationRest.getEmail())) { - throw new UnprocessableEntityException("The email cannot be omitted from the Registration endpoint"); - } - EPerson eperson = ePersonService.findByEmail(context, registrationRest.getEmail()); - if (eperson != null) { - if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail())) { - throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + - eperson.getEmail()); - } - accountService.sendForgotPasswordInfo(context, registrationRest.getEmail()); - } else { - if (!AuthorizeUtil.authorizeNewAccountRegistration(context, request)) { - throw new AccessDeniedException( - "Registration is disabled, you are not authorized to create a new Authorization"); - } - accountService.sendRegistrationInfo(context, registrationRest.getEmail()); - } - context.complete(); - return ControllerUtils.toEmptyResponse(HttpStatus.CREATED); - } - -} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java index 69ed1d5653b4..e8397f8ca763 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/RegistrationRest.java @@ -10,7 +10,7 @@ import java.util.UUID; import com.fasterxml.jackson.annotation.JsonProperty; -import org.dspace.app.rest.RegistrationRestController; +import org.dspace.app.rest.RestResourceController; /** @@ -66,7 +66,7 @@ public String getCategory() { @Override public Class getController() { - return RegistrationRestController.class; + return RestResourceController.class; } @Override diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index 4226ff6ab4c1..6d5e51f9615c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -96,8 +96,8 @@ protected EPersonRest createAndReturn(Context context) try { return createAndReturn(context, epersonRest, token); } catch (SQLException e) { - log.error(e.getMessage(), e); - throw new RuntimeException("Something with wrong in the creation of an EPerson with token: " + token); + log.error("Something went wrong in the creation of an EPerson with token: " + token, e); + throw new RuntimeException("Something went wrong in the creation of an EPerson with token: " + token); } } // If no token is present, we simply do the admin execution @@ -127,8 +127,28 @@ private EPerson createEPersonFromRestObject(Context context, EPersonRest eperson return eperson; } + /** + * This method will perform checks on whether or not the given Request was valid for the creation of an EPerson + * with a token or not. + * It'll check that the token exists, that the token doesn't yet resolve to an actual eperson already, + * that the email in the given json is equal to the email for the token and that other properties are set to + * what we expect in this creation. + * It'll check if all of those constraints hold true and if we're allowed to register new accounts. + * If this is the case, we'll create an EPerson without any authorization checks and delete the token + * @param context The DSpace context + * @param epersonRest The EPersonRest given to be created + * @param token The token to be used + * @return The EPersonRest after the creation of the EPerson object + * @throws AuthorizeException If something goes wrong + * @throws SQLException If something goes wrong + */ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, String token) throws AuthorizeException, SQLException { + if (!AuthorizeUtil.authorizeNewAccountRegistration(context, requestService + .getCurrentRequest().getHttpServletRequest())) { + throw new DSpaceBadRequestException( + "Registration is disabled, you are not authorized to create a new Authorization"); + } RegistrationData registrationData = registrationDataService.findByToken(context, token); if (registrationData == null) { throw new DSpaceBadRequestException("The token given as parameter: " + token + " does not exist" + @@ -152,11 +172,6 @@ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, St + " with a token"); } checkRequiredProperties(epersonRest); - if (!AuthorizeUtil.authorizeNewAccountRegistration(context, requestService - .getCurrentRequest().getHttpServletRequest())) { - throw new DSpaceBadRequestException( - "Registration is disabled, you are not authorized to create a new Authorization"); - } // We'll turn off authorisation system because this call isn't admin based as it's token based context.turnOffAuthorisationSystem(); EPerson ePerson = createEPersonFromRestObject(context, epersonRest); @@ -179,7 +194,7 @@ private void checkRequiredProperties(EPersonRest epersonRest) { } String password = epersonRest.getPassword(); if (!accountService.verifyPasswordStructure(password)) { - throw new DSpaceBadRequestException("the password cannot be left blank"); + throw new DSpaceBadRequestException("The given password is invalid"); } } @@ -272,8 +287,8 @@ protected void patch(Context context, HttpServletRequest request, String apiCate } } if (!passwordChangeFound) { - throw new AccessDeniedException("Couldn't perform the patch as a token with provided without " + - "a password change"); + throw new AccessDeniedException("Refused to perform the EPerson patch based on a token without " + + "changing the password"); } } patchDSpaceObject(apiCategory, model, uuid, patch); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java index 2578b8eebfaf..ba7583f1c5a8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RegistrationRestRepository.java @@ -7,22 +7,35 @@ */ package org.dspace.app.rest.repository; +import java.io.IOException; import java.sql.SQLException; +import javax.mail.MessagingException; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; import org.dspace.app.rest.Parameter; import org.dspace.app.rest.SearchRestMethod; +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.RegistrationRest; +import org.dspace.app.util.AuthorizeUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.RegistrationData; import org.dspace.eperson.service.AccountService; +import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.RegistrationDataService; +import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Component; /** @@ -31,9 +44,17 @@ @Component(RegistrationRest.CATEGORY + "." + RegistrationRest.NAME) public class RegistrationRestRepository extends DSpaceRestRepository { + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(RegistrationRestRepository.class); + + @Autowired + private EPersonService ePersonService; + @Autowired private AccountService accountService; + @Autowired + private RequestService requestService; + @Autowired private RegistrationDataService registrationDataService; @@ -47,6 +68,52 @@ public Page findAll(Context context, Pageable pageable) { throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", ""); } + @Override + public RegistrationRest createAndReturn(Context context) { + HttpServletRequest request = requestService.getCurrentRequest().getHttpServletRequest(); + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest; + try { + ServletInputStream input = request.getInputStream(); + registrationRest = mapper.readValue(input, RegistrationRest.class); + } catch (IOException e1) { + throw new UnprocessableEntityException("Error parsing request body.", e1); + } + if (StringUtils.isBlank(registrationRest.getEmail())) { + throw new UnprocessableEntityException("The email cannot be omitted from the Registration endpoint"); + } + EPerson eperson = null; + try { + eperson = ePersonService.findByEmail(context, registrationRest.getEmail()); + } catch (SQLException e) { + log.error("Something went wrong retrieving EPerson for email: " + registrationRest.getEmail(), e); + } + if (eperson != null) { + try { + if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail())) { + throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + + eperson.getEmail()); + } + accountService.sendForgotPasswordInfo(context, registrationRest.getEmail()); + } catch (SQLException | IOException | MessagingException | AuthorizeException e) { + log.error("Something went wrong with sending forgot password info for email: " + + registrationRest.getEmail(), e); + } + } else { + try { + if (!AuthorizeUtil.authorizeNewAccountRegistration(context, request)) { + throw new AccessDeniedException( + "Registration is disabled, you are not authorized to create a new Authorization"); + } + accountService.sendRegistrationInfo(context, registrationRest.getEmail()); + } catch (SQLException | IOException | MessagingException | AuthorizeException e) { + log.error("Something with wrong with sending registration info for email: " + + registrationRest.getEmail()); + } + } + return null; + } + @Override public Class getDomainClass() { return RegistrationRest.class; diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java index ae02200d98b7..1a3201abddd2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java @@ -57,15 +57,15 @@ public R perform(Context context, R object, Operation operation) { checkOperationValue(operation.getValue()); if (supports(object, operation)) { EPerson eperson = (EPerson) object; - String token = requestService.getCurrentRequest().getHttpServletRequest().getParameter("token"); - checkModelForExistingValue(eperson); - if (StringUtils.isNotBlank(token)) { - patchWithToken(context,eperson, token, operation); - } if (!AuthorizeUtil.authorizeUpdatePassword(context, eperson.getEmail())) { throw new DSpaceBadRequestException("Password cannot be updated for the given EPerson with email: " + eperson.getEmail()); } + String token = requestService.getCurrentRequest().getHttpServletRequest().getParameter("token"); + checkModelForExistingValue(eperson); + if (StringUtils.isNotBlank(token)) { + verifyAndDeleteToken(context, eperson, token, operation); + } ePersonService.setPassword(eperson, (String) operation.getValue()); return object; } else { @@ -73,7 +73,7 @@ public R perform(Context context, R object, Operation operation) { } } - private void patchWithToken(Context context, EPerson eperson, String token, Operation operation) { + private void verifyAndDeleteToken(Context context, EPerson eperson, String token, Operation operation) { try { EPerson ePersonFromToken = accountService.getEPerson(context, token); if (ePersonFromToken == null) { @@ -86,7 +86,7 @@ private void patchWithToken(Context context, EPerson eperson, String token, Oper } accountService.deleteToken(context, token); } catch (SQLException | AuthorizeException e) { - log.error(e.getMessage(), e); + log.error("Failed to verify or delete the token for an EPerson patch", e); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 21b886ec4561..ba81312f3e7b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -15,7 +15,9 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -156,11 +158,16 @@ public void createAnonAccessDeniedTest() throws Exception { dataFull.setCanLogIn(true); dataFull.setMetadata(metadataRest); + context.restoreAuthSystemState(); + getClient().perform(post("/api/eperson/epersons") .content(mapper.writeValueAsBytes(data)) .contentType(contentType) .param("projection", "full")) .andExpect(status().isUnauthorized()); + getClient().perform(get("/api/eperson/epersons/search/byEmail") + .param("email", data.getEmail())) + .andExpect(status().isNoContent()); } @Test @@ -1857,12 +1864,10 @@ public void patchReplacePasswordWithToken() throws Exception { .andExpect(status().isOk()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); - assertFalse(oldPassword.equals(newPasswordHash)); + assertNotEquals(oldPassword, newPasswordHash); assertTrue(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, tokenForEPerson); - context.restoreAuthSystemState(); + assertNull(registrationDataService.findByToken(context, tokenForEPerson)); } @@ -1886,7 +1891,7 @@ public void patchReplacePasswordWithRandomTokenPatchFail() throws Exception { String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); - String token = getAuthToken(admin.getEmail(), password); + String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) @@ -1896,10 +1901,9 @@ public void patchReplacePasswordWithRandomTokenPatchFail() throws Exception { .andExpect(status().isForbidden()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); - assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); - assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); - assertTrue(StringUtils.equals(registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(), - tokenForEPerson)); + assertEquals(oldPassword.getHashString(),newPasswordHash.getHashString()); + assertNotNull(registrationDataService.findByEmail(context, ePerson.getEmail())); + assertEquals(registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(), tokenForEPerson); context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, tokenForEPerson); @@ -1936,7 +1940,7 @@ public void patchReplacePasswordWithOtherUserTokenFail() throws Exception { String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); String tokenForEPersonTwo = registrationDataService.findByEmail(context, ePersonTwo.getEmail()).getToken(); - String token = getAuthToken(admin.getEmail(), password); + String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) @@ -1946,8 +1950,8 @@ public void patchReplacePasswordWithOtherUserTokenFail() throws Exception { .andExpect(status().isForbidden()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); - assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); - assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + assertEquals(oldPassword.getHashString(),newPasswordHash.getHashString()); + assertNotNull(registrationDataService.findByEmail(context, ePerson.getEmail())); context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, tokenForEPerson); @@ -1959,7 +1963,7 @@ public void patchReplacePasswordWithOtherUserTokenFail() throws Exception { public void patchReplaceEmailWithTokenFail() throws Exception { context.turnOffAuthorisationSystem(); - String originalEmail = "Johndoe@fake-email.com"; + String originalEmail = "johndoe@fake-email.com"; EPerson ePerson = EPersonBuilder.createEPerson(context) .withNameInMetadata("John", "Doe") .withEmail(originalEmail) @@ -1976,7 +1980,7 @@ public void patchReplaceEmailWithTokenFail() throws Exception { String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); - String token = getAuthToken(admin.getEmail(), password); + String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) @@ -1986,9 +1990,9 @@ public void patchReplaceEmailWithTokenFail() throws Exception { .andExpect(status().isForbidden()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); - assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); - assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); - assertTrue(StringUtils.equalsIgnoreCase(ePerson.getEmail(), originalEmail)); + assertEquals(oldPassword.getHashString(),newPasswordHash.getHashString()); + assertNotNull(registrationDataService.findByEmail(context, ePerson.getEmail())); + assertEquals(ePerson.getEmail(), originalEmail); context.turnOffAuthorisationSystem(); registrationDataService.delete(context, registrationDataService.findByEmail(context, ePerson.getEmail())); @@ -2026,7 +2030,7 @@ public void registerNewAccountPatchUpdatePasswordRandomUserUuidFail() throws Exc String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - String token = getAuthToken(admin.getEmail(), password); + String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) @@ -2060,6 +2064,8 @@ public void postEPersonWithTokenWithoutEmailProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"password\":\"somePassword\"," + "\"type\":\"eperson\"}"; @@ -2110,6 +2116,8 @@ public void postEPersonWithTokenWithEmailProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2162,6 +2170,8 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2223,7 +2233,8 @@ public void postEPersonWithTokenWithTwoTokensDifferentEmailProperty() throws Exc .andExpect(status().isCreated()); String newRegisterTokenTwo = registrationDataService.findByEmail(context, newRegisterEmailTwo).getToken(); - + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmailTwo + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2259,6 +2270,8 @@ public void postEPersonWithRandomTokenWithEmailProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2292,6 +2305,8 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredFalseProperty() throws .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":false,\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2325,6 +2340,8 @@ public void postEPersonWithTokenWithoutLastNameProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]},\"selfRegistered\":true," + "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2357,6 +2374,8 @@ public void postEPersonWithTokenWithoutFirstNameProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true," + "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; @@ -2389,6 +2408,8 @@ public void postEPersonWithTokenWithoutPasswordProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]}," + "\"type\":\"eperson\"}"; @@ -2423,6 +2444,8 @@ public void postEPersonWithWrongToken() throws Exception { .andExpect(status().isCreated()); String forgotPasswordToken = registrationDataService.findByEmail(context, eperson.getEmail()).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"password\":\"somePassword\"," + "\"type\":\"eperson\"}"; @@ -2457,6 +2480,8 @@ public void postEPersonWithTokenWithEmailPropertyAnonUser() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); + // We need to create this json manually to support actually setting the password to a value. + // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; From a338526583e21d10058e5a75edda81f1c4681196 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Wed, 3 Jun 2020 15:39:42 +0200 Subject: [PATCH 51/73] Implement community feedbacks --- .../rest/utils/BitstreamMetadataValuePathUtils.java | 1 + .../dspace/app/rest/WorkspaceItemRestRepositoryIT.java | 10 ++++++++-- .../dspace/app/rest/builder/WorkspaceItemBuilder.java | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java index 4ff42d70cd72..dda661e4910f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/BitstreamMetadataValuePathUtils.java @@ -38,6 +38,7 @@ public class BitstreamMetadataValuePathUtils { public void validate(String absolutePath) throws DCInputsReaderException { String[] split = absolutePath.split("/"); DCInputSet inputConfig = inputReader.getInputsByFormName(UploadStep.UPLOAD_STEP_METADATA_SECTION); + // according to the rest contract the absolute path must be something like files/:idx/metadata/dc.title if (split.length >= 4) { if (!inputConfig.isFieldPresent(split[3])) { throw new UnprocessableEntityException("The field " + split[3] + " is not present in section " diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index 4e848eb256f6..e18f06c1e4d6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -3685,16 +3685,22 @@ public void patchDeleteSectionTest() throws Exception { .withIssueDate("2020-01-21") .withSubject("Subject 1") .withSubject("Subject 2") + .withAbstract("Test description abstract") .build(); context.restoreAuthSystemState(); + String authToken = getAuthToken(eperson.getEmail(), password); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").isNotEmpty()) + .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.description.abstract']").isNotEmpty()); + List operations = new ArrayList(); operations.add(new RemoveOperation("/sections/traditionalpagetwo")); String patchBody = getPatchContent(operations); - String authToken = getAuthToken(eperson.getEmail(), password); - getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/WorkspaceItemBuilder.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/WorkspaceItemBuilder.java index 57166677032e..0c328a79e961 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/WorkspaceItemBuilder.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/builder/WorkspaceItemBuilder.java @@ -163,6 +163,9 @@ public WorkspaceItemBuilder withSubject(final String subject) { return addMetadataValue(MetadataSchemaEnum.DC.getName(), "subject", null, subject); } + public WorkspaceItemBuilder withAbstract(final String subject) { + return addMetadataValue(MetadataSchemaEnum.DC.getName(),"description", "abstract", subject); + } public WorkspaceItemBuilder grantLicense() { Item item = workspaceItem.getItem(); String license; From 099caaf86f2c0f7eb3d6f9402a75021549fb5f82 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Thu, 4 Jun 2020 16:38:37 +0200 Subject: [PATCH 52/73] 71266: Community feedback #1 --- .../SubmissionCCLicenseSearchController.java | 2 +- .../SubmissionCCLicenseRestRepository.java | 4 +- .../rest/CCLicenseRemovePatchOperationIT.java | 42 +++++++++++++++++-- .../SubmissionCCLicenseRestRepositoryIT.java | 25 +++++++++-- ...SubmissionCCLicenseSearchControllerIT.java | 31 +++++++++++--- 5 files changed, 90 insertions(+), 14 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java index 17116884cfb5..76ed1021d338 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java @@ -34,7 +34,7 @@ @RestController @RequestMapping("/api/" + SubmissionCCLicenseRest.CATEGORY + "/" + SubmissionCCLicenseRest.PLURAL + "/search" + "/rightsByQuestions") -@PreAuthorize("permitAll()") +@PreAuthorize("hasAuthority('AUTHENTICATED')") public class SubmissionCCLicenseSearchController { @Autowired diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java index 6d3cf03237c1..0dab42f9bd04 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionCCLicenseRestRepository.java @@ -30,7 +30,7 @@ public class SubmissionCCLicenseRestRepository extends DSpaceRestRepository findAll(final Context context, final Pageable pageable) { List allCCLicenses = creativeCommonsService.findAllCCLicenses(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java index afee0aa88250..40828a766777 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java @@ -59,7 +59,7 @@ public void patchRemoveSubmissionCCLicense() throws Exception { context.restoreAuthSystemState(); - String adminToken = getAuthToken(admin.getEmail(), password); + String epersonToken = getAuthToken(eperson.getEmail(), password); // First add a license and verify it is added List ops = new ArrayList(); @@ -70,7 +70,7 @@ public void patchRemoveSubmissionCCLicense() throws Exception { String patchBody = getPatchContent(ops); - getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + getClient(epersonToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isOk()) @@ -91,10 +91,46 @@ public void patchRemoveSubmissionCCLicense() throws Exception { String removePatch = getPatchContent(removeOps); - getClient(adminToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + getClient(epersonToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) .content(removePatch) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense")))); } + + + @Test + public void patchRemoveSubmissionCCLicenseNonExisting() throws Exception { + context.turnOffAuthorisationSystem(); + + Community community = CommunityBuilder.createCommunity(context) + .withName("Community") + .build(); + + Collection collection = CollectionBuilder.createCollection(context, community) + .withName("Collection") + .build(); + + WorkspaceItem workspaceItem = WorkspaceItemBuilder.createWorkspaceItem(context, collection) + .withTitle("Workspace Item") + .build(); + + context.restoreAuthSystemState(); + + String epersonToken = getAuthToken(eperson.getEmail(), password); + + + List removeOps = new ArrayList(); + RemoveOperation removeOperation = new RemoveOperation("/sections/cclicense/uri"); + + removeOps.add(removeOperation); + String removePatch = getPatchContent(removeOps); + + + getClient(epersonToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) + .content(removePatch) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense")))); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java index 5fa22470fef3..9267cef6c51f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseRestRepositoryIT.java @@ -34,8 +34,9 @@ public class SubmissionCCLicenseRestRepositoryIT extends AbstractControllerInteg */ @Test public void findAllTest() throws Exception { + String epersonToken = getAuthToken(eperson.getEmail(), password); - getClient().perform(get("/api/config/submissioncclicenses")) + getClient(epersonToken).perform(get("/api/config/submissioncclicenses")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.submissioncclicenses", Matchers.containsInAnyOrder( @@ -47,7 +48,9 @@ public void findAllTest() throws Exception { @Test public void findOneTest() throws Exception { - getClient().perform(get("/api/config/submissioncclicenses/license1")) + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken).perform(get("/api/config/submissioncclicenses/license1")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$", Matchers.is( @@ -57,7 +60,23 @@ public void findOneTest() throws Exception { @Test public void findOneTestNonExistingLicense() throws Exception { - getClient().perform(get("/api/config/submissioncclicenses/non-existing-license")) + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken).perform(get("/api/config/submissioncclicenses/non-existing-license")) .andExpect(status().isNotFound()); } + + @Test + public void findAllTestUnAuthorized() throws Exception { + getClient().perform(get("/api/config/submissioncclicenses")) + .andExpect(status().isUnauthorized()); + } + + @Test + public void findOneTestUnAuthorized() throws Exception { + + getClient().perform(get("/api/config/submissioncclicenses/license1")) + .andExpect(status().isUnauthorized()); + } + } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java index 30734c8cac30..8a93e18a8e58 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java @@ -29,7 +29,9 @@ public class SubmissionCCLicenseSearchControllerIT extends AbstractControllerInt @Test public void searchRightsByQuestionsTest() throws Exception { - getClient().perform(get( + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken).perform(get( "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + "=license2-field0-enum1")) .andExpect(status().isOk()) @@ -43,7 +45,10 @@ public void searchRightsByQuestionsTest() throws Exception { @Test public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception { - getClient().perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3")) + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken) + .perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3")) .andExpect(status().isOk()) .andExpect(jsonPath("$.url", is("mock-license-uri"))) .andExpect(jsonPath("$.type", is("submissioncclicenseUrl"))) @@ -54,7 +59,9 @@ public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception { @Test public void searchRightsByQuestionsNonExistingLicense() throws Exception { - getClient().perform(get( + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken).perform(get( "/api/config/submissioncclicenses/search/rightsByQuestions?license=nonexisting-license" + "&answer_license2-field0=license2-field0-enum1")) .andExpect(status().isNotFound()); @@ -62,7 +69,9 @@ public void searchRightsByQuestionsNonExistingLicense() throws Exception { @Test public void searchRightsByQuestionsMissingRequiredAnswer() throws Exception { - getClient().perform(get( + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken).perform(get( "/api/config/submissioncclicenses/search/rightsByQuestions?license=license1&answer_license1field0" + "=license1field0enum1")) .andExpect(status().isBadRequest()); @@ -70,9 +79,21 @@ public void searchRightsByQuestionsMissingRequiredAnswer() throws Exception { @Test public void searchRightsByQuestionsAdditionalNonExistingAnswer() throws Exception { - getClient().perform(get( + String epersonToken = getAuthToken(eperson.getEmail(), password); + + getClient(epersonToken).perform(get( "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2" + "&answer_license2field0=license2field0enum1&answer_nonexisting=test")) .andExpect(status().isBadRequest()); } + + @Test + public void searchRightsByQuestionsAdditionalUnAuthorized() throws Exception { + + getClient().perform(get( + "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + + "=license2-field0-enum1")) + .andExpect(status().isUnauthorized()); + + } } From fdaddd934fe140c60a3db77a1c79a363b369ecb6 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Mon, 8 Jun 2020 16:24:19 +0200 Subject: [PATCH 53/73] [Task 71271] applied fixes after merge and applied community feedback to the new registration endpoints --- .../org/dspace/app/util/AuthorizeUtil.java | 4 + .../repository/EPersonRestRepository.java | 5 +- .../EPersonRestPermissionEvaluatorPlugin.java | 27 +- .../app/rest/EPersonRestRepositoryIT.java | 249 +++++++++--------- .../app/rest/ProcessRestRepositoryIT.java | 7 + .../rest/RegistrationRestControllerIT.java | 18 +- 6 files changed, 172 insertions(+), 138 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java index 56891f961b49..70a0d7e2e9e0 100644 --- a/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java +++ b/dspace-api/src/main/java/org/dspace/app/util/AuthorizeUtil.java @@ -621,6 +621,10 @@ public static boolean authorizeNewAccountRegistration(Context context, HttpServl throws SQLException { if (DSpaceServicesFactory.getInstance().getConfigurationService() .getBooleanProperty("user.registration", true)) { + // This allowSetPassword is currently the only mthod that would return true only when it's + // actually expected to be returning true. + // For example the LDAP canSelfRegister will return true due to auto-register, while that + // does not imply a new user can register explicitly return AuthenticateServiceFactory.getInstance().getAuthenticationService() .allowSetPassword(context, request, null); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index 5486c54fdb26..ed878374b75d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -42,8 +42,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.security.access.AccessDeniedException; import org.springframework.hateoas.Link; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; @@ -186,6 +186,9 @@ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, St context.restoreAuthSystemState(); // Restoring authorisation state right after the creation call accountService.deleteToken(context, token); + if (context.getCurrentUser() == null) { + context.setCurrentUser(ePerson); + } return converter.toRest(ePerson, utils.obtainProjection()); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java index ca13277b04d6..92b0e433fb2e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java @@ -8,7 +8,6 @@ package org.dspace.app.rest.security; import java.io.Serializable; -import java.sql.SQLException; import java.util.List; import java.util.UUID; @@ -21,7 +20,6 @@ import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; -import org.dspace.eperson.service.EPersonService; import org.dspace.services.RequestService; import org.dspace.services.model.Request; import org.slf4j.Logger; @@ -45,9 +43,6 @@ public class EPersonRestPermissionEvaluatorPlugin extends RestObjectPermissionEv @Autowired private RequestService requestService; - @Autowired - private EPersonService ePersonService; - @Override public boolean hasDSpacePermission(Authentication authentication, Serializable targetId, String targetType, DSpaceRestPermission permission) { @@ -67,23 +62,19 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t EPerson ePerson = null; - try { - ePerson = ePersonService.findByEmail(context, (String) authentication.getPrincipal()); - UUID dsoId = UUID.fromString(targetId.toString()); - - // anonymous user - if (ePerson == null) { - return false; - } + ePerson = context.getCurrentUser(); + UUID dsoId = UUID.fromString(targetId.toString()); - if (dsoId.equals(ePerson.getID())) { - return true; - } + // anonymous user + if (ePerson == null) { + return false; + } - } catch (SQLException e) { - log.error(e.getMessage(), e); + if (dsoId.equals(ePerson.getID())) { + return true; } + return false; } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index 617c80fc7dff..fbd0f071ecca 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -32,7 +32,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import javax.ws.rs.core.MediaType; @@ -68,7 +67,6 @@ import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.web.servlet.MvcResult; public class EPersonRestRepositoryIT extends AbstractControllerIntegrationTest { @@ -2083,36 +2081,41 @@ public void postEPersonWithTokenWithoutEmailProperty() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"password\":\"somePassword\"," + "\"type\":\"eperson\"}"; - MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(json) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.uuid", not(empty())), - // is it what you expect? EPerson.getName() returns the email... - //hasJsonPath("$.name", is("Doe John")), - hasJsonPath("$.type", is("eperson")), - hasJsonPath("$._links.self.href", not(empty())), - hasJsonPath("$.metadata", Matchers.allOf( - matchMetadata("eperson.firstname", "John"), - matchMetadata("eperson.lastname", "Doe") - ))))).andReturn(); - - String content = mvcResult.getResponse().getContentAsString(); - Map map = mapper.readValue(content, Map.class); - String epersonUuid = String.valueOf(map.get("uuid")); - EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); - assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); - - assertNull(registrationDataService.findByToken(context, newRegisterToken)); - context.turnOffAuthorisationSystem(); - ePersonService.delete(context, createdEPerson); - context.restoreAuthSystemState(); + AtomicReference idRef = new AtomicReference(); - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))) + .andDo(result -> idRef + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); + + + + String epersonUuid = String.valueOf(idRef.get()); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } finally { + EPersonBuilder.deleteEPerson(idRef.get()); + } } @Test @@ -2135,37 +2138,38 @@ public void postEPersonWithTokenWithEmailProperty() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(json) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.uuid", not(empty())), - // is it what you expect? EPerson.getName() returns the email... - //hasJsonPath("$.name", is("Doe John")), - hasJsonPath("$.email", is(newRegisterEmail)), - hasJsonPath("$.type", is("eperson")), - hasJsonPath("$._links.self.href", not(empty())), - hasJsonPath("$.metadata", Matchers.allOf( - matchMetadata("eperson.firstname", "John"), - matchMetadata("eperson.lastname", "Doe") - ))))).andReturn(); - - String content = mvcResult.getResponse().getContentAsString(); - Map map = mapper.readValue(content, Map.class); - String epersonUuid = String.valueOf(map.get("uuid")); - EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); - assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); - assertNull(registrationDataService.findByToken(context, newRegisterToken)); - - context.turnOffAuthorisationSystem(); - ePersonService.delete(context, createdEPerson); - context.restoreAuthSystemState(); + AtomicReference idRef = new AtomicReference(); - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.email", is(newRegisterEmail)), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andDo(result -> idRef + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); + + String epersonUuid = String.valueOf(idRef.get()); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } finally { + EPersonBuilder.deleteEPerson(idRef.get()); + } } @@ -2189,37 +2193,42 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(json) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.uuid", not(empty())), - // is it what you expect? EPerson.getName() returns the email... - //hasJsonPath("$.name", is("Doe John")), - hasJsonPath("$.email", is(newRegisterEmail)), - hasJsonPath("$.type", is("eperson")), - hasJsonPath("$._links.self.href", not(empty())), - hasJsonPath("$.metadata", Matchers.allOf( - matchMetadata("eperson.firstname", "John"), - matchMetadata("eperson.lastname", "Doe") - ))))).andReturn(); - - String content = mvcResult.getResponse().getContentAsString(); - Map map = mapper.readValue(content, Map.class); - String epersonUuid = String.valueOf(map.get("uuid")); - EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); - assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); - assertNull(registrationDataService.findByToken(context, newRegisterToken)); - - context.turnOffAuthorisationSystem(); - ePersonService.delete(context, createdEPerson); - context.restoreAuthSystemState(); + AtomicReference idRef = new AtomicReference(); - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.email", is(newRegisterEmail)), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andDo(result -> idRef + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); + + + String epersonUuid = String.valueOf(idRef.get()); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + context.restoreAuthSystemState(); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } finally { + EPersonBuilder.deleteEPerson(idRef.get()); + } } @@ -2499,34 +2508,38 @@ public void postEPersonWithTokenWithEmailPropertyAnonUser() throws Exception { "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - MvcResult mvcResult = getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(json) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isCreated()) - .andExpect(jsonPath("$", Matchers.allOf( - hasJsonPath("$.uuid", not(empty())), - // is it what you expect? EPerson.getName() returns the email... - //hasJsonPath("$.name", is("Doe John")), - hasJsonPath("$.email", is(newRegisterEmail)), - hasJsonPath("$.type", is("eperson")), - hasJsonPath("$._links.self.href", not(empty())), - hasJsonPath("$.metadata", Matchers.allOf( - matchMetadata("eperson.firstname", "John"), - matchMetadata("eperson.lastname", "Doe") - ))))).andReturn(); - - String content = mvcResult.getResponse().getContentAsString(); - Map map = mapper.readValue(content, Map.class); - String epersonUuid = String.valueOf(map.get("uuid")); - EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); - assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); - assertNull(registrationDataService.findByToken(context, newRegisterToken)); + AtomicReference idRef = new AtomicReference(); - context.turnOffAuthorisationSystem(); - ePersonService.delete(context, createdEPerson); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(json) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", Matchers.allOf( + hasJsonPath("$.uuid", not(empty())), + // is it what you expect? EPerson.getName() returns the email... + //hasJsonPath("$.name", is("Doe John")), + hasJsonPath("$.email", is(newRegisterEmail)), + hasJsonPath("$.type", is("eperson")), + hasJsonPath("$._links.self.href", not(empty())), + hasJsonPath("$.metadata", Matchers.allOf( + matchMetadata("eperson.firstname", "John"), + matchMetadata("eperson.lastname", "Doe") + ))))).andDo(result -> idRef + .set(UUID.fromString(read(result.getResponse().getContentAsString(), "$.id")))); + + String epersonUuid = String.valueOf(idRef.get()); + EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); + assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); + assertNull(registrationDataService.findByToken(context, newRegisterToken)); + + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } finally { + EPersonBuilder.deleteEPerson(idRef.get()); + } } @Test diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java index 92c37007b135..de63aaf6cf35 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ProcessRestRepositoryIT.java @@ -43,6 +43,13 @@ public class ProcessRestRepositoryIT extends AbstractControllerIntegrationTest { @Before public void setup() throws SQLException { + CollectionUtils.emptyIfNull(processService.findAll(context)).stream().forEach(process -> { + try { + processService.delete(context, process); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }); parameters.add(new DSpaceCommandLineParameter("-r", "test")); parameters.add(new DSpaceCommandLineParameter("-i", null)); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java index f50b4c9d65e5..da6f7a94d885 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -12,16 +12,19 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.sql.SQLException; import java.util.Iterator; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.RegistrationRest; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.eperson.RegistrationData; import org.dspace.eperson.dao.RegistrationDataDAO; import org.dspace.services.ConfigurationService; +import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -33,6 +36,19 @@ public class RegistrationRestControllerIT extends AbstractControllerIntegrationT @Autowired private ConfigurationService configurationService; + + @Before + public void setup() throws SQLException { + CollectionUtils.emptyIfNull(registrationDataDAO.findAll(context, RegistrationData.class)).stream() + .forEach(registrationData -> { + try { + registrationDataDAO.delete(context, registrationData); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }); + } + @Test public void registrationFlowTest() throws Exception { List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); @@ -44,7 +60,7 @@ public void registrationFlowTest() throws Exception { getClient().perform(post("/api/eperson/registrations") .content(mapper.writeValueAsBytes(registrationRest)) .contentType(contentType)) - .andExpect(status().isCreated()); + .andExpect(status().isCreated()); registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); assertEquals(1, registrationDataList.size()); assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); From 80f7bad4944924b6644d02cfeb9e99e4529cce5b Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Thu, 11 Jun 2020 10:23:50 +0200 Subject: [PATCH 54/73] [Task 71348] fixed the patch replace password for eperson with a token to now be available to anonymous calls --- .../EPersonPasswordReplaceOperation.java | 1 + .../EPersonRestPermissionEvaluatorPlugin.java | 14 ++++++++++- .../app/rest/EPersonRestRepositoryIT.java | 23 ++++++++----------- ...rsonRestPermissionEvaluatorPluginTest.java | 19 ++++++++++++--- 4 files changed, 39 insertions(+), 18 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java index 1a3201abddd2..5a30f26fc133 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/patch/operation/EPersonPasswordReplaceOperation.java @@ -84,6 +84,7 @@ private void verifyAndDeleteToken(Context context, EPerson eperson, String token throw new AccessDeniedException("The token in the parameter belongs to a different EPerson" + " than the uri indicates"); } + context.setCurrentUser(ePersonFromToken); accountService.deleteToken(context, token); } catch (SQLException | AuthorizeException e) { log.error("Failed to verify or delete the token for an EPerson patch", e); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java index 92b0e433fb2e..6d670772ac07 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java @@ -10,7 +10,9 @@ import java.io.Serializable; import java.util.List; import java.util.UUID; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; import org.dspace.app.rest.repository.patch.operation.DSpaceObjectMetadataPatchUtils; @@ -82,6 +84,17 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t public boolean hasPatchPermission(Authentication authentication, Serializable targetId, String targetType, Patch patch) { + List operations = patch.getOperations(); + // If it's a password replace action, we can allow anon through provided that there's a token present + Request currentRequest = requestService.getCurrentRequest(); + if (currentRequest != null) { + HttpServletRequest httpServletRequest = currentRequest.getHttpServletRequest(); + if (operations.size() > 0 && StringUtils.equalsIgnoreCase(operations.get(0).getOp(), "replace") + && StringUtils.equalsIgnoreCase(operations.get(0).getPath(), "/password") + && StringUtils.isNotBlank(httpServletRequest.getParameter("token"))) { + return true; + } + } /** * First verify that the user has write permission on the eperson. */ @@ -89,7 +102,6 @@ public boolean hasPatchPermission(Authentication authentication, Serializable ta return false; } - List operations = patch.getOperations(); /** * The entire Patch request should be denied if it contains operations that are diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index fbd0f071ecca..ec2167915b29 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -1865,10 +1865,9 @@ public void patchReplacePasswordWithToken() throws Exception { String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); - String token = getAuthToken(admin.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password - getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) .param("token", tokenForEPerson)) @@ -1902,14 +1901,13 @@ public void patchReplacePasswordWithRandomTokenPatchFail() throws Exception { String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); - String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password - getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) .param("token", "RandomToken")) - .andExpect(status().isForbidden()); + .andExpect(status().isUnauthorized()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); assertEquals(oldPassword.getHashString(),newPasswordHash.getHashString()); @@ -1951,14 +1949,13 @@ public void patchReplacePasswordWithOtherUserTokenFail() throws Exception { String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); String tokenForEPersonTwo = registrationDataService.findByEmail(context, ePersonTwo.getEmail()).getToken(); - String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password - getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) .param("token", tokenForEPersonTwo)) - .andExpect(status().isForbidden()); + .andExpect(status().isUnauthorized()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); assertEquals(oldPassword.getHashString(),newPasswordHash.getHashString()); @@ -1991,14 +1988,13 @@ public void patchReplaceEmailWithTokenFail() throws Exception { String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String tokenForEPerson = registrationDataService.findByEmail(context, ePerson.getEmail()).getToken(); - String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password - getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) .param("token", tokenForEPerson)) - .andExpect(status().isForbidden()); + .andExpect(status().isUnauthorized()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); assertEquals(oldPassword.getHashString(),newPasswordHash.getHashString()); @@ -2041,14 +2037,13 @@ public void registerNewAccountPatchUpdatePasswordRandomUserUuidFail() throws Exc String patchBody = getPatchContent(ops); accountService.sendRegistrationInfo(context, ePerson.getEmail()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - String token = getAuthToken(eperson.getEmail(), password); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); // updates password - getClient(token).perform(patch("/api/eperson/epersons/" + ePerson.getID()) + getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) .param("token", newRegisterToken)) - .andExpect(status().isForbidden()); + .andExpect(status().isUnauthorized()); PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPluginTest.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPluginTest.java index 21fcd60b38d8..9ff264a1c78b 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPluginTest.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPluginTest.java @@ -19,27 +19,40 @@ import org.dspace.app.rest.model.patch.Operation; import org.dspace.app.rest.model.patch.Patch; import org.dspace.app.rest.model.patch.ReplaceOperation; +import org.dspace.services.RequestService; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.security.core.Authentication; +import org.springframework.test.util.ReflectionTestUtils; /** * This class verifies that {@link EPersonRestPermissionEvaluatorPlugin} properly * evaluates Patch requests. */ +@RunWith(MockitoJUnitRunner.class) public class EPersonRestPermissionEvaluatorPluginTest { + @InjectMocks private EPersonRestPermissionEvaluatorPlugin ePersonRestPermissionEvaluatorPlugin; private Authentication authentication; + @Mock + private RequestService requestService; + @Before public void setUp() throws Exception { ePersonRestPermissionEvaluatorPlugin = spy(EPersonRestPermissionEvaluatorPlugin.class); authentication = mock(Authentication.class); DSpaceRestPermission restPermission = DSpaceRestPermission.convert("WRITE"); when(ePersonRestPermissionEvaluatorPlugin - .hasDSpacePermission(authentication, null, null, restPermission)).thenReturn(true); + .hasDSpacePermission(authentication, null, null, restPermission)).thenReturn(true); + ReflectionTestUtils.setField(ePersonRestPermissionEvaluatorPlugin, "requestService", requestService); + when(requestService.getCurrentRequest()).thenReturn(null); } @Test @@ -52,7 +65,7 @@ public void testHasPatchPermissionAuthFails() throws Exception { ops.add(canLoginOperation); Patch patch = new Patch(ops); assertFalse(ePersonRestPermissionEvaluatorPlugin - .hasPatchPermission(authentication, null, null, patch)); + .hasPatchPermission(authentication, null, null, patch)); } @@ -64,7 +77,7 @@ public void testHasPatchPermissionAuthOk() throws Exception { ops.add(passwordOperation); Patch patch = new Patch(ops); assertTrue(ePersonRestPermissionEvaluatorPlugin - .hasPatchPermission(authentication, null, null, patch)); + .hasPatchPermission(authentication, null, null, patch)); } From aa15d658178601ed121f527395edc7b22a9bda07 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 11 Jun 2020 10:47:37 +0200 Subject: [PATCH 55/73] Implement community feedbacks --- .../WorkspaceItemRestRepository.java | 3 +- .../rest/WorkspaceItemRestRepositoryIT.java | 55 ++++++++++++++++++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java index 0b5217405058..a8b514ba0ce3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/WorkspaceItemRestRepository.java @@ -331,8 +331,7 @@ private void evaluatePatch(Context context, HttpServletRequest request, Workspac } } catch (UnprocessableEntityException e) { - log.error(e.getMessage(), e); - throw new UnprocessableEntityException("Error processing the patch request", e); + throw e; } catch (Exception e) { log.error(e.getMessage(), e); throw new PatchException("Error processing the patch request", e); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index e18f06c1e4d6..b17c76f2f3e2 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -3245,6 +3245,11 @@ public void patchAddMetadataOnSectionNotExistentTest() throws Exception { .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isUnprocessableEntity()); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.traditionalpageone['dc.title']").doesNotExist()); + } @Test @@ -3330,6 +3335,10 @@ public void patchAddTitleOnSectionThatNotContainAttributeTitleTest() throws Exce .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isUnprocessableEntity()); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.traditionalpageone['dc.title']").doesNotExist()); } @Test @@ -3506,7 +3515,7 @@ public void patchUploadMetadatoNotExistTest() throws Exception { } @Test - public void patchUploadWrongPathTest() throws Exception { + public void patchUploadNotConfiguredMetadataTest() throws Exception { context.turnOffAuthorisationSystem(); parentCommunity = CommunityBuilder.createCommunity(context) @@ -3523,7 +3532,6 @@ public void patchUploadWrongPathTest() throws Exception { WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) .withTitle("Test WorkspaceItem") - .withIssueDate("2017-10-17") .withFulltext("simple-article.pdf", "/local/path/simple-article.pdf", pdf) .build(); @@ -3541,6 +3549,11 @@ public void patchUploadWrongPathTest() throws Exception { .content(patchBody) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isUnprocessableEntity()); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.sections.bitstream-metadata['dc.date.issued']").doesNotExist()) + .andExpect(jsonPath("$.sections.traditionalpageone['dc.date.issued']").doesNotExist()); } @Test @@ -3714,4 +3727,42 @@ public void patchDeleteSectionTest() throws Exception { .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.subject']").doesNotExist()) .andExpect(jsonPath("$.sections.traditionalpagetwo['dc.description.abstract']").doesNotExist()); } + + @Test + public void patchDeleteMetadataThatNotExistTest() throws Exception { + context.turnOffAuthorisationSystem(); + + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withIssueDate("2020-04-21") + .withSubject("ExtraEntry") + .build(); + + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + List operations = new ArrayList(); + operations.add(new RemoveOperation("/sections/traditionalpageone/dc.not.existing/0")); + + String patchBody = getPatchContent(operations); + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + } } From 3aba1b7545546a068f311adf7f71695699df84d9 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Thu, 11 Jun 2020 15:08:03 +0200 Subject: [PATCH 56/73] Added a way to ignore write only properties in REST objects for tests and rewrote EPersonRestRepositoryIT tests to use this --- .../app/rest/EPersonRestRepositoryIT.java | 216 +++++++++++++----- .../jackson/IgnoreJacksonWriteOnlyAccess.java | 24 ++ 2 files changed, 180 insertions(+), 60 deletions(-) create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index ec2167915b29..ebbb26ad7ab5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -43,6 +43,7 @@ import org.dspace.app.rest.builder.EPersonBuilder; import org.dspace.app.rest.builder.GroupBuilder; import org.dspace.app.rest.builder.ItemBuilder; +import org.dspace.app.rest.jackson.IgnoreJacksonWriteOnlyAccess; import org.dspace.app.rest.matcher.EPersonMatcher; import org.dspace.app.rest.matcher.GroupMatcher; import org.dspace.app.rest.matcher.HalMatcher; @@ -2127,18 +2128,25 @@ public void postEPersonWithTokenWithEmailProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + - "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); AtomicReference idRef = new AtomicReference(); + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); try { getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$", Matchers.allOf( @@ -2182,18 +2190,28 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"email\":\"" + newRegisterEmail + - "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; - + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + ePersonRest.setSelfRegistered(true); AtomicReference idRef = new AtomicReference(); + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); + + try { getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$", Matchers.allOf( @@ -2250,15 +2268,25 @@ public void postEPersonWithTokenWithTwoTokensDifferentEmailProperty() throws Exc .andExpect(status().isCreated()); String newRegisterTokenTwo = registrationDataService.findByEmail(context, newRegisterEmailTwo).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmailTwo + - "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmailTwo); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2287,15 +2315,25 @@ public void postEPersonWithRandomTokenWithEmailProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + - "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", "randomToken") - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2322,15 +2360,26 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredFalseProperty() throws .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":false,\"email\":\"" + newRegisterEmail + - "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + ePersonRest.setSelfRegistered(false); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2357,14 +2406,23 @@ public void postEPersonWithTokenWithoutLastNameProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]},\"selfRegistered\":true," + - "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + ePersonRest.setSelfRegistered(true); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2391,14 +2449,23 @@ public void postEPersonWithTokenWithoutFirstNameProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true," + - "\"email\":\"" + newRegisterEmail + "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + ePersonRest.setSelfRegistered(true); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2425,15 +2492,24 @@ public void postEPersonWithTokenWithoutPasswordProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]}," + - "\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2461,15 +2537,25 @@ public void postEPersonWithWrongToken() throws Exception { .andExpect(status().isCreated()); String forgotPasswordToken = registrationDataService.findByEmail(context, eperson.getEmail()).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"selfRegistered\":true,\"password\":\"somePassword\"," + - "\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + ePersonRest.setSelfRegistered(true); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); getClient().perform(post("/api/eperson/epersons") .param("token", forgotPasswordToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); @@ -2497,18 +2583,28 @@ public void postEPersonWithTokenWithEmailPropertyAnonUser() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"email\":\"" + newRegisterEmail + - "\",\"password\":\"somePassword\",\"type\":\"eperson\"}"; + + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setEmail(newRegisterEmail); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); + + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); AtomicReference idRef = new AtomicReference(); try { getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$", Matchers.allOf( diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java new file mode 100644 index 000000000000..577ed6e4d553 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java @@ -0,0 +1,24 @@ +/** + * 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.jackson; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; + +public class IgnoreJacksonWriteOnlyAccess extends JacksonAnnotationIntrospector { + + @Override + public JsonProperty.Access findPropertyAccess(Annotated m) { + JsonProperty.Access access = super.findPropertyAccess(m); + if (access == JsonProperty.Access.WRITE_ONLY) { + return JsonProperty.Access.AUTO; + } + return access; + } +} From 4ff01ae963a4bb765b3464600c005b4aa51d15c0 Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Fri, 12 Jun 2020 01:07:08 +0200 Subject: [PATCH 57/73] added test --- .../rest/WorkspaceItemRestRepositoryIT.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java index b17c76f2f3e2..c4a64c6de1cd 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/WorkspaceItemRestRepositoryIT.java @@ -3765,4 +3765,42 @@ public void patchDeleteMetadataThatNotExistTest() throws Exception { .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) .andExpect(status().isUnprocessableEntity()); } + + @Test + public void patchDeleteMetadataWrongSectionTest() throws Exception { + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + Community child1 = CommunityBuilder.createSubCommunity(context, parentCommunity) + .withName("Sub Community") + .build(); + Collection col1 = CollectionBuilder.createCollection(context, child1) + .withName("Collection 1") + .build(); + WorkspaceItem witem = WorkspaceItemBuilder.createWorkspaceItem(context, col1) + .withTitle("Test title") + .withIssueDate("2019-04-25") + .withSubject("ExtraEntry") + .build(); + //disable file upload mandatory + configurationService.setProperty("webui.submit.upload.required", false); + context.restoreAuthSystemState(); + + String authToken = getAuthToken(eperson.getEmail(), password); + List operations = new ArrayList(); + operations.add(new RemoveOperation("/sections/traditionalpagetwo/dc.title/0")); + String patchBody = getPatchContent(operations); + + getClient(authToken).perform(patch("/api/submission/workspaceitems/" + witem.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .andExpect(status().isUnprocessableEntity()); + + getClient(authToken).perform(get("/api/submission/workspaceitems/" + witem.getID())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", + Matchers.is(WorkspaceItemMatcher.matchItemWithTitleAndDateIssuedAndSubject(witem, + "Test title", "2019-04-25", "ExtraEntry")))); + } } From d212a84a4e1d2739e958337258e94811c589d7d0 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Fri, 19 Jun 2020 09:56:34 +0200 Subject: [PATCH 58/73] [Task 71442] applied feedback to the new registration endpoint --- .../app/rest/EPersonRestRepositoryIT.java | 24 +++++++++++-------- .../EPersonRegistrationFeatureIT.java | 7 ------ .../jackson/IgnoreJacksonWriteOnlyAccess.java | 11 +++++++++ 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index ebbb26ad7ab5..b904429e1fc9 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -2071,18 +2071,25 @@ public void postEPersonWithTokenWithoutEmailProperty() throws Exception { .andExpect(status().isCreated()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); - // We need to create this json manually to support actually setting the password to a value. - // When using the mapper to write an EPersonRest object to JSON to pass it along, the password gets lost - String json = "{\"metadata\":{\"eperson.firstname\":[{\"value\":\"John\"}]," + - "\"eperson.lastname\":[{\"value\":\"Doe\"}]},\"password\":\"somePassword\"," + - "\"type\":\"eperson\"}"; - + EPersonRest ePersonRest = new EPersonRest(); + MetadataRest metadataRest = new MetadataRest(); + ePersonRest.setCanLogIn(true); + MetadataValueRest surname = new MetadataValueRest(); + surname.setValue("Doe"); + metadataRest.put("eperson.lastname", surname); + MetadataValueRest firstname = new MetadataValueRest(); + firstname.setValue("John"); + metadataRest.put("eperson.firstname", firstname); + ePersonRest.setMetadata(metadataRest); + ePersonRest.setPassword("somePassword"); AtomicReference idRef = new AtomicReference(); + mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); + try { getClient().perform(post("/api/eperson/epersons") .param("token", newRegisterToken) - .content(json) + .content(mapper.writeValueAsBytes(ePersonRest)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$", Matchers.allOf( @@ -2233,9 +2240,6 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); assertNull(registrationDataService.findByToken(context, newRegisterToken)); - context.turnOffAuthorisationSystem(); - context.restoreAuthSystemState(); - context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, newRegisterToken); context.restoreAuthSystemState(); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java index 754804edf436..528ab24eb2c0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/authorization/EPersonRegistrationFeatureIT.java @@ -76,18 +76,13 @@ public void userRegistrationDisabledUnAuthorizedTest() throws Exception { SiteRest SiteRest = siteConverter.convert(site, Projection.DEFAULT); String siteUri = utils.linkToSingleResource(SiteRest, "self").getHref(); - context.turnOffAuthorisationSystem(); configurationService.setProperty("user.registration", false); - context.restoreAuthSystemState(); getClient().perform(get("/api/authz/authorizations/search/objectAndFeature") .param("uri", siteUri) .param("feature", epersonRegistrationFeature.getName())) .andExpect(status().isNoContent()); - context.turnOffAuthorisationSystem(); - configurationService.setProperty("user.registration", true); - context.restoreAuthSystemState(); } @@ -104,10 +99,8 @@ public void userRegistrationEnabledShibTest() throws Exception { .param("feature", epersonRegistrationFeature.getName())) .andExpect(status().isOk()); - context.turnOffAuthorisationSystem(); //Enable Shibboleth and password login configurationService.setProperty("plugin.sequence.org.dspace.authenticate.AuthenticationMethod", SHIB_ONLY); - context.restoreAuthSystemState(); getClient().perform(get("/api/authz/authorizations/search/objectAndFeature") .param("uri", siteUri) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java index 577ed6e4d553..df68ce8ab3b4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/jackson/IgnoreJacksonWriteOnlyAccess.java @@ -11,6 +11,17 @@ import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +/** + * This is a custom JacksonAnnotationIntrospector which allows us to ignore `@JsonProperty(access = Access + * .WRITE_ONLY)` annotations in our tests. + * Normally, this annotation allows the property to be written to (during deserialization), + * but does NOT allow it to be read (during serialization). + * In some tests, we need to ignore this annotation so that the test can use/verify the property + * during both serialization & deserialization. + * + * In order to use this class in a test, assign it the the current mapper like this: + * mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); + */ public class IgnoreJacksonWriteOnlyAccess extends JacksonAnnotationIntrospector { @Override From de6bc7d8d024b88d9b69da5032c0ba7edb9c854c Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Fri, 19 Jun 2020 10:18:28 +0200 Subject: [PATCH 59/73] [Task 71442] fixed compile error after merge --- .../EPersonRestPermissionEvaluatorPlugin.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java index 14bde8e666a6..50f209cedc84 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/EPersonRestPermissionEvaluatorPlugin.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.security; import java.io.Serializable; +import java.sql.SQLException; import java.util.List; import java.util.UUID; import javax.servlet.http.HttpServletRequest; @@ -69,16 +70,20 @@ public boolean hasDSpacePermission(Authentication authentication, Serializable t UUID dsoId = UUID.fromString(targetId.toString()); // anonymous user - if (ePerson == null) { - return false; + try { + if (ePerson == null) { + return false; } else if (dsoId.equals(ePerson.getID())) { return true; } else if (authorizeService.isCommunityAdmin(context, ePerson) - && AuthorizeUtil.canCommunityAdminManageAccounts()) { + && AuthorizeUtil.canCommunityAdminManageAccounts()) { return true; } else if (authorizeService.isCollectionAdmin(context, ePerson) - && AuthorizeUtil.canCollectionAdminManageAccounts()) { - return true; + && AuthorizeUtil.canCollectionAdminManageAccounts()) { + return true; + } + } catch (SQLException e) { + log.error(e.getMessage(), e); } From 08abaf8b039a5c9a152ae296b1e1961a2af6d983 Mon Sep 17 00:00:00 2001 From: Yana De Pauw Date: Fri, 19 Jun 2020 14:05:14 +0200 Subject: [PATCH 60/73] 71440: Implement feedback --- ... => SubmissionCCLicenseUrlRepository.java} | 53 +++++++++++++------ ...ionCCLicenseUrlResourceHalLinkFactory.java | 22 +++++--- .../model/SubmissionCCLicenseUrlRest.java | 9 ++-- .../impl/CCLicenseRemovePatchOperation.java | 11 +++- .../rest/CCLicenseRemovePatchOperationIT.java | 3 +- ...> SubmissionCCLicenseUrlRepositoryIT.java} | 22 ++++---- 6 files changed, 79 insertions(+), 41 deletions(-) rename dspace-server-webapp/src/main/java/org/dspace/app/rest/{SubmissionCCLicenseSearchController.java => SubmissionCCLicenseUrlRepository.java} (65%) rename dspace-server-webapp/src/test/java/org/dspace/app/rest/{SubmissionCCLicenseSearchControllerIT.java => SubmissionCCLicenseUrlRepositoryIT.java} (77%) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java similarity index 65% rename from dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java rename to dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java index 76ed1021d338..ff7618350622 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java @@ -14,28 +14,28 @@ import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.exception.DSpaceBadRequestException; -import org.dspace.app.rest.model.SubmissionCCLicenseRest; +import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; -import org.dspace.app.rest.model.hateoas.SubmissionCCLicenseUrlResource; +import org.dspace.app.rest.repository.DSpaceRestRepository; import org.dspace.app.rest.utils.Utils; +import org.dspace.core.Context; import org.dspace.license.service.CreativeCommonsService; import org.dspace.services.RequestService; import org.dspace.utils.DSpace; import org.springframework.beans.factory.annotation.Autowired; +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.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.stereotype.Component; /** - * This controller is responsible for searching the CC License URI + * This Repository is responsible for handling the CC License URIs. + * It only supports a search method */ -@RestController -@RequestMapping("/api/" + SubmissionCCLicenseRest.CATEGORY + "/" + SubmissionCCLicenseRest.PLURAL + "/search" + - "/rightsByQuestions") -@PreAuthorize("hasAuthority('AUTHENTICATED')") -public class SubmissionCCLicenseSearchController { + +@Component(SubmissionCCLicenseUrlRest.CATEGORY + "." + SubmissionCCLicenseUrlRest.NAME) +public class SubmissionCCLicenseUrlRepository extends DSpaceRestRepository { @Autowired protected Utils utils; @@ -52,10 +52,11 @@ public class SubmissionCCLicenseSearchController { * Retrieves the CC License URI based on the license ID and answers in the field questions, provided as parameters * to this request * - * @return the CC License URI as a SubmissionCCLicenseUrlResource + * @return the CC License URI as a SubmissionCCLicenseUrlRest */ - @RequestMapping(method = RequestMethod.GET) - public SubmissionCCLicenseUrlResource findByRightsByQuestions() { + @PreAuthorize("hasAuthority('AUTHENTICATED')") + @SearchRestMethod(name = "rightsByQuestions") + public SubmissionCCLicenseUrlRest findByRightsByQuestions() { ServletRequest servletRequest = requestService.getCurrentRequest() .getServletRequest(); Map requestParameterMap = servletRequest @@ -66,6 +67,9 @@ public SubmissionCCLicenseUrlResource findByRightsByQuestions() { throw new DSpaceBadRequestException( "A \"license\" parameter needs to be provided."); } + + // Loop through parameters to find answer parameters, adding them to the parameterMap. Zero or more answers + // may exist, as some CC licenses do not require answers for (String parameter : requestParameterMap.keySet()) { if (StringUtils.startsWith(parameter, "answer_")) { String field = StringUtils.substringAfter(parameter, "answer_"); @@ -93,8 +97,25 @@ public SubmissionCCLicenseUrlResource findByRightsByQuestions() { throw new ResourceNotFoundException("No CC License URI could be found for ID: " + licenseId); } - SubmissionCCLicenseUrlRest submissionCCLicenseUrlRest = converter.toRest(licenseUri, utils.obtainProjection()); - return converter.toResource(submissionCCLicenseUrlRest); + return converter.toRest(licenseUri, utils.obtainProjection()); + + } + + /** + * The findOne method is not supported in this repository + */ + public SubmissionCCLicenseUrlRest findOne(final Context context, final String s) { + throw new RepositoryMethodNotImplementedException(SubmissionCCLicenseUrlRest.NAME, "findOne"); + } + + /** + * The findAll method is not supported in this repository + */ + public Page findAll(final Context context, final Pageable pageable) { + throw new RepositoryMethodNotImplementedException(SubmissionCCLicenseUrlRest.NAME, "findAll"); + } + public Class getDomainClass() { + return SubmissionCCLicenseUrlRest.class; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java index cb44d68e73e9..07d5e46c61e0 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/link/process/SubmissionCCLicenseUrlResourceHalLinkFactory.java @@ -10,14 +10,16 @@ import java.util.LinkedList; import java.util.Map; -import org.dspace.app.rest.SubmissionCCLicenseSearchController; +import org.dspace.app.rest.RestResourceController; import org.dspace.app.rest.link.HalLinkFactory; +import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; import org.dspace.app.rest.model.hateoas.SubmissionCCLicenseUrlResource; import org.dspace.services.RequestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; import org.springframework.hateoas.Link; import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.util.UriComponentsBuilder; /** @@ -25,16 +27,17 @@ */ @Component public class SubmissionCCLicenseUrlResourceHalLinkFactory - extends HalLinkFactory { + extends HalLinkFactory { @Autowired RequestService requestService; /** * Add a self link based on the search parameters - * @param halResource - The halResource - * @param pageable - The page information - * @param list - The list of present links + * + * @param halResource - The halResource + * @param pageable - The page information + * @param list - The list of present links * @throws Exception */ @Override @@ -46,7 +49,10 @@ protected void addLinks(SubmissionCCLicenseUrlResource halResource, final Pageab Map parameterMap = requestService.getCurrentRequest().getHttpServletRequest() .getParameterMap(); - UriComponentsBuilder uriComponentsBuilder = uriBuilder(getMethodOn().findByRightsByQuestions()); + + UriComponentsBuilder uriComponentsBuilder = uriBuilder(getMethodOn().executeSearchMethods( + SubmissionCCLicenseUrlRest.CATEGORY, SubmissionCCLicenseUrlRest.PLURAL, "rightsByQuestions", null, null, + null, null, new LinkedMultiValueMap<>())); for (String key : parameterMap.keySet()) { uriComponentsBuilder.queryParam(key, parameterMap.get(key)); } @@ -56,8 +62,8 @@ protected void addLinks(SubmissionCCLicenseUrlResource halResource, final Pageab @Override - protected Class getControllerClass() { - return SubmissionCCLicenseSearchController.class; + protected Class getControllerClass() { + return RestResourceController.class; } @Override diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java index 46aa9bc70589..77263ba3178b 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SubmissionCCLicenseUrlRest.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import org.dspace.app.rest.SubmissionCCLicenseSearchController; +import org.dspace.app.rest.RestResourceController; /** * This class is the REST representation of the CCLicense URL String object and acts as a data object @@ -17,6 +17,9 @@ */ public class SubmissionCCLicenseUrlRest extends BaseObjectRest { public static final String NAME = "submissioncclicenseUrl"; + public static final String PLURAL = "submissioncclicenseUrls"; + public static final String CATEGORY = RestAddressableModel.CONFIGURATION; + private String url; @@ -46,12 +49,12 @@ public String getType() { @Override public String getCategory() { - return SubmissionCCLicenseRest.CATEGORY; + return SubmissionCCLicenseUrlRest.CATEGORY; } @Override @JsonIgnore public Class getController() { - return SubmissionCCLicenseSearchController.class; + return RestResourceController.class; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java index 19229a4f72b5..add819b7a408 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/CCLicenseRemovePatchOperation.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest.submit.factory.impl; +import org.apache.commons.lang3.StringUtils; import org.dspace.content.InProgressSubmission; import org.dspace.content.Item; import org.dspace.core.Context; @@ -44,7 +45,15 @@ protected Class getClassForEvaluation() { void remove(Context context, Request currentRequest, InProgressSubmission source, String path, Object value) throws Exception { Item item = source.getItem(); - creativeCommonsService.removeLicense(context, item); + + + if (StringUtils.isNotBlank(creativeCommonsService.getLicenseName(item))) { + creativeCommonsService.removeLicense(context, item); + } else { + throw new IllegalArgumentException("No CC license can be removed since none is present on submission: " + + source.getID()); + } + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java index 40828a766777..3b05621f085a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/CCLicenseRemovePatchOperationIT.java @@ -130,7 +130,6 @@ public void patchRemoveSubmissionCCLicenseNonExisting() throws Exception { getClient(epersonToken).perform(patch("/api/submission/workspaceitems/" + workspaceItem.getID()) .content(removePatch) .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.sections", not(hasJsonPath("cclicense")))); + .andExpect(status().isInternalServerError()); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepositoryIT.java similarity index 77% rename from dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java rename to dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepositoryIT.java index 8a93e18a8e58..84fe06ce19c0 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseSearchControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepositoryIT.java @@ -17,14 +17,14 @@ import org.junit.Test; /** - * Class to the methods from the SubmissionCCLicenseSearchController + * Class to the methods from the SubmissionCCLicenseUrlRepository * Since the CC Licenses and the corresponding URIs are obtained from the CC License API, a mock service has been * implemented. * This mock service will return a fixed set of CC Licenses using a similar structure to the ones obtained from the * CC License API. * Refer to {@link org.dspace.license.MockCCLicenseConnectorServiceImpl} for more information */ -public class SubmissionCCLicenseSearchControllerIT extends AbstractControllerIntegrationTest { +public class SubmissionCCLicenseUrlRepositoryIT extends AbstractControllerIntegrationTest { @Test @@ -32,28 +32,28 @@ public void searchRightsByQuestionsTest() throws Exception { String epersonToken = getAuthToken(eperson.getEmail(), password); getClient(epersonToken).perform(get( - "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + + "/api/config/submissioncclicenseUrls/search/rightsByQuestions?license=license2&answer_license2-field0" + "=license2-field0-enum1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.url", is("mock-license-uri"))) .andExpect(jsonPath("$.type", is("submissioncclicenseUrl"))) .andExpect(jsonPath("$._links.self.href", - is("http://localhost/api/config/submissioncclicenses/search/rightsByQuestions" + + is("http://localhost/api/config/submissioncclicenseUrls/search/rightsByQuestions" + "?license=license2" + "&answer_license2-field0=license2-field0-enum1"))); } @Test - public void searchRightsByQuestionsTestLicenseWithoutFields() throws Exception { + public void searchRightsByQuestionsTestLicenseForLicenseWithoutQuestions() throws Exception { String epersonToken = getAuthToken(eperson.getEmail(), password); getClient(epersonToken) - .perform(get("/api/config/submissioncclicenses/search/rightsByQuestions?license=license3")) + .perform(get("/api/config/submissioncclicenseUrls/search/rightsByQuestions?license=license3")) .andExpect(status().isOk()) .andExpect(jsonPath("$.url", is("mock-license-uri"))) .andExpect(jsonPath("$.type", is("submissioncclicenseUrl"))) .andExpect(jsonPath("$._links.self.href", - is("http://localhost/api/config/submissioncclicenses/search/rightsByQuestions" + + is("http://localhost/api/config/submissioncclicenseUrls/search/rightsByQuestions" + "?license=license3"))); } @@ -62,7 +62,7 @@ public void searchRightsByQuestionsNonExistingLicense() throws Exception { String epersonToken = getAuthToken(eperson.getEmail(), password); getClient(epersonToken).perform(get( - "/api/config/submissioncclicenses/search/rightsByQuestions?license=nonexisting-license" + + "/api/config/submissioncclicenseUrls/search/rightsByQuestions?license=nonexisting-license" + "&answer_license2-field0=license2-field0-enum1")) .andExpect(status().isNotFound()); } @@ -72,7 +72,7 @@ public void searchRightsByQuestionsMissingRequiredAnswer() throws Exception { String epersonToken = getAuthToken(eperson.getEmail(), password); getClient(epersonToken).perform(get( - "/api/config/submissioncclicenses/search/rightsByQuestions?license=license1&answer_license1field0" + + "/api/config/submissioncclicenseUrls/search/rightsByQuestions?license=license1&answer_license1field0" + "=license1field0enum1")) .andExpect(status().isBadRequest()); } @@ -82,7 +82,7 @@ public void searchRightsByQuestionsAdditionalNonExistingAnswer() throws Exceptio String epersonToken = getAuthToken(eperson.getEmail(), password); getClient(epersonToken).perform(get( - "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2" + + "/api/config/submissioncclicenseUrls/search/rightsByQuestions?license=license2" + "&answer_license2field0=license2field0enum1&answer_nonexisting=test")) .andExpect(status().isBadRequest()); } @@ -91,7 +91,7 @@ public void searchRightsByQuestionsAdditionalNonExistingAnswer() throws Exceptio public void searchRightsByQuestionsAdditionalUnAuthorized() throws Exception { getClient().perform(get( - "/api/config/submissioncclicenses/search/rightsByQuestions?license=license2&answer_license2-field0" + + "/api/config/submissioncclicenseUrls/search/rightsByQuestions?license=license2&answer_license2-field0" + "=license2-field0-enum1")) .andExpect(status().isUnauthorized()); From 891ab3f3e261e8a78321091b8835fab413937899 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Fri, 19 Jun 2020 14:47:53 +0200 Subject: [PATCH 61/73] [Task 71442] fixed the tests without the additional cleanup required --- .../app/rest/RegistrationRestControllerIT.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java index da6f7a94d885..d02dceea20fe 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java @@ -12,19 +12,16 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import java.sql.SQLException; import java.util.Iterator; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.RegistrationRest; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.eperson.RegistrationData; import org.dspace.eperson.dao.RegistrationDataDAO; import org.dspace.services.ConfigurationService; -import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -36,19 +33,6 @@ public class RegistrationRestControllerIT extends AbstractControllerIntegrationT @Autowired private ConfigurationService configurationService; - - @Before - public void setup() throws SQLException { - CollectionUtils.emptyIfNull(registrationDataDAO.findAll(context, RegistrationData.class)).stream() - .forEach(registrationData -> { - try { - registrationDataDAO.delete(context, registrationData); - } catch (SQLException e) { - throw new RuntimeException(e); - } - }); - } - @Test public void registrationFlowTest() throws Exception { List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); @@ -119,5 +103,6 @@ public void forgotPasswordTest() throws Exception { RegistrationData registrationData = iterator.next(); registrationDataDAO.delete(context, registrationData); } + context.complete(); } } From 95110d2b5a202aadba7395e2f598be7bbceb82db Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 23 Jun 2020 14:23:50 +0200 Subject: [PATCH 62/73] [Task 71440] changed the ModelObject for SubmissionCCLicenseUrlRest from String to SubmissionCCLicenseUrl --- .../SubmissionCCLicenseUrlRepository.java | 4 +- .../SubmissionCCLicenseUrlConverter.java | 15 +++--- .../model/wrapper/SubmissionCCLicenseUrl.java | 51 +++++++++++++++++++ 3 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java index ff7618350622..62cd48d56e61 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java @@ -16,6 +16,7 @@ import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.RepositoryMethodNotImplementedException; import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; +import org.dspace.app.rest.model.wrapper.SubmissionCCLicenseUrl; import org.dspace.app.rest.repository.DSpaceRestRepository; import org.dspace.app.rest.utils.Utils; import org.dspace.core.Context; @@ -93,11 +94,12 @@ public SubmissionCCLicenseUrlRest findByRightsByQuestions() { String licenseUri = creativeCommonsService.retrieveLicenseUri(licenseId, fullParamMap); + SubmissionCCLicenseUrl submissionCCLicenseUrl = new SubmissionCCLicenseUrl(licenseUri, licenseUri); if (StringUtils.isBlank(licenseUri)) { throw new ResourceNotFoundException("No CC License URI could be found for ID: " + licenseId); } - return converter.toRest(licenseUri, utils.obtainProjection()); + return converter.toRest(submissionCCLicenseUrl, utils.obtainProjection()); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java index 99945661c52e..da55e8ba01f1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.converter; import org.dspace.app.rest.model.SubmissionCCLicenseUrlRest; +import org.dspace.app.rest.model.wrapper.SubmissionCCLicenseUrl; import org.dspace.app.rest.projection.Projection; import org.springframework.stereotype.Component; @@ -17,26 +18,26 @@ * representation SubmissionCCLicenseUrlRest and vice versa */ @Component -public class SubmissionCCLicenseUrlConverter implements DSpaceConverter { +public class SubmissionCCLicenseUrlConverter implements DSpaceConverter { /** * Convert a Submission CC License Url String to its REST representation - * @param modelObject - the CC License Url String to convert + * @param modelObject - the CC License Url object to convert * @param projection - the projection * @return the corresponding SubmissionCCLicenseUrlRest object */ @Override - public SubmissionCCLicenseUrlRest convert(final String modelObject, final Projection projection) { + public SubmissionCCLicenseUrlRest convert(SubmissionCCLicenseUrl modelObject, Projection projection) { SubmissionCCLicenseUrlRest submissionCCLicenseUrlRest = new SubmissionCCLicenseUrlRest(); - submissionCCLicenseUrlRest.setUrl(modelObject); - submissionCCLicenseUrlRest.setId(modelObject); + submissionCCLicenseUrlRest.setUrl(modelObject.getUrl()); + submissionCCLicenseUrlRest.setId(modelObject.getId()); return submissionCCLicenseUrlRest; } @Override - public Class getModelClass() { - return String.class; + public Class getModelClass() { + return SubmissionCCLicenseUrl.class; } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java new file mode 100644 index 000000000000..4178e67c49f3 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java @@ -0,0 +1,51 @@ +/** + * 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.wrapper; + +public class SubmissionCCLicenseUrl { + + private String url; + private String id; + + public SubmissionCCLicenseUrl(String url, String id) { + this.url = url; + this.id = id; + } + + /** + * Generic getter for the url + * @return the url value of this SubmissionCCLicenseUrl + */ + public String getUrl() { + return url; + } + + /** + * Generic setter for the url + * @param url The url to be set on this SubmissionCCLicenseUrl + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * Generic getter for the id + * @return the id value of this SubmissionCCLicenseUrl + */ + public String getId() { + return id; + } + + /** + * Generic setter for the id + * @param id The id to be set on this SubmissionCCLicenseUrl + */ + public void setId(String id) { + this.id = id; + } +} From 9f9fe26549fc8d0dd17059b4f6d84bff6451badf Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 23 Jun 2020 15:09:45 +0200 Subject: [PATCH 63/73] [Task 71440] fixed checkstyle and added preAuthorize to the SubmissionCCLicenseUrlRepository --- .../org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java | 1 + .../app/rest/converter/SubmissionCCLicenseUrlConverter.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java index 62cd48d56e61..26703f320dbd 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java @@ -106,6 +106,7 @@ public SubmissionCCLicenseUrlRest findByRightsByQuestions() { /** * The findOne method is not supported in this repository */ + @PreAuthorize("permitAll()") public SubmissionCCLicenseUrlRest findOne(final Context context, final String s) { throw new RepositoryMethodNotImplementedException(SubmissionCCLicenseUrlRest.NAME, "findOne"); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java index da55e8ba01f1..c5fd41fb7b23 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SubmissionCCLicenseUrlConverter.java @@ -18,7 +18,8 @@ * representation SubmissionCCLicenseUrlRest and vice versa */ @Component -public class SubmissionCCLicenseUrlConverter implements DSpaceConverter { +public class SubmissionCCLicenseUrlConverter + implements DSpaceConverter { /** * Convert a Submission CC License Url String to its REST representation From 2b6f19f6beb1f54b727b1a3d83c69b7179a55972 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Wed, 24 Jun 2020 11:14:40 +0200 Subject: [PATCH 64/73] [Task 71440] added javadocs to SubmissionCCLicenseUrl --- .../model/wrapper/SubmissionCCLicenseUrl.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java index 4178e67c49f3..68ff1166b452 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/wrapper/SubmissionCCLicenseUrl.java @@ -7,11 +7,28 @@ */ package org.dspace.app.rest.model.wrapper; +/** + * This class represents a model implementation for {@link org.dspace.app.rest.model.SubmissionCCLicenseUrlRest} + * This will simply store a url and an id. it'll be used to create an object with these variables out of information + * that came from the back-end. This object will then be used in the + * {@link org.dspace.app.rest.converter.SubmissionCCLicenseUrlConverter} to turn it into its REST object + */ public class SubmissionCCLicenseUrl { + /** + * The url for ths object + */ private String url; + /** + * The id for this object + */ private String id; + /** + * Default constructor with two parameters, url and id + * @param url The url of this object + * @param id The id of this object + */ public SubmissionCCLicenseUrl(String url, String id) { this.url = url; this.id = id; From 8ca6064c88af564865e837aaf40970aaa4048a77 Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Thu, 25 Jun 2020 10:16:46 +0200 Subject: [PATCH 65/73] [CC License] Adding search link to HAL output --- .../rest/SubmissionCCLicenseUrlRepository.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java index 26703f320dbd..cbfbd347b27c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletRequest; @@ -23,10 +24,12 @@ import org.dspace.license.service.CreativeCommonsService; import org.dspace.services.RequestService; import org.dspace.utils.DSpace; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.hateoas.Link; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Component; @@ -36,7 +39,8 @@ */ @Component(SubmissionCCLicenseUrlRest.CATEGORY + "." + SubmissionCCLicenseUrlRest.NAME) -public class SubmissionCCLicenseUrlRepository extends DSpaceRestRepository { +public class SubmissionCCLicenseUrlRepository extends DSpaceRestRepository + implements InitializingBean { @Autowired protected Utils utils; @@ -49,6 +53,9 @@ public class SubmissionCCLicenseUrlRepository extends DSpaceRestRepository findAll(final Context context, final Pag public Class getDomainClass() { return SubmissionCCLicenseUrlRest.class; } + + @Override + public void afterPropertiesSet() { + discoverableEndpointsService.register(this, Arrays.asList( + new Link("/api/" + SubmissionCCLicenseUrlRest.CATEGORY + "/" + SubmissionCCLicenseUrlRest.NAME + "/search", + SubmissionCCLicenseUrlRest.NAME + "-search"))); + } + } From 8cadd105468132611f42c23f94b813ad1b22e90c Mon Sep 17 00:00:00 2001 From: Kevin Van de Velde Date: Thu, 25 Jun 2020 10:56:20 +0200 Subject: [PATCH 66/73] [CC License] Fixing checkstyle issues --- .../org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java index cbfbd347b27c..957484319cbf 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/SubmissionCCLicenseUrlRepository.java @@ -132,7 +132,8 @@ public Class getDomainClass() { @Override public void afterPropertiesSet() { discoverableEndpointsService.register(this, Arrays.asList( - new Link("/api/" + SubmissionCCLicenseUrlRest.CATEGORY + "/" + SubmissionCCLicenseUrlRest.NAME + "/search", + new Link("/api/" + SubmissionCCLicenseUrlRest.CATEGORY + "/" + + SubmissionCCLicenseUrlRest.NAME + "/search", SubmissionCCLicenseUrlRest.NAME + "-search"))); } From 1e49e5877e53dce056ec1b4022a5e026dc70f6a1 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Thu, 25 Jun 2020 12:33:37 +0200 Subject: [PATCH 67/73] Fix injection to avoid issue with docker/double references --- ...tstreamMetadataValueAddPatchOperation.java | 6 ++++- ...streamMetadataValueMovePatchOperation.java | 5 ++++- ...reamMetadataValueRemovePatchOperation.java | 5 ++++- ...eamMetadataValueReplacePatchOperation.java | 6 ++++- .../spring/spring-dspace-core-services.xml | 22 +++++++++++++------ 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java index c7531e981029..447f715c12f8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java @@ -40,7 +40,7 @@ public class BitstreamMetadataValueAddPatchOperation extends MetadataValueAddPat @Autowired ItemService itemService; - @Autowired + // this is wired in the pring-dspace-core-services.xml BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; @Override @@ -103,4 +103,8 @@ void add(Context context, Request currentRequest, InProgressSubmission source, S protected BitstreamService getDSpaceObjectService() { return bitstreamService; } + + public void setBitstreamMetadataValuePathUtils(BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils) { + this.bitstreamMetadataValuePathUtils = bitstreamMetadataValuePathUtils; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java index d03d47c91eb5..f0e764f9709c 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueMovePatchOperation.java @@ -36,7 +36,7 @@ public class BitstreamMetadataValueMovePatchOperation extends MetadataValueMoveP @Autowired ItemService itemService; - @Autowired + // this is wired in the pring-dspace-core-services.xml BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; @Override @@ -78,4 +78,7 @@ protected BitstreamService getDSpaceObjectService() { return bitstreamService; } + public void setBitstreamMetadataValuePathUtils(BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils) { + this.bitstreamMetadataValuePathUtils = bitstreamMetadataValuePathUtils; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java index 0341192b21bc..17de738637de 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueRemovePatchOperation.java @@ -36,7 +36,7 @@ public class BitstreamMetadataValueRemovePatchOperation extends MetadataValueRem @Autowired ItemService itemService; - @Autowired + // this is wired in the pring-dspace-core-services.xml BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; @Override @@ -73,4 +73,7 @@ protected BitstreamService getDSpaceObjectService() { return bitstreamService; } + public void setBitstreamMetadataValuePathUtils(BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils) { + this.bitstreamMetadataValuePathUtils = bitstreamMetadataValuePathUtils; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java index fd5b39515710..d9bcba5ef12d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueReplacePatchOperation.java @@ -39,7 +39,7 @@ public class BitstreamMetadataValueReplacePatchOperation extends MetadataValueRe @Autowired ItemService itemService; - @Autowired + // this is wired in the pring-dspace-core-services.xml BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils; @Override @@ -86,4 +86,8 @@ private void replace(Context context, Bitstream b, String[] split, Object value) protected BitstreamService getDSpaceObjectService() { return bitstreamService; } + + public void setBitstreamMetadataValuePathUtils(BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils) { + this.bitstreamMetadataValuePathUtils = bitstreamMetadataValuePathUtils; + } } diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml index 0358049b18c5..fb09e305befe 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml @@ -25,7 +25,9 @@ + class="org.dspace.app.rest.submit.factory.impl.BitstreamMetadataValueMovePatchOperation"> + + + class="org.dspace.app.rest.submit.factory.impl.BitstreamMetadataValueAddPatchOperation"> + + + class="org.dspace.app.rest.submit.factory.impl.ItemMetadataValueRemovePatchOperation" /> + class="org.dspace.app.rest.submit.factory.impl.BitstreamMetadataValueRemovePatchOperation"> + + + class="org.dspace.app.rest.submit.factory.impl.BitstreamRemovePatchOperation" /> + class="org.dspace.app.rest.submit.factory.impl.BitstreamMetadataValueReplacePatchOperation"> + + - + From 06f9eb521c98fad98c5c7a6e1f4d31226e6c49e4 Mon Sep 17 00:00:00 2001 From: Andrea Bollini Date: Thu, 25 Jun 2020 14:02:00 +0200 Subject: [PATCH 68/73] Fix checkstyle issue --- .../factory/impl/BitstreamMetadataValueAddPatchOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java index 447f715c12f8..4df55c7d6058 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/BitstreamMetadataValueAddPatchOperation.java @@ -103,7 +103,7 @@ void add(Context context, Request currentRequest, InProgressSubmission source, S protected BitstreamService getDSpaceObjectService() { return bitstreamService; } - + public void setBitstreamMetadataValuePathUtils(BitstreamMetadataValuePathUtils bitstreamMetadataValuePathUtils) { this.bitstreamMetadataValuePathUtils = bitstreamMetadataValuePathUtils; } From 32a03eed36a9fbe08e44d0bc6a945a4b5853a8d6 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Thu, 25 Jun 2020 16:17:36 +0200 Subject: [PATCH 69/73] [Task 71604] moved RegistrationRestControllerIT tests to RegistrationRestRepositoryIT and added extra cleanup --- .../rest/RegistrationRestControllerIT.java | 108 ------------------ .../rest/RegistrationRestRepositoryIT.java | 81 +++++++++++++ 2 files changed, 81 insertions(+), 108 deletions(-) delete mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java deleted file mode 100644 index d02dceea20fe..000000000000 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestControllerIT.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * 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; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import java.util.Iterator; -import java.util.List; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.lang3.StringUtils; -import org.dspace.app.rest.model.RegistrationRest; -import org.dspace.app.rest.test.AbstractControllerIntegrationTest; -import org.dspace.eperson.RegistrationData; -import org.dspace.eperson.dao.RegistrationDataDAO; -import org.dspace.services.ConfigurationService; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; - -public class RegistrationRestControllerIT extends AbstractControllerIntegrationTest { - - @Autowired - private RegistrationDataDAO registrationDataDAO; - - @Autowired - private ConfigurationService configurationService; - - @Test - public void registrationFlowTest() throws Exception { - List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(0, registrationDataList.size()); - - ObjectMapper mapper = new ObjectMapper(); - RegistrationRest registrationRest = new RegistrationRest(); - registrationRest.setEmail(eperson.getEmail()); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().isCreated()); - registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(1, registrationDataList.size()); - assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); - - String newEmail = "newEPersonTest@gmail.com"; - registrationRest.setEmail(newEmail); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().isCreated()); - registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationDataList.size() == 2); - assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) || - StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); - configurationService.setProperty("user.registration", false); - - newEmail = "newEPersonTestTwo@gmail.com"; - registrationRest.setEmail(newEmail); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().is(401)); - - assertEquals(2, registrationDataList.size()); - assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && - !StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); - - Iterator iterator = registrationDataList.iterator(); - while (iterator.hasNext()) { - RegistrationData registrationData = iterator.next(); - registrationDataDAO.delete(context, registrationData); - } - } - - @Test - public void forgotPasswordTest() throws Exception { - context.turnOffAuthorisationSystem(); - configurationService.setProperty("user.registration", false); - context.restoreAuthSystemState(); - - List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(0, registrationDataList.size()); - - ObjectMapper mapper = new ObjectMapper(); - RegistrationRest registrationRest = new RegistrationRest(); - registrationRest.setEmail(eperson.getEmail()); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().isCreated()); - registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(1, registrationDataList.size()); - assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); - Iterator iterator = registrationDataList.iterator(); - while (iterator.hasNext()) { - RegistrationData registrationData = iterator.next(); - registrationDataDAO.delete(context, registrationData); - } - context.complete(); - } -} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java index 88feebb2862d..2709042cbd08 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java @@ -7,19 +7,24 @@ */ package org.dspace.app.rest; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.Iterator; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.matcher.RegistrationMatcher; import org.dspace.app.rest.model.RegistrationRest; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.eperson.RegistrationData; import org.dspace.eperson.dao.RegistrationDataDAO; +import org.dspace.services.ConfigurationService; import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -29,6 +34,9 @@ public class RegistrationRestRepositoryIT extends AbstractControllerIntegrationT @Autowired private RegistrationDataDAO registrationDataDAO; + @Autowired + private ConfigurationService configurationService; + @Test public void findByTokenTestExistingUserTest() throws Exception { String email = eperson.getEmail(); @@ -41,6 +49,8 @@ public void findByTokenTestExistingUserTest() throws Exception { .andExpect( jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, eperson.getID())))); + registrationDataDAO.delete(context, registrationData); + email = "newUser@testnewuser.com"; createTokenForEmail(email); registrationData = registrationDataDAO.findByEmail(context, email); @@ -87,4 +97,75 @@ private void createTokenForEmail(String email) throws Exception { .contentType(contentType)) .andExpect(status().isCreated()); } + + @Test + public void registrationFlowTest() throws Exception { + List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(0, registrationDataList.size()); + + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(eperson.getEmail()); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(1, registrationDataList.size()); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); + + String newEmail = "newEPersonTest@gmail.com"; + registrationRest.setEmail(newEmail); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationDataList.size() == 2); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) || + StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); + configurationService.setProperty("user.registration", false); + + newEmail = "newEPersonTestTwo@gmail.com"; + registrationRest.setEmail(newEmail); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().is(401)); + + assertEquals(2, registrationDataList.size()); + assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && + !StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); + + Iterator iterator = registrationDataList.iterator(); + while (iterator.hasNext()) { + RegistrationData registrationData = iterator.next(); + registrationDataDAO.delete(context, registrationData); + } + } + + @Test + public void forgotPasswordTest() throws Exception { + configurationService.setProperty("user.registration", false); + + List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(0, registrationDataList.size()); + + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(eperson.getEmail()); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(1, registrationDataList.size()); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); + Iterator iterator = registrationDataList.iterator(); + while (iterator.hasNext()) { + RegistrationData registrationData = iterator.next(); + registrationDataDAO.delete(context, registrationData); + } + } + } From 21e96f937c77bba576db67bb19fd93acabb8399a Mon Sep 17 00:00:00 2001 From: Mykhaylo Date: Thu, 25 Jun 2020 19:09:25 +0200 Subject: [PATCH 70/73] added missing parameter --- .../java/org/dspace/app/rest/submit/step/CCLicenseStep.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java index 75f194911655..3f8ed6a22fb8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/step/CCLicenseStep.java @@ -51,8 +51,8 @@ public DataCCLicense getData(SubmissionService submissionService, InProgressSubm * @throws Exception */ @Override - public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op) - throws Exception { + public void doPatchProcessing(Context context, Request currentRequest, InProgressSubmission source, Operation op, + SubmissionStepConfig stepConf) throws Exception { if (op.getPath().endsWith(CCLICENSE_STEP_OPERATION_ENTRY)) { From 22678a7ef54984198e9ee08623ccdfc84ff96887 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 26 Jun 2020 09:41:50 -0500 Subject: [PATCH 71/73] Configure LGTM --- .lgtm.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .lgtm.yml diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 000000000000..132de8a6de5a --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,9 @@ +# LGTM Settings (https://lgtm.com/) +# For reference, see https://lgtm.com/help/lgtm/lgtm.yml-configuration-file +# or template at https://lgtm.com/static/downloads/lgtm.template.yml + +extraction: + java: + index: + # Specify the Java version required to build the project + java_version: 11 From 0555644c9a8d1c15bbe71afb13068ba632eb6f9d Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 30 Jun 2020 08:30:01 +0200 Subject: [PATCH 72/73] [Task 71627] addressed feedback on the new registration functionaliity --- .../repository/EPersonRestRepository.java | 2 +- .../app/rest/EPersonRestRepositoryIT.java | 225 ++++++++++-------- .../rest/RegistrationRestRepositoryIT.java | 155 ++++++------ 3 files changed, 208 insertions(+), 174 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index b57df5f73dc3..e4a1ca1389fc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -199,7 +199,7 @@ private void checkRequiredProperties(EPersonRest epersonRest) { List epersonLastName = metadataRest.getMap().get("eperson.lastname"); if (epersonFirstName == null || epersonLastName == null || epersonFirstName.isEmpty() || epersonLastName.isEmpty()) { - throw new DSpaceBadRequestException("The eperson.firstname and eperson.lastname values need to be " + + throw new UnprocessableEntityException("The eperson.firstname and eperson.lastname values need to be " + "filled in"); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java index b681d5fe0deb..d2b599e47719 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/EPersonRestRepositoryIT.java @@ -2044,22 +2044,24 @@ public void registerNewAccountPatchUpdatePasswordRandomUserUuidFail() throws Exc accountService.sendRegistrationInfo(context, ePerson.getEmail()); String newRegisterToken = registrationDataService.findByEmail(context, newRegisterEmail).getToken(); PasswordHash oldPassword = ePersonService.getPasswordHash(ePerson); - // updates password - getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) - .content(patchBody) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) - .param("token", newRegisterToken)) - .andExpect(status().isUnauthorized()); - - PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); - assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); - assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); - assertFalse(registrationDataService.findByEmail(context, newRegisterEmail) == null); + try { + // updates password + getClient().perform(patch("/api/eperson/epersons/" + ePerson.getID()) + .content(patchBody) + .contentType(MediaType.APPLICATION_JSON_PATCH_JSON) + .param("token", newRegisterToken)) + .andExpect(status().isUnauthorized()); - context.turnOffAuthorisationSystem(); - registrationDataService.delete(context, registrationDataService.findByEmail(context, ePerson.getEmail())); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); + PasswordHash newPasswordHash = ePersonService.getPasswordHash(ePerson); + assertTrue(StringUtils.equalsIgnoreCase(oldPassword.getHashString(),newPasswordHash.getHashString())); + assertFalse(registrationDataService.findByEmail(context, ePerson.getEmail()) == null); + assertFalse(registrationDataService.findByEmail(context, newRegisterEmail) == null); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.delete(context, registrationDataService.findByEmail(context, ePerson.getEmail())); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } } @Test @@ -2118,10 +2120,10 @@ public void postEPersonWithTokenWithoutEmailProperty() throws Exception { assertNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, newRegisterToken); context.restoreAuthSystemState(); - } finally { EPersonBuilder.deleteEPerson(idRef.get()); } } @@ -2179,10 +2181,10 @@ public void postEPersonWithTokenWithEmailProperty() throws Exception { assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); assertNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, newRegisterToken); context.restoreAuthSystemState(); - } finally { EPersonBuilder.deleteEPerson(idRef.get()); } @@ -2245,10 +2247,10 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredProperty() throws Exce assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); assertNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, newRegisterToken); context.restoreAuthSystemState(); - } finally { EPersonBuilder.deleteEPerson(idRef.get()); } @@ -2293,21 +2295,24 @@ public void postEPersonWithTokenWithTwoTokensDifferentEmailProperty() throws Exc mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); - EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmailTwo); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); - assertNotNull(registrationDataService.findByToken(context, newRegisterTokenTwo)); + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmailTwo); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + assertNotNull(registrationDataService.findByToken(context, newRegisterTokenTwo)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + registrationDataService.deleteByToken(context, newRegisterTokenTwo); + context.restoreAuthSystemState(); - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - registrationDataService.deleteByToken(context, newRegisterTokenTwo); - context.restoreAuthSystemState(); + } } @Test @@ -2340,19 +2345,22 @@ public void postEPersonWithRandomTokenWithEmailProperty() throws Exception { mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", "randomToken") - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", "randomToken") + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); - EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); } @Test @@ -2386,19 +2394,22 @@ public void postEPersonWithTokenWithEmailAndSelfRegisteredFalseProperty() throws mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); - EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); } @Test @@ -2429,19 +2440,22 @@ public void postEPersonWithTokenWithoutLastNameProperty() throws Exception { mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); - - EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnprocessableEntity()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); } @Test @@ -2472,19 +2486,22 @@ public void postEPersonWithTokenWithoutFirstNameProperty() throws Exception { mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); - - EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnprocessableEntity()); + + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); } @Test @@ -2516,19 +2533,21 @@ public void postEPersonWithTokenWithoutPasswordProperty() throws Exception { mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", newRegisterToken) - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); - - EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", newRegisterToken) + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, newRegisterToken); - context.restoreAuthSystemState(); + EPerson createdEPerson = ePersonService.findByEmail(context, newRegisterEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, newRegisterToken)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, newRegisterToken); + context.restoreAuthSystemState(); + } } @@ -2562,19 +2581,22 @@ public void postEPersonWithWrongToken() throws Exception { mapper.setAnnotationIntrospector(new IgnoreJacksonWriteOnlyAccess()); - getClient().perform(post("/api/eperson/epersons") - .param("token", forgotPasswordToken) - .content(mapper.writeValueAsBytes(ePersonRest)) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isBadRequest()); + try { + getClient().perform(post("/api/eperson/epersons") + .param("token", forgotPasswordToken) + .content(mapper.writeValueAsBytes(ePersonRest)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); - EPerson createdEPerson = ePersonService.findByEmail(context, newEmail); - assertNull(createdEPerson); - assertNotNull(registrationDataService.findByToken(context, forgotPasswordToken)); + EPerson createdEPerson = ePersonService.findByEmail(context, newEmail); + assertNull(createdEPerson); + assertNotNull(registrationDataService.findByToken(context, forgotPasswordToken)); + } finally { + context.turnOffAuthorisationSystem(); + registrationDataService.deleteByToken(context, forgotPasswordToken); + context.restoreAuthSystemState(); + } - context.turnOffAuthorisationSystem(); - registrationDataService.deleteByToken(context, forgotPasswordToken); - context.restoreAuthSystemState(); } @@ -2633,11 +2655,10 @@ public void postEPersonWithTokenWithEmailPropertyAnonUser() throws Exception { EPerson createdEPerson = ePersonService.find(context, UUID.fromString(epersonUuid)); assertTrue(ePersonService.checkPassword(context, createdEPerson, "somePassword")); assertNull(registrationDataService.findByToken(context, newRegisterToken)); - + } finally { context.turnOffAuthorisationSystem(); registrationDataService.deleteByToken(context, newRegisterToken); context.restoreAuthSystemState(); - } finally { EPersonBuilder.deleteEPerson(idRef.get()); } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java index 2709042cbd08..1c3ae583740e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/RegistrationRestRepositoryIT.java @@ -16,6 +16,7 @@ import java.util.Iterator; import java.util.List; +import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.StringUtils; @@ -43,25 +44,28 @@ public void findByTokenTestExistingUserTest() throws Exception { createTokenForEmail(email); RegistrationData registrationData = registrationDataDAO.findByEmail(context, email); - getClient().perform(get("/api/eperson/registrations/search/findByToken") - .param("token", registrationData.getToken())) - .andExpect(status().isOk()) - .andExpect( - jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, eperson.getID())))); + try { + getClient().perform(get("/api/eperson/registrations/search/findByToken") + .param("token", registrationData.getToken())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, eperson.getID())))); - registrationDataDAO.delete(context, registrationData); + registrationDataDAO.delete(context, registrationData); - email = "newUser@testnewuser.com"; - createTokenForEmail(email); - registrationData = registrationDataDAO.findByEmail(context, email); + email = "newUser@testnewuser.com"; + createTokenForEmail(email); + registrationData = registrationDataDAO.findByEmail(context, email); - getClient().perform(get("/api/eperson/registrations/search/findByToken") - .param("token", registrationData.getToken())) - .andExpect(status().isOk()) - .andExpect( - jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, null)))); + getClient().perform(get("/api/eperson/registrations/search/findByToken") + .param("token", registrationData.getToken())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, null)))); + } finally { + registrationDataDAO.delete(context, registrationData); + } - registrationDataDAO.delete(context, registrationData); } @@ -71,13 +75,16 @@ public void findByTokenTestNewUserTest() throws Exception { createTokenForEmail(email); RegistrationData registrationData = registrationDataDAO.findByEmail(context, email); - getClient().perform(get("/api/eperson/registrations/search/findByToken") - .param("token", registrationData.getToken())) - .andExpect(status().isOk()) - .andExpect( - jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, null)))); + try { + getClient().perform(get("/api/eperson/registrations/search/findByToken") + .param("token", registrationData.getToken())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", Matchers.is(RegistrationMatcher.matchRegistration(email, null)))); + } finally { + registrationDataDAO.delete(context, registrationData); + } - registrationDataDAO.delete(context, registrationData); } @Test @@ -106,41 +113,44 @@ public void registrationFlowTest() throws Exception { ObjectMapper mapper = new ObjectMapper(); RegistrationRest registrationRest = new RegistrationRest(); registrationRest.setEmail(eperson.getEmail()); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().isCreated()); - registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(1, registrationDataList.size()); - assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); - - String newEmail = "newEPersonTest@gmail.com"; - registrationRest.setEmail(newEmail); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().isCreated()); - registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertTrue(registrationDataList.size() == 2); - assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) || - StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); - configurationService.setProperty("user.registration", false); - newEmail = "newEPersonTestTwo@gmail.com"; - registrationRest.setEmail(newEmail); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().is(401)); - - assertEquals(2, registrationDataList.size()); - assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && - !StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); - - Iterator iterator = registrationDataList.iterator(); - while (iterator.hasNext()) { - RegistrationData registrationData = iterator.next(); - registrationDataDAO.delete(context, registrationData); + try { + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(1, registrationDataList.size()); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); + + String newEmail = "newEPersonTest@gmail.com"; + registrationRest.setEmail(newEmail); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertTrue(registrationDataList.size() == 2); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) || + StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); + configurationService.setProperty("user.registration", false); + + newEmail = "newEPersonTestTwo@gmail.com"; + registrationRest.setEmail(newEmail); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().is(HttpServletResponse.SC_UNAUTHORIZED)); + + assertEquals(2, registrationDataList.size()); + assertTrue(!StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), newEmail) && + !StringUtils.equalsIgnoreCase(registrationDataList.get(1).getEmail(), newEmail)); + } finally { + Iterator iterator = registrationDataList.iterator(); + while (iterator.hasNext()) { + RegistrationData registrationData = iterator.next(); + registrationDataDAO.delete(context, registrationData); + } } } @@ -149,22 +159,25 @@ public void forgotPasswordTest() throws Exception { configurationService.setProperty("user.registration", false); List registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(0, registrationDataList.size()); - - ObjectMapper mapper = new ObjectMapper(); - RegistrationRest registrationRest = new RegistrationRest(); - registrationRest.setEmail(eperson.getEmail()); - getClient().perform(post("/api/eperson/registrations") - .content(mapper.writeValueAsBytes(registrationRest)) - .contentType(contentType)) - .andExpect(status().isCreated()); - registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); - assertEquals(1, registrationDataList.size()); - assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); - Iterator iterator = registrationDataList.iterator(); - while (iterator.hasNext()) { - RegistrationData registrationData = iterator.next(); - registrationDataDAO.delete(context, registrationData); + try { + assertEquals(0, registrationDataList.size()); + + ObjectMapper mapper = new ObjectMapper(); + RegistrationRest registrationRest = new RegistrationRest(); + registrationRest.setEmail(eperson.getEmail()); + getClient().perform(post("/api/eperson/registrations") + .content(mapper.writeValueAsBytes(registrationRest)) + .contentType(contentType)) + .andExpect(status().isCreated()); + registrationDataList = registrationDataDAO.findAll(context, RegistrationData.class); + assertEquals(1, registrationDataList.size()); + assertTrue(StringUtils.equalsIgnoreCase(registrationDataList.get(0).getEmail(), eperson.getEmail())); + } finally { + Iterator iterator = registrationDataList.iterator(); + while (iterator.hasNext()) { + RegistrationData registrationData = iterator.next(); + registrationDataDAO.delete(context, registrationData); + } } } From 2a6731297cc8c7c7f1387cf214800e42a9672071 Mon Sep 17 00:00:00 2001 From: Raf Ponsaerts Date: Tue, 30 Jun 2020 10:27:47 +0200 Subject: [PATCH 73/73] Fixed LGTM issue with an error String --- .../org/dspace/app/rest/repository/EPersonRestRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java index e4a1ca1389fc..a312fd4f28f7 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/EPersonRestRepository.java @@ -164,7 +164,7 @@ private EPersonRest createAndReturn(Context context, EPersonRest epersonRest, St } if (es.findByEmail(context, registrationData.getEmail()) != null) { throw new DSpaceBadRequestException("The token given already contains an email address that resolves" + - "to an eperson"); + " to an eperson"); } String emailFromJson = epersonRest.getEmail(); if (StringUtils.isNotBlank(emailFromJson)) {