diff --git a/dspace-api/pom.xml b/dspace-api/pom.xml index 6ac36d129ade..9cba7263632d 100644 --- a/dspace-api/pom.xml +++ b/dspace-api/pom.xml @@ -859,23 +859,6 @@ - - io.findify - s3mock_2.13 - 0.2.6 - test - - - com.amazonawsl - aws-java-sdk-s3 - - - com.amazonaws - aws-java-sdk-s3 - - - - diff --git a/dspace-api/src/main/java/org/dspace/authorize/AuthorizationBitstreamUtils.java b/dspace-api/src/main/java/org/dspace/authorize/AuthorizationBitstreamUtils.java index 1b98556ecad0..0f7dc8eaa285 100644 --- a/dspace-api/src/main/java/org/dspace/authorize/AuthorizationBitstreamUtils.java +++ b/dspace-api/src/main/java/org/dspace/authorize/AuthorizationBitstreamUtils.java @@ -7,6 +7,8 @@ */ package org.dspace.authorize; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; + import java.sql.SQLException; import java.util.List; import java.util.Objects; @@ -117,10 +119,10 @@ public boolean authorizeLicenseWithUser(Context context, UUID bitstreamID) throw // Bitstream should have only one type of the Clarin license, so we could get first record ClarinLicense clarinLicense = Objects.requireNonNull(clarinLicenseResourceMappings.get(0)).getLicense(); - // 3 - Allow download for anonymous users, but with license confirmation - // 0 - License confirmation is not required - if (Objects.equals(clarinLicense.getConfirmation(), 3) || - Objects.equals(clarinLicense.getConfirmation(), 0)) { + // ALLOW_ANONYMOUS - Allow download for anonymous users, but with license confirmation + // NOT_REQUIRED - License confirmation is not required + if ((clarinLicense.getConfirmation() == Confirmation.ALLOW_ANONYMOUS) || + (clarinLicense.getConfirmation() == Confirmation.NOT_REQUIRED)) { return true; } return false; diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java index 71500d00ecad..bc592507d538 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicense.java @@ -16,6 +16,8 @@ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -83,7 +85,8 @@ public class ClarinLicense implements ReloadableEntity { private String definition = null; @Column(name = "confirmation") - private Integer confirmation = 0; + @Enumerated(EnumType.ORDINAL) + private Confirmation confirmation = Confirmation.NOT_REQUIRED; @Column(name = "required_info") private String requiredInfo = null; @@ -111,11 +114,11 @@ public void setDefinition(String definition) { this.definition = definition; } - public Integer getConfirmation() { - return confirmation; + public Confirmation getConfirmation() { + return confirmation == null ? Confirmation.NOT_REQUIRED : confirmation; } - public void setConfirmation(Integer confirmation) { + public void setConfirmation(Confirmation confirmation) { this.confirmation = confirmation; } @@ -191,4 +194,29 @@ public ClarinUserRegistration getEperson() { public void setEperson(ClarinUserRegistration eperson) { this.eperson = eperson; } + + public enum Confirmation { + + // if new Confirmation value is needed, add it to the end of this list, to not break the backward compatibility + NOT_REQUIRED(0), ASK_ONLY_ONCE(1), ASK_ALWAYS(2), ALLOW_ANONYMOUS(3); + + private final int value; + + Confirmation(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Confirmation getConfirmation(int value) { + return Arrays.stream(values()) + .filter(v -> (v.getValue() == value)) + .findFirst() + .orElseThrow(() -> + new IllegalArgumentException("No license confirmation found for value: " + value)); + } + + } } diff --git a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseResourceMappingServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseResourceMappingServiceImpl.java index 82c8b2839bd5..bc5d1884564e 100644 --- a/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseResourceMappingServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/clarin/ClarinLicenseResourceMappingServiceImpl.java @@ -7,6 +7,8 @@ */ package org.dspace.content.clarin; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; + import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -203,23 +205,23 @@ public ClarinLicense getLicenseToAgree(Context context, UUID userId, UUID resour } // Confirmation states: - // 0 - Not required - // 1 - Ask only once - // 2 - Ask always - // 3 - Allow anonymous - if (Objects.equals(clarinLicenseToAgree.getConfirmation(), 0)) { + // NOT_REQUIRED + // ASK_ONLY_ONCE + // ASK_ALWAYS + // ALLOW_ANONYMOUS + if (clarinLicenseToAgree.getConfirmation() == Confirmation.NOT_REQUIRED) { return null; } switch (clarinLicenseToAgree.getConfirmation()) { - case 1: + case ASK_ONLY_ONCE: // Ask only once - check if the clarin license required info is filled in by the user if (userFilledInRequiredInfo(context, clarinLicenseResourceMapping, userId)) { return null; } return clarinLicenseToAgree; - case 2: - case 3: + case ASK_ALWAYS: + case ALLOW_ANONYMOUS: return clarinLicenseToAgree; default: return null; diff --git a/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java b/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java index 8aaae6d8bd5d..126e7e6e7bf0 100644 --- a/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/handle/HandleClarinServiceImpl.java @@ -337,6 +337,11 @@ public DSpaceObject resolveToObject(Context context, String handle) throws Illeg + Constants.typeText[handleTypeId]); } + @Override + public int count(Context context) throws SQLException { + return handleDAO.countRows(context); + } + /** * Create id for handle object. * @@ -457,6 +462,21 @@ public Handle createHandle(Context context, String handleStr) throws SQLExceptio return handle; } + @Override + public Handle findByHandleAndMagicToken(Context context, String handle, String token) throws SQLException { + Handle h = findByHandle(context, handle); + if (Objects.isNull(h) || Objects.isNull(h.getUrl()) || !h.getUrl().contains(MAGIC_BEAN)) { + return null; + } + org.dspace.handle.external.Handle magicHandle = + new org.dspace.handle.external.Handle(h.getHandle(), h.getUrl()); + if (magicHandle.token.equals(token)) { + return h; + } else { + return null; + } + } + /** * Strips the part identifier from the handle * diff --git a/dspace-api/src/main/java/org/dspace/handle/external/Handle.java b/dspace-api/src/main/java/org/dspace/handle/external/Handle.java index 5f34012b7173..e4ff31cdcc71 100644 --- a/dspace-api/src/main/java/org/dspace/handle/external/Handle.java +++ b/dspace-api/src/main/java/org/dspace/handle/external/Handle.java @@ -87,20 +87,20 @@ public Handle(String handle, String magicURL) { } /** - * From the attributes generate the url with `@magicLindat` string + * Generate new token and combine the properties into the url with `@magicLindat` string * @return url with the `@magicLindat` string */ - public String getMagicUrl() { - return this.getMagicUrl(this.title, this.submitdate, this.reportemail, this.datasetName, this.datasetVersion, + public String generateMagicUrl() { + return generateMagicUrl(this.title, this.submitdate, this.reportemail, this.datasetName, this.datasetVersion, this.query, this.url); } /** - * From the attributes generate the url with `@magicLindat` string + * Generate new token and combine the params into the url with `@magicLindat` string * @return url with the `@magicLindat` string */ - public String getMagicUrl(String title, String submitdate, String reportemail, String datasetName, - String datasetVersion, String query, String url) { + private static String generateMagicUrl(String title, String submitdate, String reportemail, String datasetName, + String datasetVersion, String query, String url) { String magicURL = ""; String token = UUID.randomUUID().toString(); String[] magicURLProps = new String[] {title, HandlePlugin.getRepositoryName(), submitdate, reportemail, diff --git a/dspace-api/src/main/java/org/dspace/handle/service/HandleClarinService.java b/dspace-api/src/main/java/org/dspace/handle/service/HandleClarinService.java index 3cc4fb60f46e..ab0ffac99b34 100644 --- a/dspace-api/src/main/java/org/dspace/handle/service/HandleClarinService.java +++ b/dspace-api/src/main/java/org/dspace/handle/service/HandleClarinService.java @@ -156,6 +156,14 @@ public void update(Context context, Handle handleObject, String newHandle, */ public DSpaceObject resolveToObject(Context context, String handle) throws IllegalStateException, SQLException; + /** + * Return the number of entries in the handle table. + * @param context + * @return number of rows in the handle table + * @throws SQLException + */ + int count(Context context) throws SQLException; + /** * Create the external handles from the list of handles with magic URL * @@ -226,4 +234,15 @@ public void update(Context context, Handle handleObject, String newHandle, * @throws AuthorizeException if authorization error */ public Handle createHandle(Context context, String handle) throws SQLException, AuthorizeException; + + /** + * Returns a handle entity matching the provided `prefix/suffix` but only when the "magic url" + * contains the provided token. + * @param context + * @param handle prefix/suffix + * @param token the automatically generated part of the magic URL + * @return Handle entity or null (if the handle is not found or the "magic url" does not contain the provided token) + * @throws SQLException + */ + Handle findByHandleAndMagicToken(Context context, String handle, String token) throws SQLException; } diff --git a/dspace-api/src/test/java/org/dspace/content/BundleClarinTest.java b/dspace-api/src/test/java/org/dspace/content/BundleClarinTest.java index eedbd2db715e..36ddfafa1679 100644 --- a/dspace-api/src/test/java/org/dspace/content/BundleClarinTest.java +++ b/dspace-api/src/test/java/org/dspace/content/BundleClarinTest.java @@ -7,6 +7,7 @@ */ package org.dspace.content; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.dspace.core.Constants.CONTENT_BUNDLE_NAME; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -119,7 +120,7 @@ public void init() { this.clarinLicense.setLicenseLabels(cllSet); this.clarinLicense.setName(LICENSE_NAME); this.clarinLicense.setDefinition(LICENSE_URI); - this.clarinLicense.setConfirmation(0); + this.clarinLicense.setConfirmation(Confirmation.NOT_REQUIRED); this.clarinLicenseService.update(context, this.clarinLicense); // initialize second clarin license and clarin license label @@ -139,7 +140,7 @@ public void init() { this.secondClarinLicense.setLicenseLabels(secondCllSet); this.secondClarinLicense.setName("wrong name"); this.secondClarinLicense.setDefinition("wrong uri"); - this.secondClarinLicense.setConfirmation(0); + this.secondClarinLicense.setConfirmation(Confirmation.NOT_REQUIRED); this.clarinLicenseService.update(context, this.secondClarinLicense); //we need to commit the changes, so we don't block the table for testing diff --git a/dspace-server-webapp/pom.xml b/dspace-server-webapp/pom.xml index d6442a57174a..d55c28030ca6 100644 --- a/dspace-server-webapp/pom.xml +++ b/dspace-server-webapp/pom.xml @@ -290,12 +290,6 @@ ${spring-boot.version} - - org.springframework.boot - spring-boot-starter-actuator - ${spring-boot.version} - - com.flipkart.zjsonpatch zjsonpatch diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java index 74f28bbfd19c..5b1a2fdaa520 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ItemAddBundleController.java @@ -16,11 +16,11 @@ import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.BadRequestException; import com.fasterxml.jackson.databind.ObjectMapper; import org.dspace.app.rest.converter.ConverterService; import org.dspace.app.rest.converter.MetadataConverter; +import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.BundleRest; import org.dspace.app.rest.model.ItemRest; @@ -156,7 +156,7 @@ public ItemRest updateLicenseForBundle(@PathVariable UUID uuid, throws SQLException, AuthorizeException { Context context = ContextUtil.obtainContext(request); if (Objects.isNull(context)) { - throw new BadRequestException("No context found for current user"); + throw new DSpaceBadRequestException("No context found for current user"); } Item item = itemService.find(context, uuid); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinLicenseConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinLicenseConverter.java index 87abebaa2b15..77325827bb1f 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinLicenseConverter.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/ClarinLicenseConverter.java @@ -41,7 +41,7 @@ public ClarinLicenseRest convert(ClarinLicense modelObject, Projection projectio license.setProjection(projection); license.setId(modelObject.getID()); license.setName(modelObject.getName()); - license.setConfirmation(modelObject.getConfirmation()); + license.setConfirmation(modelObject.getConfirmation().getValue()); license.setDefinition(modelObject.getDefinition()); license.setRequiredInfo(modelObject.getRequiredInfo()); setExtendedClarinLicenseLabels(license, modelObject.getLicenseLabels(), projection); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index 8640f64a7f8d..4833cb938317 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -246,24 +246,67 @@ protected void handleGenericException(HttpServletRequest request, HttpServletRes } else { returnCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; } - sendErrorResponse(request, response, ex, "An exception has occurred", returnCode); + if ((HttpStatus.valueOf(returnCode).is4xxClientError())) { + sendErrorResponseFromException(request, response, ex, returnCode); + } else { + sendErrorResponse(request, response, ex, "An exception has occurred", returnCode); + } } + /** + * Send the error to the response. The error message is taken from exception message. + * This method is mainly appropriate for 4xx errors, + * where the exception message should be exposed to REST API clients in response body. + * This method also logs the ERROR log, containing the first line from the exception stack trace. + * Specific errors where an ERROR log with full stack trace is more appropriate are configured + * using property {@code logging.server.include-stacktrace-for-httpcode} + * (see {@link DSpaceApiExceptionControllerAdvice#P_LOG_AS_ERROR} + * and {@link DSpaceApiExceptionControllerAdvice#LOG_AS_ERROR_DEFAULT}). + * + * @param request current request + * @param response current response + * @param ex Exception causing the error response + * @param statusCode status code to send in response + */ + private void sendErrorResponseFromException(final HttpServletRequest request, + final HttpServletResponse response, + final Exception ex, + final int statusCode) + throws IOException { + //Make sure Spring picks up this exception + request.setAttribute(EXCEPTION_ATTRIBUTE, ex); + + final Set statusCodesLoggedAsErrors = getStatusCodesLoggedAsErrors(); + + String message = ex.getMessage(); + if (statusCodesLoggedAsErrors.contains(statusCode)) { + log.error("{} (status:{})", message, statusCode, ex); + } else { + // Log the error as a single-line WARN + StackTraceElement[] trace = ex.getStackTrace(); + String location = trace.length <= 0 ? "unknown" : trace[0].toString(); + log.warn("{} (status:{} exception: {} at: {})", + message, statusCode, ex.getClass().getName(), location); + } + + response.sendError(statusCode, message); + } + /** * Send the error to the response. * 5xx errors will be logged as ERROR with a full stack trace. 4xx errors * will be logged as WARN without a stack trace. Specific 4xx errors where * an ERROR log with full stack trace is more appropriate are configured * using property {@code logging.server.include-stacktrace-for-httpcode} - * (see {@link P_LOG_AS_ERROR} and {@link LOG_AS_ERROR_DEFAULT}). + * (see {@link DSpaceApiExceptionControllerAdvice#P_LOG_AS_ERROR} + * and {@link DSpaceApiExceptionControllerAdvice#LOG_AS_ERROR_DEFAULT}). * * @param request current request * @param response current response * @param ex Exception thrown * @param message message to log or send in response * @param statusCode status code to send in response - * @throws IOException */ private void sendErrorResponse(final HttpServletRequest request, final HttpServletResponse response, @@ -272,18 +315,7 @@ private void sendErrorResponse(final HttpServletRequest request, //Make sure Spring picks up this exception request.setAttribute(EXCEPTION_ATTRIBUTE, ex); - // Which status codes should be treated as ERROR? - final Set LOG_AS_ERROR = new HashSet<>(); - String[] error_codes = configurationService.getArrayProperty( - P_LOG_AS_ERROR, LOG_AS_ERROR_DEFAULT); - for (String code : error_codes) { - try { - LOG_AS_ERROR.add(Integer.valueOf(code)); - } catch (NumberFormatException e) { - log.warn("Non-integer HTTP status code {} in {}", code, P_LOG_AS_ERROR); - // And continue - } - } + final Set LOG_AS_ERROR = getStatusCodesLoggedAsErrors(); // We don't want to fill logs with bad/invalid REST API requests. if (HttpStatus.valueOf(statusCode).is5xxServerError() || LOG_AS_ERROR.contains(statusCode)) { @@ -309,4 +341,24 @@ private void sendErrorResponse(final HttpServletRequest request, response.sendError(statusCode, message); } + /** + * Get set of status codes that should be treated as errors. + * + * @return set of status codes that should be treated as errors + */ + private Set getStatusCodesLoggedAsErrors() { + final Set statusCodesLoggedAsErrors = new HashSet<>(); + String[] error_codes = configurationService.getArrayProperty( + P_LOG_AS_ERROR, LOG_AS_ERROR_DEFAULT); + for (String code : error_codes) { + try { + statusCodesLoggedAsErrors.add(Integer.valueOf(code)); + } catch (NumberFormatException e) { + log.warn("Non-integer HTTP status code {} in {}", code, P_LOG_AS_ERROR); + // And continue + } + } + return statusCodesLoggedAsErrors; + } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinLicenseRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinLicenseRestRepository.java index 2ef00709a512..0ad207114168 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinLicenseRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinLicenseRestRepository.java @@ -8,6 +8,7 @@ package org.dspace.app.rest.repository; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import java.io.IOException; import java.sql.SQLException; @@ -25,6 +26,7 @@ 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.PaginationException; import org.dspace.app.rest.exception.UnprocessableEntityException; import org.dspace.app.rest.model.ClarinLicenseLabelRest; import org.dspace.app.rest.model.ClarinLicenseRest; @@ -43,6 +45,7 @@ import org.dspace.core.Context; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.security.access.prepost.PreAuthorize; @@ -103,33 +106,44 @@ public Page findByName(@Parameter(value = "name", required = throw new RuntimeException(e.getMessage(), e); } clarinLicenseList.add(clarinLicense); - return converter.toRestPage(clarinLicenseList, pageable, utils.obtainProjection()); + try { + return converter.toRestPage(clarinLicenseList, pageable, utils.obtainProjection()); + } catch (PaginationException pe) { + return getEmptyPageForIncorrectPageable(pageable, clarinLicenseList.size()); + } } @SearchRestMethod(name = "byNameLike") public Page findByNameLike(@Parameter(value = "name", required = true) String name, - Pageable pageable) { - List clarinLicenseList; + Pageable pageable) { + List clarinLicenseList = new ArrayList<>(); try { Context context = obtainContext(); - clarinLicenseList = clarinLicenseService.findByNameLike(context, name); - if (CollectionUtils.isEmpty(clarinLicenseList)) { + clarinLicenseList.addAll(clarinLicenseService.findByNameLike(context, name)); + if (clarinLicenseList.isEmpty()) { return null; } } catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } - return converter.toRestPage(clarinLicenseList, pageable, utils.obtainProjection()); + try { + return converter.toRestPage(clarinLicenseList, pageable, utils.obtainProjection()); + } catch (PaginationException pe) { + return getEmptyPageForIncorrectPageable(pageable, clarinLicenseList.size()); + } } @Override @PreAuthorize("permitAll()") public Page findAll(Context context, Pageable pageable) { + List clarinLicenseList = new ArrayList<>(); try { - List clarinLicenseList = clarinLicenseService.findAll(context); + clarinLicenseList.addAll(clarinLicenseService.findAll(context)); return converter.toRestPage(clarinLicenseList, pageable, utils.obtainProjection()); } catch (SQLException | AuthorizeException e) { throw new RuntimeException(e.getMessage(), e); + } catch (PaginationException pe) { + return getEmptyPageForIncorrectPageable(pageable, clarinLicenseList.size()); } } @@ -172,7 +186,7 @@ protected ClarinLicenseRest createAndReturn(Context context) clarinLicense.setLicenseLabels(this.getClarinLicenseLabels(clarinLicenseRest.getClarinLicenseLabel(), clarinLicenseRest.getExtendedClarinLicenseLabels())); clarinLicense.setDefinition(clarinLicenseRest.getDefinition()); - clarinLicense.setConfirmation(clarinLicenseRest.getConfirmation()); + clarinLicense.setConfirmation(Confirmation.getConfirmation(clarinLicenseRest.getConfirmation())); clarinLicense.setRequiredInfo(clarinLicenseRest.getRequiredInfo()); clarinLicense.setEperson(userRegistration); @@ -220,7 +234,7 @@ protected ClarinLicenseRest put(Context context, HttpServletRequest request, Str clarinLicense.setName(clarinLicenseRest.getName()); clarinLicense.setRequiredInfo(clarinLicenseRest.getRequiredInfo()); clarinLicense.setDefinition(clarinLicenseRest.getDefinition()); - clarinLicense.setConfirmation(clarinLicenseRest.getConfirmation()); + clarinLicense.setConfirmation(Confirmation.getConfirmation(clarinLicenseRest.getConfirmation())); clarinLicense.setLicenseLabels(this.getClarinLicenseLabels(clarinLicenseRest.getClarinLicenseLabel(), clarinLicenseRest.getExtendedClarinLicenseLabels())); @@ -273,4 +287,8 @@ private ClarinLicenseLabel getClarinLicenseLabelFromRest(ClarinLicenseLabelRest return clarinLicenseLabel; } + private Page getEmptyPageForIncorrectPageable(Pageable pageable, int totalElements) { + return new PageImpl<>(List.of(), pageable, totalElements); + } + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java index 3f217345cdb0..e6657769bde8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ClarinUserMetadataRestController.java @@ -9,6 +9,7 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.dspace.app.rest.utils.ContextUtil.obtainContext; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.dspace.content.clarin.ClarinLicense.EXTRA_EMAIL; import static org.dspace.content.clarin.ClarinLicense.SEND_TOKEN; import static org.dspace.content.clarin.ClarinUserRegistration.ANONYMOUS_USER_REGISTRATION; @@ -25,12 +26,12 @@ import java.util.UUID; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.BadRequestException; import javax.ws.rs.NotFoundException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.model.BitstreamRest; import org.dspace.app.rest.model.ClarinUserMetadataRest; import org.dspace.app.rest.model.ItemRest; @@ -219,6 +220,11 @@ public ResponseEntity manageUserMetadata(@RequestParam("bitstreamUUID") UUID bit " and the bitstream"); } + ClarinLicense clarinLicense = this.getClarinLicense(clarinLicenseResourceMapping); + if (Objects.isNull(currentUser) && (clarinLicense.getConfirmation() != Confirmation.ALLOW_ANONYMOUS)) { + throw new AuthorizeException("Anonymous user is not allowed to get access token"); + } + // Get ClarinUserMetadataRest Array from the request body ClarinUserMetadataRest[] clarinUserMetadataRestArray = new ObjectMapper().readValue(request.getInputStream(), ClarinUserMetadataRest[].class); @@ -246,7 +252,6 @@ public ResponseEntity manageUserMetadata(@RequestParam("bitstreamUUID") UUID bit // If yes - send token to e-mail try { String email = getEmailFromUserMetadata(clarinUserMetadataRestList); - ClarinLicense clarinLicense = this.getClarinLicense(clarinLicenseResourceMapping); this.sendEmailWithDownloadLink(context, bitstream, clarinLicense, email, downloadToken, MailType.BITSTREAM, clarinUserMetadataRestList); } catch (MessagingException e) { @@ -270,12 +275,13 @@ private void sendEmailWithDownloadLink(Context context, DSpaceObject dso, throws IOException, SQLException, MessagingException { if (StringUtils.isBlank(email)) { log.error("Cannot send email with download link because the email is empty."); - throw new BadRequestException("Cannot send email with download link because the email is empty."); + throw new DSpaceBadRequestException("Cannot send email with download link because the email is empty."); } if (Objects.isNull(dso)) { log.error("Cannot send email with download link because the DSpaceObject is null."); - throw new BadRequestException("Cannot send email with download link because the DSpaceObject is null."); + throw new DSpaceBadRequestException( + "Cannot send email with download link because the DSpaceObject is null."); } // Fetch DSpace main cfg info and send it in the email @@ -547,10 +553,6 @@ public ClarinLicenseResourceMapping getLicenseResourceMapping(Context context, U } private ClarinLicense getClarinLicense(ClarinLicenseResourceMapping clarinLicenseResourceMapping) { - if (Objects.isNull(clarinLicenseResourceMapping)) { - throw new NullPointerException("The clarinLicenseResourceMapping object is null."); - } - // Get ClarinLicense from the ClarinLicenseResourceMapping ClarinLicense clarinLicense = clarinLicenseResourceMapping.getLicense(); if (Objects.isNull(clarinLicense)) { @@ -562,9 +564,6 @@ private ClarinLicense getClarinLicense(ClarinLicenseResourceMapping clarinLicens private boolean shouldEmailToken(ClarinLicenseResourceMapping clarinLicenseResourceMapping) { ClarinLicense clarinLicense = this.getClarinLicense(clarinLicenseResourceMapping); - if (Objects.isNull(clarinLicense)) { - throw new NullPointerException("The ClarinLicense is null."); - } // If the required info contains the key work `SEND_TOKEN` it should generate the token. if (StringUtils.isBlank(clarinLicense.getRequiredInfo())) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ExternalHandleRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ExternalHandleRestRepository.java index df89e4214bb6..5330f49e43bc 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ExternalHandleRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ExternalHandleRestRepository.java @@ -18,12 +18,10 @@ import java.util.Date; import java.util.List; import java.util.Objects; -import javax.persistence.PersistenceException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.MediaType; -import org.apache.commons.lang.RandomStringUtils; import org.dspace.app.rest.utils.ContextUtil; import org.dspace.authorize.AuthorizeException; import org.dspace.content.DCDate; @@ -34,7 +32,8 @@ import org.dspace.handle.service.HandleClarinService; import org.dspace.handle.service.HandleService; import org.dspace.services.ConfigurationService; -import org.postgresql.util.PSQLException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -50,6 +49,8 @@ @RequestMapping("/api/services") public class ExternalHandleRestRepository { + private static final Logger log = LoggerFactory.getLogger(ExternalHandleRestRepository.class); + private final String EXTERNAL_HANDLE_ENDPOINT_FIND_ALL = "handles/magic"; private final String EXTERNAL_HANDLE_ENDPOINT_SHORTEN = "handles"; private final String EXTERNAL_HANDLE_ENDPOINT_UPDATE = "handles"; @@ -65,6 +66,9 @@ public class ExternalHandleRestRepository { @Autowired private ConfigurationService configurationService; + @Autowired + private RandomStringGenerator randomStringGenerator; + /** * Get all Handles with url which contains the `@magicLindat` string then convert them to the `external/Handle` * and return in the List. @@ -108,11 +112,11 @@ public ResponseEntity shortenHandle(@RequestBody Handle handle, HttpServ } handle.submitdate = new DCDate(new Date()).toString(); String subprefix = (isNotBlank(handle.subprefix)) ? handle.subprefix + "-" : ""; - String magicURL = handle.getMagicUrl(); + String magicURL = handle.generateMagicUrl(); String hdl = createHandle(subprefix, magicURL, context); if (Objects.isNull(hdl)) { - return new ResponseEntity<>("Cannot create the shortened handle, try it again.", - HttpStatus.BAD_REQUEST); + return new ResponseEntity<>("Failed to create the shortened handle.", + HttpStatus.INTERNAL_SERVER_ERROR); } context.complete(); @@ -122,7 +126,8 @@ public ResponseEntity shortenHandle(@RequestBody Handle handle, HttpServ } } - return new ResponseEntity<>("Cannot create handle because some parameter is null or the URL is not valid", + return new ResponseEntity<>(configurationService.getProperty("shortener.post.error", + "Cannot create handle because some parameter is null or the URL is not valid"), HttpStatus.BAD_REQUEST); } @@ -148,28 +153,41 @@ && isNotBlank(updatedHandle.getHandle()) && isNotBlank(updatedHandle.token)) { updatedHandle.getHandle().replace(canonicalPrefix, ""); // load Handle object from the DB - org.dspace.handle.Handle oldHandle = - this.handleClarinService.findByHandle(context, oldHandleStr); + org.dspace.handle.Handle handleEntity = + this.handleClarinService.findByHandleAndMagicToken(context, oldHandleStr, updatedHandle.token); - if (Objects.isNull(oldHandle)) { + if (Objects.isNull(handleEntity)) { return new ResponseEntity<>("Cannot find the handle in the database.", - HttpStatus.BAD_REQUEST); + HttpStatus.NOT_FOUND); } + String oldHandleMagicUrl = handleEntity.getUrl(); + String hdl = handleEntity.getHandle(); + // create externalHandle based on the handle and the URL with the `@magicLindat` string - Handle oldExternalHandle = new Handle(oldHandle.getHandle(), oldHandle.getUrl()); + Handle oldExternalHandle = new Handle(hdl, oldHandleMagicUrl); + log.info("Handle [{}] changed url from \"{}\" to \"{}\".", hdl, oldExternalHandle.url, + updatedHandle.url); oldExternalHandle.url = updatedHandle.url; - // generate new magicURL for the oldHandle - oldHandle.setUrl(oldExternalHandle.getMagicUrl()); + // this has the new url and a new token + String newMagicUrl = oldExternalHandle.generateMagicUrl(); + + // set the new magic url as url on the entity object + handleEntity.setUrl(newMagicUrl); // update handle in the DB - this.handleClarinService.save(context, oldHandle); + context.turnOffAuthorisationSystem(); + try { + this.handleClarinService.save(context, handleEntity); + } finally { + context.restoreAuthSystemState(); + } context.commit(); // return updated external handle - return new ResponseEntity<>(oldExternalHandle, HttpStatus.OK); + return new ResponseEntity<>(new Handle(hdl, newMagicUrl), HttpStatus.OK); } catch (SQLException | AuthorizeException e) { return new ResponseEntity<>("Cannot update the external handle because: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); @@ -189,22 +207,27 @@ && isNotBlank(updatedHandle.getHandle()) && isNotBlank(updatedHandle.token)) { * @return shortened Handle string e.g. `5478` */ private String createHandle(String subprefix, String url, Context context) throws SQLException, AuthorizeException { + int attempts = 100; String handle; this.loadPrefix(); - // generate short handle - String rnd = RandomStringUtils.random(4,true,true).toUpperCase(); - handle = prefix + "/" + subprefix + rnd; - - try { - // check if exists such handle - it throws error if exists such handle - this.handleClarinService.findByHandle(context, handle); - } catch (PSQLException | PersistenceException e) { - return null; + for (int attempt = 0; attempt < attempts; attempt++) { + // generate short handle + String rnd = randomStringGenerator.generate(4).toUpperCase(); + handle = prefix + "/" + subprefix + rnd; + // check if exists, create and return if not + if (handleClarinService.findByHandle(context, handle) == null) { + context.turnOffAuthorisationSystem(); + try { + handleClarinService.createExternalHandle(context, handle, url); + } finally { + context.restoreAuthSystemState(); + } + return handle; + } } - - handleClarinService.createExternalHandle(context, handle, url); - return handle; + log.error("Cannot create handle, all attempts failed."); + return null; } private boolean validateHandle(Handle handle) { @@ -215,8 +238,7 @@ private boolean validateHandle(Handle handle) { // handle parameters cannot be blank if (isBlank(handle.url) || isBlank(handle.title) || - isBlank(handle.reportemail) || - isBlank(handle.subprefix)) { + isBlank(handle.reportemail)) { return false; } @@ -257,7 +279,7 @@ private boolean matchesAnyOf(String tested, String configPropertyWithPatterns) { return false; } - final String pattern = String.join(",", patterns); + final String pattern = String.join(";", patterns); String[] list = pattern.split(";"); for (String regexp : list) { diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 2ee96a242cd1..1e216a6ae501 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -70,7 +70,6 @@ public Page findByHandle(@Parameter(value = "handl if (StringUtils.isBlank(handle)) { throw new DSpaceBadRequestException("handle cannot be null!"); } - List metadataValueWrappers = new ArrayList<>(); Context context = obtainContext(); if (Objects.isNull(context)) { throw new RuntimeException("Cannot obtain the context from the request."); @@ -78,7 +77,7 @@ public Page findByHandle(@Parameter(value = "handl HttpServletRequest request = getRequestService().getCurrentRequest().getHttpServletRequest(); String contextPath = request.getContextPath(); List rs = new ArrayList<>(); - DSpaceObject dso = null; + DSpaceObject dso; try { dso = handleService.resolveToObject(context, handle); @@ -91,15 +90,15 @@ public Page findByHandle(@Parameter(value = "handl } Item item = (Item) dso; - List fileGrpTypes = Arrays.asList(fileGrpType.split(",")); + List fileGrpTypes = (fileGrpType == null ? List.of() : Arrays.asList(fileGrpType.split(","))); List bundles = findEnabledBundles(fileGrpTypes, item); for (Bundle bundle : bundles) { - List bitstreams = bundle.getBitstreams(); + List bitstreams = new ArrayList<>(bundle.getBitstreams()); String use = bundle.getName(); if (StringUtils.equals("THUMBNAIL", use)) { Thumbnail thumbnail = itemService.getThumbnail(context, item, false); if (Objects.nonNull(thumbnail)) { - bitstreams = new ArrayList<>(); + bitstreams.clear(); bitstreams.add(thumbnail.getThumb()); } } @@ -116,33 +115,31 @@ public Page findByHandle(@Parameter(value = "handl boolean allowComposePreviewContent = configurationService.getBooleanProperty ("create.file-preview.on-item-page-load", false); if (allowComposePreviewContent) { - fileInfos = previewContentService.getFilePreviewContent(context, bitstream); + fileInfos.addAll(previewContentService.getFilePreviewContent(context, bitstream)); // Do not store HTML content in the database because it could be longer than the limit // of the database column - if (!StringUtils.equals("text/html", bitstream.getFormat(context).getMIMEType())) { + if (!fileInfos.isEmpty() && + !StringUtils.equals("text/html", bitstream.getFormat(context).getMIMEType())) { for (FileInfo fi : fileInfos) { previewContentService.createPreviewContent(context, bitstream, fi); } } } } else { - fileInfos = new ArrayList<>(); for (PreviewContent pc : prContents) { fileInfos.add(previewContentService.createFileInfo(pc)); } } } catch (Exception e) { log.error("Cannot create preview content for bitstream: {} because: {}", - bitstream.getID(), e.getMessage(), e); + bitstream.getID(), e.getMessage()); } } MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), bitstream.getFormatDescription(context), url, canPreview); - metadataValueWrappers.add(bts); rs.add(metadataBitstreamWrapperConverter.convert(bts, utils.obtainProjection())); } - context.commit(); } return new PageImpl<>(rs, pageable, rs.size()); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RandomStringGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RandomStringGenerator.java new file mode 100644 index 000000000000..ed1487f651f4 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RandomStringGenerator.java @@ -0,0 +1,21 @@ +/** + * 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; + +/** + * Interface for generating random strings. + */ +public interface RandomStringGenerator { + /** + * Generate a random string of the specified length. + * + * @param length the length of the random string + * @return the generated random string + */ + String generate(int length); +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RandomStringGeneratorImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RandomStringGeneratorImpl.java new file mode 100644 index 000000000000..b1248d626850 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/RandomStringGeneratorImpl.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.repository; + +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.stereotype.Component; + +/** + * Default implementation of RandomStringGenerator using RandomStringUtils. + */ +@Component +public class RandomStringGeneratorImpl implements RandomStringGenerator { + + @Override + public String generate(int length) { + return RandomStringUtils.random(length, true, true); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionController.java index fb7c06f606e8..775a08e496c3 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SubmissionController.java @@ -14,11 +14,11 @@ import java.util.UUID; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.BadRequestException; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.dspace.app.rest.converter.ConverterService; +import org.dspace.app.rest.exception.DSpaceBadRequestException; import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.ShareSubmissionLinkDTO; import org.dspace.app.rest.model.WorkspaceItemRest; @@ -101,7 +101,7 @@ public ResponseEntity generateShareLink(@RequestParam(na if (currentUser == null) { String errorMessage = "The current user is not valid, it cannot be null."; log.error(errorMessage); - throw new BadRequestException(errorMessage); + throw new DSpaceBadRequestException(errorMessage); } // Send email to submitter with share link @@ -137,7 +137,7 @@ public WorkspaceItemRest setOwner(@RequestParam(name = "shareToken") String shar if (CollectionUtils.isEmpty(wsiList)) { String errorMessage = "The workspace item with share token:" + shareToken + " does not exist."; log.error(errorMessage); - throw new BadRequestException(errorMessage); + throw new DSpaceBadRequestException(errorMessage); } // Get the first workspace item - the only one @@ -157,7 +157,7 @@ public WorkspaceItemRest setOwner(@RequestParam(name = "shareToken") String shar if (currentUser == null) { String errorMessage = "The current user is not valid, it cannot be null."; log.error(errorMessage); - throw new BadRequestException(errorMessage); + throw new DSpaceBadRequestException(errorMessage); } wsi.getItem().setSubmitter(currentUser); @@ -217,7 +217,7 @@ private void validateWorkspaceItem(WorkspaceItem wsi, Integer wsoId, String shar String identifierName = wsoId != null ? "ID" : "share token"; String errorMessage = "The workspace item with " + identifierName + ":" + identifier + " does not exist."; log.error(errorMessage); - throw new BadRequestException(errorMessage); + throw new DSpaceBadRequestException(errorMessage); } } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestController.java index dd94321937d2..0a1bd4a8a2ad 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SuggestionRestController.java @@ -18,13 +18,13 @@ import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.BadRequestException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.collections4.CollectionUtils; 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.VocabularyEntryRest; import org.dspace.app.rest.model.hateoas.VocabularyEntryResource; import org.dspace.app.rest.utils.Utils; @@ -118,7 +118,7 @@ public PagedModel filter(@Nullable HttpServletRequest r if (!isAllowedSearching(autocompleteCustom)) { String errorMessage = "Searching for autocompleteCustom: " + autocompleteCustom + " is not allowed"; log.warn(errorMessage); - throw new BadRequestException(errorMessage); + throw new DSpaceBadRequestException(errorMessage); } Pageable pageable = utils.getPageable(optionalPageable); 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 e6ff59f2a729..2bafd26516aa 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 @@ -22,7 +22,6 @@ import java.util.Objects; import java.util.UUID; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.BadRequestException; import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.io.FileUtils; @@ -492,7 +491,7 @@ public Page findByShareToken(@Parameter(value = SHARE_TOKEN, if (CollectionUtils.isEmpty(witems)) { String errorMessage = "The workspace item with share token:" + shareToken + " does not exist."; log.error(errorMessage); - throw new BadRequestException(errorMessage); + throw new DSpaceBadRequestException(errorMessage); } if (!authorizeService.authorizeActionBoolean(context, witems.get(0).getItem(), Constants.READ)) { String errorMessage = "The current user does not have rights to view the WorkflowItem"; diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestControllerIT.java index 427749be1abb..f61622c20d9e 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/AuthorizationRestControllerIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static org.dspace.app.rest.repository.ClarinLicenseRestRepository.OPERATION_PATH_LICENSE_RESOURCE; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; @@ -126,7 +127,8 @@ public void setup() throws Exception { .withDspaceObject(bitstream).build(); // Create clarin license with clarin license label - clarinLicense = createClarinLicense(CLARIN_LICENSE_NAME, "Test Def", "Test R Info", 3); + clarinLicense = createClarinLicense(CLARIN_LICENSE_NAME, "Test Def", "Test R Info", + Confirmation.ALLOW_ANONYMOUS); context.restoreAuthSystemState(); } @@ -321,8 +323,8 @@ private ClarinLicenseLabel createClarinLicenseLabel(String label, boolean extend /** * Create ClarinLicense object with ClarinLicenseLabel object for testing purposes. */ - private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, int confirmation) - throws SQLException, AuthorizeException { + private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, + Confirmation confirmation) throws SQLException, AuthorizeException { ClarinLicense clarinLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); clarinLicense.setConfirmation(confirmation); clarinLicense.setDefinition(definition); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseImportControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseImportControllerIT.java index 92ee88fa21fb..c3f772d755be 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseImportControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseImportControllerIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static com.jayway.jsonpath.JsonPath.read; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -82,7 +83,7 @@ public void setup() throws Exception { // create ClarinLicenses firstCLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); firstCLicense.setName("CL Name1"); - firstCLicense.setConfirmation(0); + firstCLicense.setConfirmation(Confirmation.NOT_REQUIRED); firstCLicense.setDefinition("CL Definition1"); firstCLicense.setRequiredInfo("CL Req1"); // add ClarinLicenseLabels to the ClarinLicense @@ -111,7 +112,7 @@ public void importLicenseTest() throws Exception { ClarinLicenseRest clarinLicenseRest = new ClarinLicenseRest(); clarinLicenseRest.setName("name"); clarinLicenseRest.setBitstreams(0); - clarinLicenseRest.setConfirmation(4); + clarinLicenseRest.setConfirmation(Confirmation.NOT_REQUIRED.getValue()); clarinLicenseRest.setRequiredInfo("Not required"); clarinLicenseRest.setDefinition("definition"); clarinLicenseConverter.setExtendedClarinLicenseLabels(clarinLicenseRest, firstCLicense.getLicenseLabels(), diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseRestRepositoryIT.java index 7f07464b456a..3b0cfcba59f5 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinLicenseRestRepositoryIT.java @@ -10,6 +10,8 @@ import static com.jayway.jsonpath.JsonPath.read; import static org.apache.commons.codec.CharEncoding.UTF_8; import static org.apache.commons.io.IOUtils.toInputStream; +import static org.dspace.app.rest.utils.Utils.DEFAULT_PAGE_SIZE; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -54,6 +56,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; /** * Integration tests for the Clarin License Rest Repository @@ -62,6 +65,8 @@ */ public class ClarinLicenseRestRepositoryIT extends AbstractControllerIntegrationTest { + private static final String BASE_URI = "/api/core/clarinlicenses"; + @Autowired ClarinLicenseService clarinLicenseService; @@ -110,7 +115,7 @@ public void setup() throws Exception { // create ClarinLicenses firstCLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); firstCLicense.setName("CL Name1"); - firstCLicense.setConfirmation(0); + firstCLicense.setConfirmation(Confirmation.NOT_REQUIRED); firstCLicense.setDefinition("CL Definition1"); firstCLicense.setRequiredInfo("CL Req1"); // add ClarinLicenseLabels to the ClarinLicense @@ -123,7 +128,7 @@ public void setup() throws Exception { secondCLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); secondCLicense.setName("CL Name2"); - secondCLicense.setConfirmation(1); + secondCLicense.setConfirmation(Confirmation.ASK_ONLY_ONCE); secondCLicense.setDefinition("CL Definition2"); secondCLicense.setRequiredInfo("CL Req2"); // add ClarinLicenseLabels to the ClarinLicense @@ -188,7 +193,7 @@ public void clarinLicensesAndLicenseLabelsAreInitialized() throws Exception { @Test public void findAll() throws Exception { String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses")) + getClient(authTokenAdmin).perform(get(BASE_URI)) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) .andExpect(jsonPath("$._embedded.clarinlicenses", Matchers.hasItem( @@ -207,8 +212,7 @@ public void findAll() throws Exception { Objects.requireNonNull(getExtendedLicenseLabels( firstCLicense.getLicenseLabels()))) ))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/clarinlicenses"))); + .andExpect(jsonPath("$._links.self.href", Matchers.containsString(BASE_URI))); } /** @@ -217,7 +221,7 @@ public void findAll() throws Exception { @Test public void searchBy() throws Exception { String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses/search/byName") + getClient(authTokenAdmin).perform(get(BASE_URI + "/search/byName") .param("name", "CL Name1")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) @@ -232,7 +236,7 @@ public void searchBy() throws Exception { @Test public void searchByLike() throws Exception { String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses/search/byNameLike") + getClient(authTokenAdmin).perform(get(BASE_URI + "/search/byNameLike") .param("name", "Name")) .andExpect(status().isOk()) .andExpect(content().contentType(contentType)) @@ -252,8 +256,17 @@ public void searchByLike() throws Exception { Objects.requireNonNull(getExtendedLicenseLabels( firstCLicense.getLicenseLabels()))) ))) - .andExpect(jsonPath("$._links.self.href", - Matchers.containsString("/api/core/clarinlicenses"))); + .andExpect(jsonPath("$._links.self.href", Matchers.containsString(BASE_URI))); + } + + /** + * This method tests requests for incorrect page parameter (e.g. page=5). + */ + @Test + public void searchWithIncorrectPageParameter() throws Exception { + testRequestWithIncorrectPageable("", null, 2, 10); + testRequestWithIncorrectPageable("/search/byName", "CL Name1", 1, 20); + testRequestWithIncorrectPageable("/search/byNameLike", "Name", 2, 30); } @Test @@ -261,7 +274,7 @@ public void create() throws Exception { ClarinLicenseRest clarinLicenseRest = new ClarinLicenseRest(); clarinLicenseRest.setName("name"); clarinLicenseRest.setBitstreams(0); - clarinLicenseRest.setConfirmation(4); + clarinLicenseRest.setConfirmation(Confirmation.NOT_REQUIRED.getValue()); clarinLicenseRest.setRequiredInfo("Not required"); clarinLicenseRest.setDefinition("definition"); clarinLicenseConverter.setExtendedClarinLicenseLabels(clarinLicenseRest, firstCLicense.getLicenseLabels(), @@ -276,7 +289,7 @@ public void create() throws Exception { AtomicReference idRef = new AtomicReference<>(); String authTokenAdmin = getAuthToken(admin.getEmail(), password); try { - getClient(authTokenAdmin).perform(post("/api/core/clarinlicenses") + getClient(authTokenAdmin).perform(post(BASE_URI) .content(new ObjectMapper().writeValueAsBytes(clarinLicenseRest)) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) @@ -328,7 +341,7 @@ public void update() throws Exception { ClarinLicense clarinLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); clarinLicense.setName("default name"); clarinLicense.setDefinition("default definition"); - clarinLicense.setConfirmation(0); + clarinLicense.setConfirmation(Confirmation.NOT_REQUIRED); clarinLicense.setRequiredInfo("default info"); Set clarinLicenseLabels = new HashSet<>(); @@ -340,7 +353,7 @@ public void update() throws Exception { ClarinLicense clarinLicenseUpdated = ClarinLicenseBuilder.createClarinLicense(context).build(); clarinLicenseUpdated.setName("updated name"); clarinLicenseUpdated.setDefinition("updated definition"); - clarinLicenseUpdated.setConfirmation(4); + clarinLicenseUpdated.setConfirmation(Confirmation.ASK_ALWAYS); clarinLicenseUpdated.setRequiredInfo("updated info"); Set clarinLicenseLabelUpdated = new HashSet<>(); @@ -352,15 +365,15 @@ public void update() throws Exception { ClarinLicenseRest clarinLicenseRest = clarinLicenseConverter.convert(clarinLicenseUpdated, Projection.DEFAULT); String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient(authTokenAdmin).perform(get(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isOk()); - getClient(authTokenAdmin).perform(put("/api/core/clarinlicenses/" + clarinLicense.getID()) + getClient(authTokenAdmin).perform(put(BASE_URI + "/" + clarinLicense.getID()) .content(new ObjectMapper().writeValueAsBytes(clarinLicenseRest)) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient(authTokenAdmin).perform(get(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$", Matchers.is( ClarinLicenseMatcher.matchClarinLicenseWithoutId(clarinLicenseUpdated)) @@ -376,7 +389,7 @@ public void forbiddenUpdateClarinLicense() throws Exception { clarinLicense.setName("default name"); clarinLicense.setDefinition("default definition"); - clarinLicense.setConfirmation(0); + clarinLicense.setConfirmation(Confirmation.NOT_REQUIRED); clarinLicense.setRequiredInfo("default info"); Set clarinLicenseLabels = new HashSet<>(); @@ -387,7 +400,7 @@ public void forbiddenUpdateClarinLicense() throws Exception { ClarinLicenseRest clarinLicenseRest = clarinLicenseConverter.convert(clarinLicense, Projection.DEFAULT); String authTokenUser = getAuthToken(eperson.getEmail(), password); - getClient(authTokenUser).perform(delete("/api/core/clarinlicenses/" + clarinLicense.getID()) + getClient(authTokenUser).perform(delete(BASE_URI + "/" + clarinLicense.getID()) .content(new ObjectMapper().writeValueAsBytes(clarinLicenseRest)) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isForbidden()) @@ -403,7 +416,7 @@ public void notFoundUpdateClarinLicense() throws Exception { clarinLicense.setName("default name"); clarinLicense.setDefinition("default definition"); - clarinLicense.setConfirmation(0); + clarinLicense.setConfirmation(Confirmation.NOT_REQUIRED); clarinLicense.setRequiredInfo("default info"); Set clarinLicenseLabels = new HashSet<>(); @@ -415,7 +428,7 @@ public void notFoundUpdateClarinLicense() throws Exception { ClarinLicenseRest clarinLicenseRest = clarinLicenseConverter.convert(clarinLicense, Projection.DEFAULT); String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(put("/api/core/clarinlicenses/" + clarinLicense.getID() + "124679") + getClient(authTokenAdmin).perform(put(BASE_URI + "/" + clarinLicense.getID() + "124679") .content(new ObjectMapper().writeValueAsBytes(clarinLicenseRest)) .contentType(org.springframework.http.MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()) @@ -430,13 +443,13 @@ public void deleteClarinLicense() throws Exception { context.restoreAuthSystemState(); String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient(authTokenAdmin).perform(get(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isOk()); - getClient(authTokenAdmin).perform(delete("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient(authTokenAdmin).perform(delete(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isNoContent()); - getClient(authTokenAdmin).perform(get("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient(authTokenAdmin).perform(get(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isNotFound()); } @@ -447,7 +460,7 @@ public void unauthorizedDeleteClarinLicense() throws Exception { ClarinLicense clarinLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); context.restoreAuthSystemState(); - getClient().perform(delete("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient().perform(delete(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isUnauthorized()) ; } @@ -460,7 +473,7 @@ public void forbiddenDeleteClarinLicense() throws Exception { context.restoreAuthSystemState(); String authTokenUser = getAuthToken(eperson.getEmail(), password); - getClient(authTokenUser).perform(delete("/api/core/clarinlicenses/" + clarinLicense.getID())) + getClient(authTokenUser).perform(delete(BASE_URI + "/" + clarinLicense.getID())) .andExpect(status().isForbidden()) ; } @@ -469,7 +482,7 @@ public void forbiddenDeleteClarinLicense() throws Exception { @Test public void notFoundDeleteClarinLicense() throws Exception { String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(delete("/api/core/clarinlicenses/" + 1239990)) + getClient(authTokenAdmin).perform(delete(BASE_URI + "/" + 1239990)) .andExpect(status().isNotFound()) ; } @@ -489,7 +502,7 @@ public void findAllBitstreamsAttachedToLicense() throws Exception { String tokenAdmin = getAuthToken(admin.getEmail(), password); // Check if the Clarin License was attached to the Bitstreams - getClient(tokenAdmin).perform(get("/api/core/clarinlicenses/" + firstCLicense.getID())) + getClient(tokenAdmin).perform(get(BASE_URI + "/" + firstCLicense.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$.bitstreams", is(2))); } @@ -514,5 +527,26 @@ private ClarinLicenseLabel getExtendedLicenseLabels(List cla return null; } + private void testRequestWithIncorrectPageable(String uri, + String nameValue, + int expectedTotal, + int pageNumber) throws Exception { + String authTokenAdmin = getAuthToken(admin.getEmail(), password); + MockHttpServletRequestBuilder mockBuilder = get(BASE_URI + uri) + .param("page", String.valueOf(pageNumber)); + if (nameValue != null) { + mockBuilder = mockBuilder.param("name", nameValue); + } + + getClient(authTokenAdmin).perform(mockBuilder) + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$._embedded").doesNotExist()) + .andExpect(jsonPath("$._links.self.href", Matchers.containsString(BASE_URI))) + .andExpect(jsonPath("$.page.size", Matchers.equalTo(DEFAULT_PAGE_SIZE))) + .andExpect(jsonPath("$.page.totalElements", Matchers.equalTo(expectedTotal))) + .andExpect(jsonPath("$.page.totalPages", Matchers.equalTo(1))) + .andExpect(jsonPath("$.page.number", Matchers.equalTo(pageNumber))); + } } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataImportControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataImportControllerIT.java index 737283f9a5be..a9e7e1069cd6 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataImportControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataImportControllerIT.java @@ -8,6 +8,7 @@ package org.dspace.app.rest; import static org.dspace.app.rest.repository.ClarinLicenseRestRepository.OPERATION_PATH_LICENSE_RESOURCE; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -90,7 +91,7 @@ private void prepareEnvironment(String requiredInfo) throws Exception { String clarinLicenseName = "Test Clarin License"; // 2. Create clarin license with clarin license label - clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", requiredInfo, 0); + clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", requiredInfo, Confirmation.NOT_REQUIRED); // creating replace operation Map licenseReplaceOpValue = new HashMap(); @@ -326,7 +327,8 @@ private ClarinLicenseLabel createClarinLicenseLabel(String label, boolean extend /** * Create ClarinLicense object with ClarinLicenseLabel object for testing purposes. */ - private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, int confirmation) + private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, + Confirmation confirmation) throws SQLException, AuthorizeException { ClarinLicense clarinLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); clarinLicense.setConfirmation(confirmation); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java index 2f90faae3d17..708095688375 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinUserMetadataRestControllerIT.java @@ -9,6 +9,7 @@ import static org.dspace.app.rest.repository.ClarinLicenseRestRepository.OPERATION_PATH_LICENSE_RESOURCE; import static org.dspace.app.rest.repository.ClarinUserMetadataRestController.CHECK_EMAIL_RESPONSE_CONTENT; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -73,7 +74,7 @@ public class ClarinUserMetadataRestControllerIT extends AbstractControllerIntegr Bitstream bitstream2; // Attach ClarinLicense to the Bitstream - private void prepareEnvironment(String requiredInfo, Integer confirmation) throws Exception { + private void prepareEnvironment(String requiredInfo, Confirmation confirmation) throws Exception { // 1. Create Workspace Item with uploaded file // 2. Create Clarin License // 3. Send request to add Clarin License to the Workspace Item @@ -126,8 +127,8 @@ private void prepareEnvironment(String requiredInfo, Integer confirmation) throw } @Test - public void notAuthorizedUser_shouldReturnToken() throws Exception { - this.prepareEnvironment("NAME", 0); + public void notAuthorizedUser_withLicenseConfirmation_ALLOW_ANONYMOUS_USER() throws Exception { + this.prepareEnvironment("NAME", Confirmation.ALLOW_ANONYMOUS); ObjectMapper mapper = new ObjectMapper(); ClarinUserMetadataRest clarinUserMetadata1 = new ClarinUserMetadataRest(); clarinUserMetadata1.setMetadataKey("NAME"); @@ -158,8 +159,23 @@ public void notAuthorizedUser_shouldReturnToken() throws Exception { } @Test - public void notAuthorizedUser_shouldSendEmail() throws Exception { - this.prepareEnvironment("SEND_TOKEN", 0); + public void notAuthorizedUser_withLicenseConfirmation_NOT_REQUIRED() throws Exception { + requestTokenForUnauthorizedUser(Confirmation.NOT_REQUIRED); + } + + @Test + public void notAuthorizedUser_withLicenseConfirmation_ASK_ONLY_ONCE() throws Exception { + requestTokenForUnauthorizedUser(Confirmation.ASK_ONLY_ONCE); + } + + @Test + public void notAuthorizedUser_withLicenseConfirmation_ASK_ALWAYS() throws Exception { + requestTokenForUnauthorizedUser(Confirmation.ASK_ALWAYS); + } + + @Test + public void notAuthorizedUser_withAllowingAnonymousLicense_shouldSendEmail() throws Exception { + this.prepareEnvironment("SEND_TOKEN", Confirmation.ALLOW_ANONYMOUS); ObjectMapper mapper = new ObjectMapper(); ClarinUserMetadataRest clarinUserMetadata1 = new ClarinUserMetadataRest(); clarinUserMetadata1.setMetadataKey("NAME"); @@ -200,7 +216,7 @@ public void notAuthorizedUser_shouldSendEmail() throws Exception { @Test public void authorizedUserWithoutMetadata_shouldReturnToken() throws Exception { - this.prepareEnvironment("NAME", 0); + this.prepareEnvironment("NAME", Confirmation.NOT_REQUIRED); context.turnOffAuthorisationSystem(); ClarinUserRegistration clarinUserRegistration = ClarinUserRegistrationBuilder .createClarinUserRegistration(context).withEPersonID(admin.getID()).build(); @@ -245,7 +261,7 @@ public void authorizedUserWithoutMetadata_shouldReturnToken() throws Exception { @Test public void authorizedUserWithoutMetadata_shouldSendEmail() throws Exception { - this.prepareEnvironment("SEND_TOKEN", 0); + this.prepareEnvironment("SEND_TOKEN", Confirmation.NOT_REQUIRED); context.turnOffAuthorisationSystem(); ClarinUserRegistration clarinUserRegistration = ClarinUserRegistrationBuilder .createClarinUserRegistration(context).withEPersonID(admin.getID()).build(); @@ -299,7 +315,7 @@ public void authorizedUserWithoutMetadata_shouldSendEmail() throws Exception { @Test public void authorizedUserWithMetadata_shouldSendToken() throws Exception { - this.prepareEnvironment("NAME,ADDRESS", 0); + this.prepareEnvironment("NAME,ADDRESS", Confirmation.NOT_REQUIRED); context.turnOffAuthorisationSystem(); ClarinUserRegistration clarinUserRegistration = ClarinUserRegistrationBuilder .createClarinUserRegistration(context).withEPersonID(admin.getID()).build(); @@ -348,7 +364,7 @@ public void authorizedUserWithMetadata_shouldSendToken() throws Exception { @Test public void authorizedUserWithMetadata_shouldSendEmail() throws Exception { - this.prepareEnvironment("SEND_TOKEN,NAME,ADDRESS", 0); + this.prepareEnvironment("SEND_TOKEN,NAME,ADDRESS", Confirmation.NOT_REQUIRED); context.turnOffAuthorisationSystem(); ClarinUserRegistration clarinUserRegistration = ClarinUserRegistrationBuilder .createClarinUserRegistration(context).withEPersonID(admin.getID()).build(); @@ -407,7 +423,7 @@ public void authorizedUserWithMetadata_shouldSendEmail() throws Exception { // Confirmation = 1 @Test public void authorizedUserWithoutMetadata_shouldDownloadToken() throws Exception { - this.prepareEnvironment(null, 1); + this.prepareEnvironment(null, Confirmation.ASK_ONLY_ONCE); context.turnOffAuthorisationSystem(); ClarinUserRegistration clarinUserRegistration = ClarinUserRegistrationBuilder .createClarinUserRegistration(context).withEPersonID(admin.getID()).build(); @@ -441,7 +457,7 @@ public void authorizedUserWithoutMetadata_shouldDownloadToken() throws Exception public void shouldNotCreateDuplicateUserMetadataBasedOnHistory() throws Exception { // Prepare environment with Clarin License, resource mapping, allowance, user registration and user metadata // then try to download the same bitstream again and the user metadata should not be created based on history - this.prepareEnvironment("NAME,ADDRESS", 0); + this.prepareEnvironment("NAME,ADDRESS", Confirmation.NOT_REQUIRED); context.turnOffAuthorisationSystem(); ClarinUserRegistration clarinUserRegistration = ClarinUserRegistrationBuilder .createClarinUserRegistration(context).withEPersonID(admin.getID()).build(); @@ -617,7 +633,8 @@ private ClarinLicenseLabel createClarinLicenseLabel(String label, boolean extend /** * Create ClarinLicense object with ClarinLicenseLabel object for testing purposes. */ - private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, int confirmation) + private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, + Confirmation confirmation) throws SQLException, AuthorizeException { ClarinLicense clarinLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); clarinLicense.setConfirmation(confirmation); @@ -634,4 +651,22 @@ private ClarinLicense createClarinLicense(String name, String definition, String clarinLicenseService.update(context, clarinLicense); return clarinLicense; } + + private void requestTokenForUnauthorizedUser(Confirmation licenseConfirmation) throws Exception { + this.prepareEnvironment("NAME", licenseConfirmation); + + ObjectMapper mapper = new ObjectMapper(); + ClarinUserMetadataRest clarinUserMetadata = new ClarinUserMetadataRest(); + clarinUserMetadata.setMetadataKey("NAME"); + clarinUserMetadata.setMetadataValue("Test"); + + List clarinUserMetadataRestList = new ArrayList<>(); + clarinUserMetadataRestList.add(clarinUserMetadata); + + getClient().perform(post("/api/core/clarinusermetadata/manage?bitstreamUUID=" + bitstream.getID()) + .content(mapper.writeValueAsBytes(clarinUserMetadataRestList.toArray())) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnauthorized()); + } + } diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java index 028952e0555b..44d253cafc1c 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ClarinWorkspaceItemRestRepositoryIT.java @@ -9,6 +9,8 @@ import static org.dspace.app.rest.repository.ClarinLicenseRestRepository.OPERATION_PATH_LICENSE_RESOURCE; import static org.dspace.content.InstallItemServiceImpl.SET_OWNING_COLLECTION_EVENT_DETAIL; +import static org.dspace.content.clarin.ClarinLicense.Confirmation; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; @@ -628,7 +630,8 @@ public void addClarinLicenseToWI() throws Exception { String clarinLicenseName = "Test Clarin License"; // 2. Create clarin license with clarin license label - ClarinLicense clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", "Test R Info", 0); + ClarinLicense clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", "Test R Info", + Confirmation.NOT_REQUIRED); // creating replace operation Map licenseReplaceOpValue = new HashMap(); @@ -679,7 +682,8 @@ public void removeClarinLicenseFromWI() throws Exception { List replaceOperations = new ArrayList(); // 2. Create Clarin License String clarinLicenseName = "Test Clarin License"; - ClarinLicense clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", "Test R Info", 0); + ClarinLicense clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", "Test R Info", + Confirmation.NOT_REQUIRED); context.restoreAuthSystemState(); // Creating replace operation @@ -757,9 +761,10 @@ public void updateClarinLicenseInWI() throws Exception { String updateClarinLicenseName = "Updated Clarin License"; // 2. Create Clarin Licenses - ClarinLicense clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", "Test R Info", 0); + ClarinLicense clarinLicense = createClarinLicense(clarinLicenseName, "Test Def", "Test R Info", + Confirmation.NOT_REQUIRED); ClarinLicense updatedClarinLicense = - createClarinLicense(updateClarinLicenseName, "Test Def2", "Test R Info2", 0); + createClarinLicense(updateClarinLicenseName, "Test Def2", "Test R Info2", Confirmation.NOT_REQUIRED); context.restoreAuthSystemState(); // Creating replace operation @@ -992,10 +997,15 @@ public void testWsiWithShareToken() throws Exception { String adminToken = getAuthToken(admin.getEmail(), password); getClient(adminToken).perform(get("/api/submission/workspaceitems/search/shareToken") - .param("shareToken", shareToken) - .contentType(MediaType.APPLICATION_JSON_PATCH_JSON)) + .param("shareToken", shareToken)) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.workspaceitems[0].id", is(wItem.getID()))); + + // test BadRequest response for invalid token + getClient(adminToken).perform(get("/api/submission/workspaceitems/search/shareToken") + .param("shareToken", "invalid_token")) + .andExpect(status().isBadRequest()) + .andExpect(status().reason(containsString("invalid_token does not exist"))); } /** @@ -1015,8 +1025,8 @@ private ClarinLicenseLabel createClarinLicenseLabel(String label, boolean extend /** * Create ClarinLicense object with ClarinLicenseLabel object for testing purposes. */ - private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, int confirmation) - throws SQLException, AuthorizeException { + private ClarinLicense createClarinLicense(String name, String definition, String requiredInfo, + Confirmation confirmation) throws SQLException, AuthorizeException { ClarinLicense clarinLicense = ClarinLicenseBuilder.createClarinLicense(context).build(); clarinLicense.setConfirmation(confirmation); clarinLicense.setDefinition(definition); diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalHandleRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalHandleRestRepositoryIT.java index 6447433ecd18..8c8a8775b63a 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalHandleRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/ExternalHandleRestRepositoryIT.java @@ -7,8 +7,12 @@ */ package org.dspace.app.rest; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.when; 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.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -17,21 +21,27 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; import org.dspace.app.rest.matcher.ExternalHandleMatcher; +import org.dspace.app.rest.repository.RandomStringGenerator; import org.dspace.app.rest.test.AbstractControllerIntegrationTest; import org.dspace.authorize.AuthorizeException; import org.dspace.builder.ClarinHandleBuilder; +import org.dspace.handle.HandlePlugin; import org.dspace.handle.external.ExternalHandleConstants; import org.dspace.handle.external.Handle; import org.dspace.handle.service.HandleClarinService; import org.dspace.services.ConfigurationService; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.ObjectUtils; /** @@ -45,12 +55,31 @@ public class ExternalHandleRestRepositoryIT extends AbstractControllerIntegratio @Autowired ConfigurationService configurationService; + private String prefix; + + @SpyBean + private RandomStringGenerator rnd; + List handlesWithMagicURLs = new ArrayList<>(); + private final ObjectMapper mapper = new ObjectMapper(); + + private List handlesBeforeTest; + + private final String urlBlacklistConfig = "shortener.post.url.blacklist.regexps"; + private final String hostBlacklistConfig = "shortener.post.host.blacklist.regexps"; + private String[] urlBlacklist; + private String[] hostBlacklist; + @Before public void setup() throws SQLException, AuthorizeException { + prefix = configurationService.getProperty("shortener.handle.prefix") + "/" ; + urlBlacklist = configurationService.getArrayProperty(urlBlacklistConfig); + hostBlacklist = configurationService.getArrayProperty(hostBlacklistConfig); context.turnOffAuthorisationSystem(); + handlesBeforeTest = handleClarinService.findAll(context); + List magicURLs = this.createMagicURLs(); // create Handles with magicURLs @@ -59,7 +88,7 @@ public void setup() throws SQLException, AuthorizeException { // create Handle org.dspace.handle.Handle handle = ClarinHandleBuilder - .createHandle(context, "123/" + index, magicURL) + .createHandle(context, prefix + index, magicURL) .build(); this.handlesWithMagicURLs.add(handle); index++; @@ -69,6 +98,21 @@ public void setup() throws SQLException, AuthorizeException { context.restoreAuthSystemState(); } + @After + public void cleanUp() throws SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + for (org.dspace.handle.Handle handle : handleClarinService.findAll(context)) { + if (!handlesBeforeTest.contains(handle)) { + handleClarinService.delete(context, handle); + } + } + context.commit(); + context.restoreAuthSystemState(); + + configurationService.setProperty(urlBlacklistConfig, urlBlacklist); + configurationService.setProperty(hostBlacklistConfig, hostBlacklist); + } + @Test public void findAllExternalHandles() throws Exception { // call endpoint which should return external handles @@ -90,25 +134,262 @@ public void findAllExternalHandles() throws Exception { @Test public void updateHandle() throws Exception { - ObjectMapper mapper = new ObjectMapper(); org.dspace.handle.Handle handleToUpdate = this.handlesWithMagicURLs.get(0); String handle = handleToUpdate.getHandle(); + String newUrl = "https://lindat.mff.cuni.cz/#!/services/pmltq/"; - String repositoryName = configurationService.getProperty("dspace.name"); - String token = "token0"; - String updatedMagicURL = "@magicLindat@@magicLindat@" + repositoryName + - "@magicLindat@@magicLindat@@magicLindat@@magicLindat@@magicLindat@@magicLindat@" + token + - "@magicLindat@https://lindat.mff.cuni.cz/#!/services/pmltq/"; - Handle updatedHandle = new Handle(handle, updatedMagicURL); String authTokenAdmin = getAuthToken(admin.getEmail(), password); - getClient(authTokenAdmin).perform(put("/api/services/handles") - .content(mapper.writeValueAsBytes(updatedHandle)) + Map updatedHandle = Map.of( + "handle", handle, + "url", newUrl, + "token", "token0" + ); + updateHandle(authTokenAdmin, updatedHandle); + } + + // try create handle more than once + @Test + public void simulateHittingAnExistingHandleButThenSucceed() throws Exception { + when(rnd.generate(4)).thenReturn("0") + .thenReturn("1").thenReturn("2").thenReturn("3"); + MvcResult result = createHandle(null, Map.of( + "url", "https://lindat.mff.cuni.cz/#!/services/pmltq/", + "title", "title", + "reportemail", "reporteMail" + )); + Map handleResponse = mapper.readValue(result.getResponse().getContentAsString(), + Map.class); + Assert.assertEquals("Handle should be created", handleResponse.get("handle"), + HandlePlugin.getCanonicalHandlePrefix() + prefix + "3"); + } + + @Test + public void createAndUpdateHandleByAdmin() throws Exception { + + String authToken = getAuthToken(admin.getEmail(), password); + + String url = "https://lindat.mff.cuni.cz/#!/services/pmltq/"; + Map handleJson = Map.of( + "url", url, + "title", "title", + "reportemail", "reporteMail", + "subprefix", "subprefix", + "datasetName", "datasetName", + "datasetVersion", "datasetVersion", + "query", "query" + ); + // create new handle + MvcResult createResult = createHandle(authToken, handleJson); + + Map handleResponse = mapper.readValue(createResult.getResponse().getContentAsString(), + Map.class); + String handle = handleResponse.get("handle"); + + Assert.assertTrue("Handle contains subprefix", handle.contains("subprefix")); + + String token = handleResponse.get("token"); + String newUrl = "https://lindat.cz/#!/services/pmltq/"; + Map updatedHandleJson = Map.of( + "handle", handle, + "url", newUrl, + "token", token, + "title", "IGNORE", + "reportemail", "IGNORE", + "subprefix", "IGNORE", + "datasetName", "IGNORE", + "datasetVersion", "IGNORE", + "query", "IGNORE" + ); + + // remember how many handles we have + int count = handleClarinService.count(context); + + // update the handle + MvcResult result = updateHandle(authToken, updatedHandleJson); + Assert.assertFalse("Only the URL should be updated", result.getResponse().getContentAsString().contains( + "IGNORE")); + Assert.assertEquals("Update should not create new handles.", count, handleClarinService.count(context)); + } + + // create/update no login + @Test + public void createHandleAsAnonymous() throws Exception { + String authToken = null; + + String url = "https://lindat.mff.cuni.cz/#!/services/pmltq/"; + Map handleJson = Map.of( + "url", url, + "title", "title", + "reportemail", "reporteMail", + "subprefix", "subprefix", + "datasetName", "datasetName", + "datasetVersion", "datasetVersion", + "query", "query" + ); + // create new handle + MvcResult createResult = createHandle(authToken, handleJson); + + Map handleResponse = mapper.readValue(createResult.getResponse().getContentAsString(), + Map.class); + String handle = handleResponse.get("handle"); + String token = handleResponse.get("token"); + String newUrl = "https://lindat.cz/#!/services/pmltq/"; + Map updatedHandleJson = Map.of( + "handle", handle, + "url", newUrl, + "token", token + ); + + // remember how many handles we have + int count = handleClarinService.count(context); + + // update the handle + updateHandle(authToken, updatedHandleJson); + + Assert.assertEquals("Update should not create new handles.", count, handleClarinService.count(context)); + } + + // update works only with token + @Test + public void updateWithValidTokenOnly() throws Exception { + String hdl = prefix + "0"; + Map updateWithInvalid = Map.of( + "handle", hdl, + "token", "INVALID", + "url", "https://lindat.cz" + ); + getClient().perform(put("/api/services/handles") + .content(mapper.writeValueAsBytes(updateWithInvalid)) .contentType(contentType)) + .andExpect(status().isNotFound()); + + Map updateWithValid = Map.of( + "handle", HandlePlugin.getCanonicalHandlePrefix() + hdl, + "token", "token0", + "url", "https://lindat.cz" + ); + MvcResult result = updateHandle(null, updateWithValid); + Map map = mapper.readValue(result.getResponse().getContentAsString(), Map.class); + + // did we get a token that actually works? + Map updateWithValid2 = Map.of( + "handle", HandlePlugin.getCanonicalHandlePrefix() + hdl, + "token", map.get("token"), + "url", "https://lindat.cz/test1" + ); + + updateHandle(null, updateWithValid2); + + } + + // the token or magic url does not leak (it has the token) + @Test + public void noTokenInGetResponse() throws Exception { + MvcResult result = getClient().perform(get("/api/services/handles/magic")) .andExpect(status().isOk()) - .andExpect(jsonPath("$.url", is(updatedHandle.url))) + .andExpect(content().contentType(MediaType.valueOf("application/json;charset=UTF-8"))) + .andReturn() ; + String content = result.getResponse().getContentAsString(); + Assert.assertFalse("token key should not be in the response", content.contains("\"token\":")); + Assert.assertFalse("Token should not be in the response", content.contains("token0")); + } + + @Test + public void noMagicInCreateResponse() throws Exception { + String url = "https://lindat.mff.cuni.cz/#!/services/pmltq/"; + Map handleJson = Map.of( + "url", url, + "title", "title", + "reportemail", "reporteMail", + "subprefix", "subprefix", + "datasetName", "datasetName", + "datasetVersion", "datasetVersion", + "query", "query" + ); + MvcResult result = createHandle(null, handleJson); + + String content = result.getResponse().getContentAsString(); + Assert.assertFalse("magic bean should not be in the response", + content.contains(ExternalHandleConstants.MAGIC_BEAN)); + } + + @Test + public void hostBlacklist() throws Exception { + configurationService.setProperty(urlBlacklistConfig, null); + configurationService.setProperty(hostBlacklistConfig, ".*\\.com;.*\\.cz;.*\\.app"); + Assert.assertEquals(1, configurationService.getArrayProperty(hostBlacklistConfig).length); + for (String tld : new String[] {".com", ".cz", ".app"}) { + getClient().perform(post("/api/services/handles") + .content(mapper.writeValueAsBytes(Map.of( + "url", "https://example" + tld, + "title", "title", + "reportemail", "reporteMail" + ))) + .contentType(contentType)) + .andExpect(status().isBadRequest()); + } + } + @Test + public void urlBlacklist() throws Exception { + getClient().perform(post("/api/services/handles") + .content(mapper.writeValueAsBytes(Map.of( + "url", "https://junk", + "title", "title", + "reportemail", "reporteMail" + ))) + .contentType(contentType)) + .andExpect(status().isBadRequest()); + } + + @Test + public void urlBlacklistRegexpWithRange() throws Exception { + configurationService.setProperty(hostBlacklistConfig, null); + configurationService.setProperty(urlBlacklistConfig, "http://.*;.*\\..{3\\,4}$,.*/wp-login$"); + Assert.assertEquals(2, configurationService.getArrayProperty(urlBlacklistConfig).length); + List shouldBeBlocked = List.of("http://junk", "https://example.com", "https://localhost:4000/wp-login"); + for (String url : shouldBeBlocked) { + getClient().perform(post("/api/services/handles") + .content(mapper.writeValueAsBytes(Map.of( + "url", url, + "title", "title", + "reportemail", "reporteMail" + ))) + .contentType(contentType)) + .andExpect(status().isBadRequest()); + } + //should not be blocked + createHandle(null, Map.of( + "url", "https://example.cz", + "title", "title", + "reportemail", "email" + )); + } + + private MvcResult updateHandle(String authToken, Map updatedHandleJson) throws Exception { + return getClient(authToken).perform(put("/api/services/handles") + .content(mapper.writeValueAsBytes(updatedHandleJson)) + .contentType(contentType)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.handle", anyOf(is(updatedHandleJson.get("handle")), + is(HandlePlugin.getCanonicalHandlePrefix() + updatedHandleJson.get("handle"))))) + .andExpect(jsonPath("$.token", notNullValue())) + .andExpect(jsonPath("$.url", is(updatedHandleJson.get("url")))) + .andReturn() + ; + } + + private MvcResult createHandle(String authToken, Map handleJson) throws Exception { + return getClient(authToken).perform(post("/api/services/handles") + .content(mapper.writeValueAsBytes(handleJson)) + .contentType(contentType)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.handle", notNullValue())) + .andExpect(jsonPath("$.token", notNullValue())) + .andExpect(jsonPath("$.url", is(handleJson.get("url")))) + .andReturn(); } private List createMagicURLs() { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java index de5c7851e10a..6dd643441783 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamRestRepositoryIT.java @@ -7,6 +7,7 @@ */ package org.dspace.app.rest; +import static org.dspace.app.rest.utils.Utils.DEFAULT_PAGE_SIZE; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -32,6 +33,7 @@ import org.dspace.content.Bitstream; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.service.BundleService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.services.ConfigurationService; @@ -45,9 +47,8 @@ public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegra private static final String METADATABITSTREAM_ENDPOINT = "/api/core/metadatabitstream/"; private static final String METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT = METADATABITSTREAM_ENDPOINT + "search/byHandle"; - private static final String FILE_GRP_TYPE = "ORIGINAL"; + private static final String FILE_GRP_TYPE = "ORIGINAL,TEXT,THUMBNAIL"; private static final String AUTHOR = "Test author name"; - private Collection col; private Item publicItem; private Bitstream bts; @@ -58,6 +59,9 @@ public class MetadataBitstreamRestRepositoryIT extends AbstractControllerIntegra @Autowired AuthorizeService authorizeService; + @Autowired + BundleService bundleService; + @Autowired ConfigurationService configurationService; @@ -68,12 +72,15 @@ public void setup() throws Exception { .withName("Parent Community") .build(); - col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build(); + Collection col = CollectionBuilder.createCollection(context, parentCommunity).withName("Collection").build(); publicItem = ItemBuilder.createItem(context, col) .withAuthor(AUTHOR) .build(); + // create empty THUMBNAIL bundle + bundleService.create(context, publicItem, "THUMBNAIL"); + String bitstreamContent = "ThisIsSomeDummyText"; InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8); bts = BitstreamBuilder. @@ -173,7 +180,7 @@ public void findByHandleEmptyFileGrpType() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.page.totalElements", is(0))) .andExpect(jsonPath("$.page.totalPages", is(0))) - .andExpect(jsonPath("$.page.size", is(20))) + .andExpect(jsonPath("$.page.size", is(DEFAULT_PAGE_SIZE))) .andExpect(jsonPath("$.page.number", is(0))) .andExpect(jsonPath("$._links.self.href", Matchers.containsString(METADATABITSTREAM_SEARCH_BY_HANDLE_ENDPOINT + diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClarinLicenseMatcher.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClarinLicenseMatcher.java index baa3b7cbf2db..636d0669cd53 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClarinLicenseMatcher.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/matcher/ClarinLicenseMatcher.java @@ -33,7 +33,7 @@ public static Matcher matchClarinLicense(ClarinLicense clarinLic hasJsonPath("$.name", is(clarinLicense.getName())), hasJsonPath("$.definition", is(clarinLicense.getDefinition())), hasJsonPath("$.type", is(ClarinLicenseRest.NAME)), - hasJsonPath("$.confirmation", is(clarinLicense.getConfirmation())), + hasJsonPath("$.confirmation", is(clarinLicense.getConfirmation().getValue())), hasJsonPath("$.requiredInfo", is(clarinLicense.getRequiredInfo())), hasJsonPath("$.bitstreams", Matchers.not(Matchers.empty())), hasJsonPath("$.clarinLicenseLabel", Matchers.not(Matchers.empty())), @@ -49,7 +49,7 @@ public static Matcher matchClarinLicenseWithoutId(ClarinLicense hasJsonPath("$.name", is(clarinLicense.getName())), hasJsonPath("$.definition", is(clarinLicense.getDefinition())), hasJsonPath("$.type", is(ClarinLicenseRest.NAME)), - hasJsonPath("$.confirmation", is(clarinLicense.getConfirmation())), + hasJsonPath("$.confirmation", is(clarinLicense.getConfirmation().getValue())), hasJsonPath("$.requiredInfo", is(clarinLicense.getRequiredInfo())), hasJsonPath("$.bitstreams", Matchers.not(Matchers.empty())), hasJsonPath("$.clarinLicenseLabel", Matchers.not(Matchers.empty())), diff --git a/dspace/config/clarin-dspace.cfg b/dspace/config/clarin-dspace.cfg index 158bf2afb05d..8386434d526a 100644 --- a/dspace/config/clarin-dspace.cfg +++ b/dspace/config/clarin-dspace.cfg @@ -53,11 +53,14 @@ handle.plugin.checknameauthority = false # shortener.enabled = ${lr.shortener.enabled} shortener.handle.prefix = 1234 # separate the patterns with ; that is less likely to appear as pattern char -shortener.post.url.blacklist.regexps = .*://.\{1,15\} -shortener.post.host.whitelist.regexps = (.*\\.)?google\\.com -shortener.post.host.blacklist.regexps = .*\\.com;.*\\.xyz;.*\\.ga;.*\\.br;.*\\.app -shortener.post.error = Data POSTed by you didn't pass the validation. Please check that you've supplied the title, \ -the email and the url is not on *.com domain and the part after schema:// is longer than 15 chars +# **NOTE**: The config value is always split on `,` escape any commas in your patterns e.g.: `.*\\..{3\\,4}$` +# everything blacklisted by default +shortener.post.url.blacklist.regexps = .* +# except lindat +shortener.post.host.whitelist.regexps = lindat(\\.mff\\.cuni)?.cz +shortener.post.host.blacklist.regexps = .* +shortener.post.error = Data submitted by you didn't pass the validation. Please check that you've supplied the title, \ +the email and the url is not blacklisted. ##### HELP DESK ##### # `lr.help.mail` is exposed to the FE - must be set as exposed