Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ufal/migrate dtq-dev updated to UK customer #459

Merged
merged 2 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.dspace.content.Bitstream;
import org.dspace.content.Item;
import org.dspace.content.clarin.ClarinLicense;
import org.dspace.content.clarin.ClarinLicenseLabel;
import org.dspace.content.clarin.ClarinLicenseResourceMapping;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService;
Expand Down Expand Up @@ -93,15 +92,15 @@ public boolean authorizeBitstream(Context context, Bitstream bitstream) throws S
}

/**
* If the bitstream has RES or ACA license and the user is Anonymous do not authorize that user.
* The user will be redirected to the login.
* Do not allow download for anonymous users. Allow it only if the bitstream has Clarin License and the license has
* confirmation = 3 (allow anonymous).
*
* @param context DSpace context object
* @param bitstreamID downloading Bitstream UUID
* @return if the current user is authorized
*/
public boolean authorizeLicenseWithUser(Context context, UUID bitstreamID) throws SQLException {
// If the current user is null that means that the user is not signed in and cannot download the bitstream
// with RES or ACA license
// If the current user is null that means that the user is not signed
if (Objects.nonNull(context.getCurrentUser())) {
// User is signed
return true;
Expand All @@ -118,16 +117,13 @@ 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();
// Get License Labels from clarin license and check if one of them is ACA or RES
List<ClarinLicenseLabel> clarinLicenseLabels = clarinLicense.getLicenseLabels();
for (ClarinLicenseLabel clarinLicenseLabel : clarinLicenseLabels) {
if (StringUtils.equals(clarinLicenseLabel.getLabel(), "RES") ||
StringUtils.equals(clarinLicenseLabel.getLabel(), "ACA")) {
return false;
}
// 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)) {
return true;
}

return true;
return false;
}

private boolean userIsSubmitter(Context context, Bitstream bitstream, EPerson currentUser, UUID userID) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.dspace.content.Bitstream;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -84,28 +85,30 @@ public ResponseEntity authrn(@PathVariable String id, HttpServletResponse respon
return null;
}

// If the bitstream has RES or ACA license and the user is Anonymous return NotAuthorized exception
// Do not allow download for anonymous users. Allow it only if the bitstream has Clarin License and the license
// has confirmation = 3 (allow anonymous).
if (!authorizationBitstreamUtils.authorizeLicenseWithUser(context, bitstream.getID())) {
response.sendError(HttpStatus.UNAUTHORIZED.value(),
"Anonymous user cannot download bitstream with REC or ACA license");
"Anonymous user cannot download this bitstream");
return null;
}

// Wrap exceptions to the AuthrnRest object.
String errorMessage = "User is not authorized to download the bitstream.";
boolean isAuthorized = false;
String errorMessage = "";

try {
isAuthorized = authorizationBitstreamUtils.authorizeBitstream(context, bitstream);
authorizeService.authorizeAction(context, bitstream, Constants.READ);
} catch (AuthorizeException e) {
if (e instanceof MissingLicenseAgreementException) {
errorMessage = MissingLicenseAgreementException.NAME;
} else if (e instanceof DownloadTokenExpiredException) {
errorMessage = DownloadTokenExpiredException.NAME;
} else {
errorMessage = e.getMessage();
}
}

