mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-07 01:54:22 +00:00
Request-a-copy: Access expiry sent as delta or date, stored as date
This commit is contained in:
@@ -72,6 +72,12 @@ public class RequestItem implements ReloadableEntity<Integer> {
|
||||
@Column(name = "accept_request")
|
||||
private boolean accept_request;
|
||||
|
||||
@Column(name = "access_token", unique = true, length = 48)
|
||||
private String access_token = null;
|
||||
|
||||
@Column(name = "access_expiry")
|
||||
private Instant access_expiry = null;
|
||||
|
||||
/**
|
||||
* Protected constructor, create object using:
|
||||
* {@link org.dspace.app.requestitem.service.RequestItemService#createRequest(
|
||||
@@ -85,7 +91,7 @@ public class RequestItem implements ReloadableEntity<Integer> {
|
||||
return requestitem_id;
|
||||
}
|
||||
|
||||
void setAllfiles(boolean allfiles) {
|
||||
public void setAllfiles(boolean allfiles) {
|
||||
this.allfiles = allfiles;
|
||||
}
|
||||
|
||||
@@ -134,7 +140,8 @@ public class RequestItem implements ReloadableEntity<Integer> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a unique request identifier which can be emailed.
|
||||
* @return a unique request identifier which can be emailed to the *approver* of the request.
|
||||
* This is not the same as the access token, which is used by the requester to access the item after approval.
|
||||
*/
|
||||
public String getToken() {
|
||||
return token;
|
||||
@@ -187,4 +194,38 @@ public class RequestItem implements ReloadableEntity<Integer> {
|
||||
void setRequest_date(Instant request_date) {
|
||||
this.request_date = request_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A unique token to be used by the requester when granted access to the resource, which
|
||||
* can be emailed upon approval
|
||||
*/
|
||||
public String getAccess_token() {
|
||||
return access_token;
|
||||
}
|
||||
|
||||
public void setAccess_token(String access_token) {
|
||||
this.access_token = access_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The date and time when the access token expires.
|
||||
*/
|
||||
public Instant getAccess_expiry() {
|
||||
return access_expiry;
|
||||
}
|
||||
public void setAccess_expiry(Instant access_expiry) {
|
||||
this.access_expiry = access_expiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize personal information and the approval token, to be used when returning a RequestItem
|
||||
* to Angular, especially for users clicking on the secure link
|
||||
*/
|
||||
public void sanitizePersonalData() {
|
||||
setReqEmail("sanitized");
|
||||
setReqName("sanitized");
|
||||
setReqMessage("sanitized");
|
||||
// Even though [approval] token is not a name, it can be used to access the original object
|
||||
setToken("sanitized");
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,9 @@ package org.dspace.app.requestitem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.annotation.ManagedBean;
|
||||
@@ -175,6 +177,12 @@ public class RequestItemEmailNotifier {
|
||||
grantorAddress = grantor.getEmail();
|
||||
}
|
||||
|
||||
// Set date format for access expiry date
|
||||
String accessExpiryFormat = configurationService.getProperty("request.item.grant.link.dateformat",
|
||||
"yyyy-MM-dd");
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(accessExpiryFormat)
|
||||
.withZone(ZoneId.of("UTC"));
|
||||
|
||||
Email email;
|
||||
// If this item has a secure access token, send the template with that link instead of attaching files
|
||||
if (ri.isAccept_request() && ri.getAccess_token() != null) {
|
||||
@@ -201,11 +209,10 @@ public class RequestItemEmailNotifier {
|
||||
// {6} secure access link
|
||||
email.addArgument(configurationService.getProperty("dspace.ui.url")
|
||||
+ "/items/" + ri.getItem().getID()
|
||||
+ "/access-by-token?accessToken=" + ri.getAccess_token());
|
||||
// {7} access end date
|
||||
if (ri.getAccess_period() > 0) {
|
||||
DateFormat dateFormat = DateFormat.getDateInstance();
|
||||
email.addArgument(dateFormat.format(ri.getAccessEndDate()));
|
||||
+ "?accessToken=" + ri.getAccess_token());
|
||||
// {7} access end date, but only add formatted date string if it is set and not "forever"
|
||||
if (ri.getAccess_expiry() != null && !ri.getAccess_expiry().equals(Instant.MAX)) {
|
||||
email.addArgument(dateTimeFormatter.format(ri.getAccess_expiry()));
|
||||
} else {
|
||||
email.addArgument(null);
|
||||
}
|
||||
|
@@ -11,9 +11,16 @@ import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -32,8 +39,9 @@ import org.dspace.core.Constants;
|
||||
import org.dspace.core.Context;
|
||||
import org.dspace.core.LogHelper;
|
||||
import org.dspace.core.Utils;
|
||||
import org.dspace.eperson.EPerson;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.util.DateMathParser;
|
||||
import org.dspace.util.MultiFormatDateParser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
@@ -61,6 +69,11 @@ public class RequestItemServiceImpl implements RequestItemService {
|
||||
@Autowired
|
||||
protected ConfigurationService configurationService;
|
||||
|
||||
/**
|
||||
* Always set UTC for dateMathParser for consistent database date handling
|
||||
*/
|
||||
static DateMathParser dateMathParser = new DateMathParser(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
private static final int DEFAULT_MINIMUM_FILE_SIZE = 20;
|
||||
|
||||
protected RequestItemServiceImpl() {
|
||||
@@ -145,8 +158,8 @@ public class RequestItemServiceImpl implements RequestItemService {
|
||||
// Save the request item
|
||||
requestItemDAO.save(context, requestItem);
|
||||
|
||||
log.debug("Created RequestItem with ID {}, approval token {}, access token {}, access period {}",
|
||||
requestItem::getID, requestItem::getToken, requestItem::getAccess_token, requestItem::getAccess_period);
|
||||
log.debug("Created RequestItem with ID {}, approval token {}, access token {}, access expiry {}",
|
||||
requestItem::getID, requestItem::getToken, requestItem::getAccess_token, requestItem::getAccess_expiry);
|
||||
|
||||
// Return the approver token
|
||||
return requestItem.getToken();
|
||||
@@ -225,6 +238,35 @@ public class RequestItemServiceImpl implements RequestItemService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the access expiry date for the request item.
|
||||
* @param requestItem the request item to update
|
||||
* @param accessExpiry the expiry date to set
|
||||
*/
|
||||
@Override
|
||||
public void setAccessExpiry(RequestItem requestItem, Instant accessExpiry) {
|
||||
requestItem.setAccess_expiry(accessExpiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a string either as a formatted date, or in the "math" format expected by
|
||||
* the DateMathParser, e.g. +7DAYS or +10MONTHS, and set the access expiry date accordingly.
|
||||
* There are no special checks here to check that the date is in the future, or after the
|
||||
* 'decision date', as there may be legitimate reasons to set past dates.
|
||||
* If past dates are not allowed by some interface, then the caller should check this.
|
||||
*
|
||||
* @param requestItem the request item to update
|
||||
* @param dateOrDelta the delta as a string in format expected by the DateMathParser
|
||||
*/
|
||||
@Override
|
||||
public void setAccessExpiry(RequestItem requestItem, String dateOrDelta) {
|
||||
try {
|
||||
setAccessExpiry(requestItem, parseDateOrDelta(dateOrDelta, requestItem.getDecision_date()));
|
||||
} catch (ParseException e) {
|
||||
log.error("Error parsing access expiry or duration: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taking into account 'accepted' flag, bitstream id or allfiles flag, decision date and access period,
|
||||
* either return cleanly or throw an AuthorizeException
|
||||
@@ -247,9 +289,8 @@ public class RequestItemServiceImpl implements RequestItemService {
|
||||
&& (requestItem.getAccess_token() != null && requestItem.getAccess_token().equals(accessToken))
|
||||
// 3. Request is 'allfiles' or for this bitstream ID
|
||||
&& (requestItem.isAllfiles() || bitstream.equals(requestItem.getBitstream()))
|
||||
// 4. access period is 0 (forever), or the elapsed seconds since decision date is less than the
|
||||
// access period granted
|
||||
&& requestItem.accessPeriodCurrent()
|
||||
// 4. access expiry timestamp is null (forever), or is *after* the current time
|
||||
&& (requestItem.getAccess_expiry() == null || requestItem.getAccess_expiry().isAfter(Instant.now()))
|
||||
) {
|
||||
log.info("Authorizing access to bitstream {} by access token", bitstream.getID());
|
||||
return;
|
||||
@@ -306,8 +347,7 @@ public class RequestItemServiceImpl implements RequestItemService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a RequestItem depending on the current session user. If the current user is not
|
||||
* the approver, an administrator or other privileged group, the following values in the return object
|
||||
* Sanitize a RequestItem. The following values in the referenced RequestItem
|
||||
* are nullified:
|
||||
* - approver token (aka token)
|
||||
* - requester name
|
||||
@@ -325,28 +365,39 @@ public class RequestItemServiceImpl implements RequestItemService {
|
||||
log.error("Null request item passed for sanitization, skipping");
|
||||
return;
|
||||
}
|
||||
if (null != context) {
|
||||
// Get current user, if any
|
||||
EPerson currentUser = context.getCurrentUser();
|
||||
// Get item
|
||||
Item item = requestItem.getItem();
|
||||
if (null != currentUser) {
|
||||
try {
|
||||
if (currentUser == requestItem.getItem().getSubmitter()
|
||||
&& authorizeService.isAdmin(context, requestItem.getItem())) {
|
||||
// Return original object, this person technically had full access to the request item data via
|
||||
// the original approval link
|
||||
log.debug("User is authorized to receive all request item data: {}", currentUser.getEmail());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Could not determine isAdmin for item {}: {}",item.getID(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By default, sanitize (strips requester name, email, message, and the approver token)
|
||||
// This is the case if we have a non-admin, non-submitter or a null user/session
|
||||
// Sanitized referenced data (strips requester name, email, message, and the approver token)
|
||||
requestItem.sanitizePersonalData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date or delta string into an Instant. Kept here as a static method for use in unit tests
|
||||
* and other areas that might not have access to the full spring service
|
||||
*
|
||||
* @param dateOrDelta
|
||||
* @param decisionDate
|
||||
* @return parsed date as instant
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static Instant parseDateOrDelta(String dateOrDelta, Instant decisionDate) throws ParseException, DateTimeException {
|
||||
// First, if dateOrDelta is a null string or "FOREVER", we will set the expiry
|
||||
// date to a very distant date in the future.
|
||||
if (dateOrDelta == null || dateOrDelta.equals("FOREVER")) {
|
||||
return Instant.MAX;
|
||||
}
|
||||
// Next, try parsing as a straight date using the multiple format parser
|
||||
ZonedDateTime parsedExpiryDate = MultiFormatDateParser.parse(dateOrDelta);
|
||||
|
||||
if (parsedExpiryDate == null) {
|
||||
// That did not work, so try parsing as a delta
|
||||
// Set the 'now' date to the decision date of the request item
|
||||
dateMathParser.setNow(LocalDateTime.ofInstant(decisionDate, ZoneOffset.UTC));
|
||||
// Parse the delta (e.g. +7DAYS) and set the new access expiry date
|
||||
return dateMathParser.parseMath(dateOrDelta).toInstant(ZoneOffset.UTC);
|
||||
}
|
||||
else {
|
||||
// The expiry date was a valid formatted date string, so set the access expiry date
|
||||
return parsedExpiryDate.toInstant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ package org.dspace.app.requestitem.service;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@@ -112,6 +113,10 @@ public interface RequestItemService {
|
||||
public boolean isRestricted(Context context, DSpaceObject o)
|
||||
throws SQLException;
|
||||
|
||||
void setAccessExpiry(RequestItem requestItem, Instant accessExpiry);
|
||||
|
||||
void setAccessExpiry(RequestItem requestItem, String delta);
|
||||
|
||||
/**
|
||||
* Taking into account 'accepted' flag, bitstream id or allfiles flag, decision date and access period,
|
||||
* either return cleanly or throw an AuthorizeException
|
||||
|
@@ -8,6 +8,6 @@
|
||||
|
||||
-- Add new access_token column to hold a secure access token for the requestor to use for weblink-based access
|
||||
ALTER TABLE requestitem ADD COLUMN IF NOT EXISTS access_token VARCHAR(48);
|
||||
-- Add new access_period column to hold a time delta in seconds (from decision_date timestamp) to calculate validity
|
||||
-- and expiry, with NULL interpreted as 'forever' (if accept_request is true). int4 allows for 68 year period max.
|
||||
ALTER TABLE requestitem ADD COLUMN IF NOT EXISTS access_period INT4;
|
||||
-- Add new access_expiry DATESTAMP column to hold the expiry date of the access token
|
||||
-- (note this is separate from the existing 'expires' column which was intended as the expiry date of the request itself)
|
||||
ALTER TABLE requestitem ADD COLUMN IF NOT EXISTS access_expiry TIMESTAMP;
|
@@ -21,6 +21,13 @@ import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.sql.Date;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.dspace.AbstractUnitTest;
|
||||
@@ -44,6 +51,7 @@ import org.dspace.handle.factory.HandleServiceFactory;
|
||||
import org.dspace.handle.service.HandleService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||
import org.dspace.util.MultiFormatDateParser;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
@@ -162,12 +170,13 @@ public class RequestItemTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
public void testAuthorizeWithValidPeriod() throws Exception {
|
||||
Instant decisionDate = getYesterdayAsInstant();
|
||||
RequestItem request = RequestItemBuilder
|
||||
.createRequestItem(context, item, bitstream)
|
||||
.withAcceptRequest(true)
|
||||
.withAccessToken("test-token")
|
||||
.withDecisionDate(new Date(System.currentTimeMillis() + 86400000)) // Yesterday
|
||||
.withAccessPeriod(10 * 86400) // 10 day period
|
||||
.withDecisionDate(decisionDate) // Yesterday
|
||||
.withAccessExpiry(getExpiryAsInstant("+10DAYS", decisionDate)) // 10 day period
|
||||
.build();
|
||||
|
||||
// The access token should be valid so we expect no exceptions
|
||||
@@ -181,12 +190,13 @@ public class RequestItemTest extends AbstractUnitTest {
|
||||
|
||||
@Test(expected = AuthorizeException.class)
|
||||
public void testAuthorizeWithExpiredPeriod() throws Exception {
|
||||
Instant decisionDate = getYesterdayAsInstant();
|
||||
RequestItem request = RequestItemBuilder
|
||||
.createRequestItem(context, item, bitstream)
|
||||
.withAcceptRequest(true)
|
||||
.withAccessToken("test-token")
|
||||
.withDecisionDate(new Date(System.currentTimeMillis() - 86400000)) // Yesterday
|
||||
.withAccessPeriod(86400) // 1 day period
|
||||
.withDecisionDate(decisionDate) // Yesterday
|
||||
.withAccessExpiry(getExpiryAsInstant("+1DAY", decisionDate)) // 1 day period
|
||||
.build();
|
||||
|
||||
// The access token should not be valid so we expect to catch an AuthorizeException
|
||||
@@ -207,12 +217,13 @@ public class RequestItemTest extends AbstractUnitTest {
|
||||
|
||||
@Test(expected = AuthorizeException.class)
|
||||
public void testAuthorizeWithMismatchedToken() throws Exception {
|
||||
Instant decisionDate = getYesterdayAsInstant();
|
||||
RequestItem request = RequestItemBuilder
|
||||
.createRequestItem(context, item, bitstream)
|
||||
.withAcceptRequest(true)
|
||||
.withAccessToken("test-token")
|
||||
.withDecisionDate(new Date(System.currentTimeMillis() - 86400000)) // Yesterday
|
||||
.withAccessPeriod(0) // forever
|
||||
.withDecisionDate(decisionDate) // Yesterday
|
||||
.withAccessExpiry(getExpiryAsInstant("FOREVER", decisionDate)) // forever
|
||||
.build();
|
||||
|
||||
// The access token should NOT valid so we expect to catch an AuthorizeException
|
||||
@@ -262,29 +273,32 @@ public class RequestItemTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
public void testGrantRequestWithAccessPeriod() throws Exception {
|
||||
Instant decisionDate = Instant.now();
|
||||
Instant expectedExpiryDate = decisionDate.plus(7, ChronoUnit.DAYS);
|
||||
RequestItem request = RequestItemBuilder
|
||||
.createRequestItem(context, item, bitstream)
|
||||
.build();
|
||||
|
||||
request.setAccept_request(true);
|
||||
request.setDecision_date(new Date(System.currentTimeMillis()));
|
||||
request.setAccess_period(7); // 7 day access
|
||||
request.setDecision_date(decisionDate);
|
||||
request.setAccess_expiry(getExpiryAsInstant("+7DAYS", decisionDate)); // 7 day access
|
||||
requestItemService.update(context, request);
|
||||
|
||||
RequestItem found = requestItemService.findByToken(context, request.getToken());
|
||||
assertTrue(found.isAccept_request());
|
||||
assertNotNull(found.getDecision_date());
|
||||
assertEquals(7, found.getAccess_period());
|
||||
assertEquals(decisionDate, found.getDecision_date());
|
||||
assertEquals(expectedExpiryDate, found.getAccess_expiry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDenyRequest() throws Exception {
|
||||
Instant decisionDate = Instant.now();
|
||||
RequestItem request = RequestItemBuilder
|
||||
.createRequestItem(context, item, bitstream)
|
||||
.build();
|
||||
|
||||
request.setAccept_request(false);
|
||||
request.setDecision_date(new Date(System.currentTimeMillis()));
|
||||
request.setDecision_date(decisionDate);
|
||||
requestItemService.update(context, request);
|
||||
|
||||
RequestItem found = requestItemService.findByToken(context, request.getToken());
|
||||
@@ -314,19 +328,22 @@ public class RequestItemTest extends AbstractUnitTest {
|
||||
|
||||
@Test
|
||||
public void testModifyGrantedRequest() throws Exception {
|
||||
Instant decisionDate = Instant.now();
|
||||
Instant expectedExpiryDate = decisionDate.plus(10, ChronoUnit.DAYS);
|
||||
RequestItem request = RequestItemBuilder
|
||||
.createRequestItem(context, item, bitstream)
|
||||
.withAcceptRequest(true)
|
||||
.withDecisionDate(new Date(System.currentTimeMillis()))
|
||||
.withAccessPeriod(30)
|
||||
.withDecisionDate(decisionDate)
|
||||
.withAccessExpiry(getExpiryAsInstant("+1DAY", decisionDate))
|
||||
.build();
|
||||
|
||||
request.setAccess_period(60);
|
||||
// Manually set new expiry date
|
||||
request.setAccess_expiry(getExpiryAsInstant("+10DAYS", decisionDate));
|
||||
request.setAllfiles(true);
|
||||
requestItemService.update(context, request);
|
||||
|
||||
RequestItem found = requestItemService.findByToken(context, request.getToken());
|
||||
assertEquals(60, found.getAccess_period());
|
||||
assertEquals(expectedExpiryDate, found.getAccess_expiry());
|
||||
assertTrue(found.isAccept_request());
|
||||
assertTrue(found.isAllfiles());
|
||||
}
|
||||
@@ -359,6 +376,17 @@ public class RequestItemTest extends AbstractUnitTest {
|
||||
configurationService.reloadConfig();
|
||||
}
|
||||
|
||||
private Instant getYesterdayAsInstant() {
|
||||
return Instant.now().minus(Duration.ofDays(1));
|
||||
}
|
||||
|
||||
private Instant getExpiryAsInstant(String dateOrDelta, Instant decision) {
|
||||
try {
|
||||
return RequestItemServiceImpl.parseDateOrDelta(dateOrDelta, decision);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ public class RequestItemBuilder
|
||||
private Instant decisionDate;
|
||||
private boolean accepted;
|
||||
private String accessToken = null;
|
||||
private int accessPeriod;
|
||||
private Instant accessExpiry = null;
|
||||
private boolean allFiles;
|
||||
|
||||
protected RequestItemBuilder(Context context) {
|
||||
@@ -95,8 +95,8 @@ public class RequestItemBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
public RequestItemBuilder withAccessPeriod(int accessPeriod) {
|
||||
this.accessPeriod = accessPeriod;
|
||||
public RequestItemBuilder withAccessExpiry(Instant accessExpiry) {
|
||||
this.accessExpiry = accessExpiry;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class RequestItemBuilder
|
||||
if (accessToken != null) {
|
||||
requestItem.setAccess_token(accessToken);
|
||||
}
|
||||
requestItem.setAccess_period(accessPeriod);
|
||||
requestItem.setAccess_expiry(accessExpiry);
|
||||
requestItem.setAllfiles(allFiles);
|
||||
|
||||
requestItemService.update(context, requestItem);
|
||||
|
@@ -46,7 +46,7 @@ public class RequestItemConverter
|
||||
requestItemRest.setRequestDate(requestItem.getRequest_date());
|
||||
requestItemRest.setToken(requestItem.getToken());
|
||||
requestItemRest.setAccessToken(requestItem.getAccess_token());
|
||||
requestItemRest.setAccessPeriod(requestItem.getAccess_period());
|
||||
requestItemRest.setAccessExpiry(requestItem.getAccess_expiry());
|
||||
return requestItemRest;
|
||||
}
|
||||
|
||||
|
@@ -56,9 +56,7 @@ public class RequestItemRest extends BaseObjectRest<Integer> {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
protected String accessToken;
|
||||
|
||||
protected int accessPeriod;
|
||||
|
||||
protected String decisionNote;
|
||||
protected Instant accessExpiry;
|
||||
|
||||
/**
|
||||
* @return the bitstream requested.
|
||||
@@ -229,17 +227,17 @@ public class RequestItemRest extends BaseObjectRest<Integer> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return access period (in seconds) which indicates how long this access is valid from the decision date
|
||||
* @return the date the access token expires.
|
||||
*/
|
||||
public int getAccessPeriod() {
|
||||
return accessPeriod;
|
||||
public Instant getAccessExpiry() {
|
||||
return this.accessExpiry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accessPeriod access period, in seconds, indicating how long this access is valid from the decision date
|
||||
* @param accessExpiry the date the access token expires.
|
||||
*/
|
||||
public void setAccessPeriod(int accessPeriod) {
|
||||
this.accessPeriod = accessPeriod;
|
||||
public void setAccessExpiry(Instant accessExpiry) {
|
||||
this.accessExpiry = accessExpiry;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -14,14 +14,19 @@ import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.sql.SQLException;
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.validator.routines.EmailValidator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -49,6 +54,10 @@ import org.dspace.eperson.InvalidReCaptchaException;
|
||||
import org.dspace.eperson.factory.CaptchaServiceFactory;
|
||||
import org.dspace.eperson.service.CaptchaService;
|
||||
import org.dspace.services.ConfigurationService;
|
||||
import org.dspace.util.DateMathParser;
|
||||
import static org.dspace.util.MultiFormatDateParser.parse;
|
||||
|
||||
import org.dspace.util.MultiFormatDateParser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -291,15 +300,13 @@ public class RequestItemRepository
|
||||
// Set the decision date (now)`
|
||||
ri.setDecision_date(Instant.now());
|
||||
|
||||
// If the (optional) access period was included, extract it here and set accordingly
|
||||
JsonNode accessPeriodNode = requestBody.findValue("accessPeriod");
|
||||
int accessPeriod = 0;
|
||||
if (accessPeriodNode != null && !accessPeriodNode.isNull()) {
|
||||
accessPeriod = accessPeriodNode.asInt(0);
|
||||
}
|
||||
// If a valid access period was set, update the request, otherwise we will leave it as null
|
||||
if (accessPeriod > 0) {
|
||||
ri.setAccess_period(accessPeriod);
|
||||
// If the (optional) access expiry period was included, extract it here and set accordingly
|
||||
// We expect it to be sent either as a timestamp or as a delta math like +7DAYS
|
||||
JsonNode accessPeriod = requestBody.findValue("accessPeriod");
|
||||
if (accessPeriod != null && !accessPeriod.isNull()) {
|
||||
// The request item service is responsible for parsing and setting the expiry date based
|
||||
// on a delta like "+7DAYS" or special string like "FOREVER", or a formatted date
|
||||
requestItemService.setAccessExpiry(ri, accessPeriod.asText());
|
||||
}
|
||||
|
||||
JsonNode responseSubjectNode = requestBody.findValue("subject");
|
||||
@@ -307,7 +314,6 @@ public class RequestItemRepository
|
||||
if (responseSubjectNode != null && !responseSubjectNode.isNull()) {
|
||||
subject = responseSubjectNode.asText();
|
||||
}
|
||||
ri.setDecision_date(Instant.now());
|
||||
requestItemService.update(context, ri);
|
||||
|
||||
// Send the response email
|
||||
@@ -356,8 +362,10 @@ public class RequestItemRepository
|
||||
throw new ResourceNotFoundException("No such request item for accessToken=" + accessToken);
|
||||
}
|
||||
|
||||
// Send 403 FORBIDDEN if request access has not been granted or access period is in the past
|
||||
if (!requestItem.isAccept_request() || !requestItem.accessPeriodCurrent()) {
|
||||
// Send 403 FORBIDDEN if request access has not been granted or access period is null or in the past
|
||||
if (!requestItem.isAccept_request() ||
|
||||
requestItem.getAccess_expiry() == null ||
|
||||
requestItem.getAccess_expiry().isBefore(Instant.now())) {
|
||||
throw new AccessDeniedException("Access has not been granted for this item request");
|
||||
}
|
||||
|
||||
|
@@ -34,6 +34,7 @@ import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
@@ -7,37 +7,49 @@
|
||||
# logged - Login is mandatory to request an item
|
||||
# empty/commented out - request-copy not allowed
|
||||
#request.item.type = all
|
||||
|
||||
# Should all Request Copy emails go to the helpdesk instead of the item submitter?
|
||||
#request.item.helpdesk.override = false
|
||||
|
||||
# Should a rejection of a copy request send an email back to the requester?
|
||||
# Defaults to "true", which means a rejection email is sent back.
|
||||
# Setting it to "false" results in a silent rejection.
|
||||
#request.item.reject.email = true
|
||||
# Bundles to use when granting access and inspecting file size, if "all files" is indicated
|
||||
# Default: ORIGINAL
|
||||
#request.item.grant.bundles = ORIGINAL
|
||||
# By default a granted request will send the file as an attachment.
|
||||
|
||||
## Bundles to use when granting access and inspecting file size, if "all files" is indicated
|
||||
## Default: ORIGINAL
|
||||
request.item.grant.bundles = ORIGINAL
|
||||
|
||||
## By default a granted request will send the file as an attachment.
|
||||
# If a secure web link to the bitstream(s) should be sent in some cases, enable the following property
|
||||
# Default: false
|
||||
#request.item.grant.link = true
|
||||
|
||||
# If request.item.grant.link is enabled, you can specify a minimum file size (in megabytes) to use as
|
||||
# a threshold, or else the default attachment functionality will be used instead.
|
||||
# If 'all files' was indicated in the request, this threshold will be activated if any of the files
|
||||
# meet the configured size minimum.
|
||||
# To send links instead of attachments for all files, set this property to 0.
|
||||
# Default: 20
|
||||
#request.item.grant.link.filesize = 0
|
||||
request.item.grant.link.filesize = 0
|
||||
|
||||
# Valid access periods, in seconds, to allow the approver to select when using links.
|
||||
# These are presented to users with friendly labels via i18n keys in the angular frontend.
|
||||
# A 0 (or null) access period will be interpreted as 'forever'.
|
||||
# These should be in a format like +<n><unit>, where <n> is the number of units and <unit> is one of
|
||||
# "SECONDS", "MINUTES", "HOURS", "DAYS", "WEEKS", "MONTHS", "YEARS". (or singular if n=1)
|
||||
# The default is FOREVER, which will set a very distant expiry date for permanent access.
|
||||
# The first access period in the list will be the default option in the grant form dropdown.
|
||||
# 1 day = 86400
|
||||
# 7 days = 604800
|
||||
#request.item.grant.link.period = 0
|
||||
#request.item.grant.link.period = 120
|
||||
#request.item.grant.link.period = 240
|
||||
#request.item.grant.link.period = 86400
|
||||
#request.item.grant.link.period = 604800
|
||||
#
|
||||
request.item.grant.link.period = FOREVER
|
||||
request.item.grant.link.period = +1DAY
|
||||
request.item.grant.link.period = +1WEEK
|
||||
request.item.grant.link.period = +1MONTH
|
||||
request.item.grant.link.period = +3MONTHS
|
||||
|
||||
# Date format to use for the access expiry date in the grant email
|
||||
request.item.grant.link.dateformat = dd/MM/yyyy HH:mm:ss
|
||||
#request.item.grant.link.dateformat = MM/dd/yy
|
||||
|
||||
# Require a captcha for item request creation
|
||||
# Default: false
|
||||
#request.item.create.captcha=true
|
||||
|
Reference in New Issue
Block a user