if (!isAuthorized) {
if (StringUtils.isNotBlank(errorMessage)) {
// If the user is not authorized return response with the error message
response.sendError(HttpStatus.UNAUTHORIZED.value(), errorMessage);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,10 @@ public BitstreamRest importBitstreamForExistingFile(HttpServletRequest request)
log.info("SequenceId is null. Bitstream UUID: " + bitstream.getID());
}
//add bitstream format
String bitstreamFormatIdString = request.getParameter("bitstreamFormat");
Integer bitstreamFormatId = getIntegerFromString(bitstreamFormatIdString);
String bitstreamFormatMimeType = request.getParameter("bitstreamFormat");
BitstreamFormat bitstreamFormat = null;
if (!Objects.isNull(bitstreamFormatId)) {
bitstreamFormat = bitstreamFormatService.find(context, bitstreamFormatId);
if (StringUtils.isNotBlank(bitstreamFormatMimeType)) {
bitstreamFormat = bitstreamFormatService.findByMIMEType(context, bitstreamFormatMimeType);
}
bitstream.setFormat(context, bitstreamFormat);
String deletedString = request.getParameter("deleted");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,53 @@
*/
package org.dspace.app.rest;

import static org.dspace.app.rest.utils.RegexUtils.REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.zip.Deflater;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.rest.exception.DSpaceBadRequestException;
import org.dspace.app.rest.exception.UnprocessableEntityException;
import org.dspace.app.rest.model.BitstreamRest;
import org.dspace.app.rest.model.ItemRest;
import org.dspace.app.rest.utils.ContextUtil;
import org.dspace.authorize.AuthorizationBitstreamUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.MissingLicenseAgreementException;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.service.BitstreamService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.handle.service.HandleService;
import org.dspace.services.ConfigurationService;
import org.dspace.services.RequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
/**
* This CLARIN Controller download a single file or a ZIP file from the Item's bitstream.
*/
@RestController
@RequestMapping("/api/" + BitstreamRest.CATEGORY + "/" + BitstreamRest.PLURAL_NAME)
@RequestMapping("/api/" + ItemRest.CATEGORY + "/" + ItemRest.PLURAL_NAME + REGEX_REQUESTMAPPING_IDENTIFIER_AS_UUID)
public class MetadataBitstreamController {

private static Logger log = org.apache.logging.log4j.LogManager.getLogger(MetadataBitstreamController.class);
Expand All @@ -69,90 +67,17 @@ public class MetadataBitstreamController {
private AuthorizeService authorizeService;
@Autowired
private ConfigurationService configurationService;


@GetMapping("/handle/{id}/{subId}/{fileName}")
public ResponseEntity<Resource> downloadSingleFile(@PathVariable("id") String id,
@PathVariable("subId") String subId,
@PathVariable("fileName") String fileName,
HttpServletRequest request, HttpServletResponse response)
throws IOException {
String handleID = id + "/" + subId;
if (StringUtils.isBlank(id) || StringUtils.isBlank(subId)) {
log.error("Handle cannot be null! PathVariable `id` or `subId` is null.");
throw new DSpaceBadRequestException("Handle cannot be null!");
}

Context context = ContextUtil.obtainContext(request);
if (Objects.isNull(context)) {
log.error("Cannot obtain the context from the request.");
throw new RuntimeException("Cannot obtain the context from the request.");
}

DSpaceObject dso = null;
try {
dso = handleService.resolveToObject(context, handleID);
} catch (Exception e) {
log.error("Cannot resolve handle: " + handleID);
throw new RuntimeException("Cannot resolve handle: " + handleID);
}


if (Objects.isNull(dso)) {
log.error("DSO is null");
return null;
}

if (!(dso instanceof Item)) {
log.error("DSO is not instance of Item");
return null;
}

Item item = (Item) dso;
List<Bundle> bundles = item.getBundles();
// Find bitstream and start downloading.
for (Bundle bundle: bundles) {
for (Bitstream bitstream: bundle.getBitstreams()) {
// Authorize the action - it will send response redirect if something gets wrong.
authorizeBitstreamAction(context, bitstream, response);

String btName = bitstream.getName();
if (!(btName.equalsIgnoreCase(fileName))) {
continue;
}
try {
BitstreamFormat bitstreamFormat = bitstream.getFormat(context);
// Check if the bitstream has some extensions e.g., `.txt, .jpg,..`
checkBitstreamExtensions(bitstreamFormat);

// Get content of the bitstream
InputStream inputStream = bitstreamService.retrieve(context, bitstream);
InputStreamResource resource = new InputStreamResource(inputStream);
HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + fileName);
header.add("Cache-Control", "no-cache, no-store, must-revalidate");
header.add("Pragma", "no-cache");
header.add("Expires", "0");
return ResponseEntity.ok()
.headers(header)
.contentLength(inputStream.available())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

return null;
}
@Autowired
AuthorizationBitstreamUtils authorizationBitstreamUtils;
@Autowired
private RequestService requestService;

/**
* Download all Item's bitstreams as single ZIP file.
*/
@GetMapping("/allzip")
public void downloadFileZip(@RequestParam("handleId") String handleId,
@PreAuthorize("hasPermission(#uuid, 'ITEM', 'READ')")
@RequestMapping( method = {RequestMethod.GET, RequestMethod.HEAD}, value = "allzip")
public void downloadFileZip(@PathVariable UUID uuid, @RequestParam("handleId") String handleId,
HttpServletResponse response,
HttpServletRequest request) throws IOException, SQLException, AuthorizeException {
if (StringUtils.isBlank(handleId)) {
Expand Down Expand Up @@ -195,11 +120,11 @@ public void downloadFileZip(@RequestParam("handleId") String handleId,
for (Bundle original : bundles) {
List<Bitstream> bss = original.getBitstreams();
for (Bitstream bitstream : bss) {
authorizeBitstreamAction(context, bitstream, response);

String filename = bitstream.getName();
ZipArchiveEntry ze = new ZipArchiveEntry(filename);
zip.putArchiveEntry(ze);
// Get content of the bitstream
// Retrieve method authorize bitstream download action.
InputStream is = bitstreamService.retrieve(context, bitstream);
IOUtils.copy(is, zip);
zip.closeArchiveEntry();
Expand All @@ -209,37 +134,4 @@ public void downloadFileZip(@RequestParam("handleId") String handleId,
zip.close();
response.getOutputStream().flush();
}

/**
* Could the user download that bitstream?
* @param context DSpace context object
* @param bitstream Bitstream to download
* @param response for possibility to redirect
*/
private void authorizeBitstreamAction(Context context, Bitstream bitstream, HttpServletResponse response)
throws IOException {

String uiURL = configurationService.getProperty("dspace.ui.url");
if (StringUtils.isBlank(uiURL)) {
log.error("Configuration property `dspace.ui.url` cannot be empty or null!");
throw new RuntimeException("Configuration property `dspace.ui.url` cannot be empty or null!");
}
try {
authorizeService.authorizeAction(context, bitstream, Constants.READ);
} catch (MissingLicenseAgreementException e) {
response.sendRedirect(uiURL + "/bitstream/" + bitstream.getID() + "/download");
} catch (AuthorizeException | SQLException e) {
response.sendRedirect(uiURL + "/login");
}
}

/**
* Check if the bitstream has file extension.
*/
private void checkBitstreamExtensions(BitstreamFormat bitstreamFormat) {
if ( Objects.isNull(bitstreamFormat) || CollectionUtils.isEmpty(bitstreamFormat.getExtensions())) {
log.error("Bitstream Extensions cannot be empty for downloading/previewing bitstreams.");
throw new RuntimeException("Bitstream Extensions cannot be empty for downloading/previewing bitstreams.");
}
}
}
Loading
Loading