Skip to content

Commit feb1c02

Browse files
authored
Merge pull request #11349 from IQSS/11299-file-restrict-api-extension
extending restrict api
2 parents fb33fa4 + 9eca4ef commit feb1c02

File tree

6 files changed

+132
-14
lines changed

6 files changed

+132
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
### Extend Restrict API to include new attributes
2+
3+
Original /restrict API only allowed for a boolean to update the restricted attribute of a file.
4+
The extended API still allows for the single boolean for backward compatibility.
5+
This change also allows for a JSON object to be passed which allows for the required `restrict` flag as well as optional attributes: `enableAccessRequest` and `termsOfAccess`.
6+
If `enableAccessRequest` is false then the `termsOfAccess` text must also be included.
7+
8+
See [the guides](https://dataverse-guide--11349.org.readthedocs.build/en/11349/api/native-api.html#restrict-files), #11299, and #11349.

doc/sphinx-guides/source/api/native-api.rst

+29
Original file line numberDiff line numberDiff line change
@@ -4028,6 +4028,8 @@ Restrict Files
40284028
~~~~~~~~~~~~~~
40294029
40304030
Restrict or unrestrict an existing file where ``id`` is the database id of the file or ``pid`` is the persistent id (DOI or Handle) of the file to restrict. Note that some Dataverse installations do not allow the ability to restrict files (see :ref:`:PublicInstall`).
4031+
Restricting or Unrestricting a file, not in a draft version of the Dataset, will result in a new Draft version being created.
4032+
Optionally the API can receive a JSON string with additional parameters related to the ability to request access to the file and the terms of that access.
40314033
40324034
A curl example using an ``id``
40334035
@@ -4061,6 +4063,33 @@ The fully expanded example above (without environment variables) looks like this
40614063
40624064
curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT -d true "https://demo.dataverse.org/api/files/:persistentId/restrict?persistentId=doi:10.5072/FK2/AAA000"
40634065
4066+
Optional JSON string with additional attributes:
4067+
4068+
.. code-block:: bash
4069+
4070+
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
4071+
export SERVER_URL=https://demo.dataverse.org
4072+
export ID=24
4073+
4074+
curl -H "X-Dataverse-key:$API_TOKEN" -X PUT "$SERVER_URL/api/files/$ID/restrict" \
4075+
-H "Content-Type: application/json" \
4076+
-d '{"restrict": true, "enableAccessRequest":false, "termsOfAccess": "Reason for the restricted access"}'
4077+
4078+
Note the behavior of the optional parameters:
4079+
4080+
- If restrict is false then enableAccessRequest and termsOfAccess are ignored
4081+
- If restrict is true and enableAccessRequest is false then termsOfAccess is required. A status of CONFLICT (409) will be returned if the termsOfAccess is missing
4082+
4083+
The enableAccessRequest and termsOfAccess are applied to the Draft version of the Dataset and affect all of the restricted files in said Draft version.
4084+
4085+
The fully expanded example above (without environment variables) looks like this:
4086+
4087+
.. code-block:: bash
4088+
4089+
curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/files/:persistentId/restrict?persistentId=doi:10.5072/FK2/AAA000" \
4090+
-H "Content-Type: application/json" \
4091+
-d '{"restrict": true, "enableAccessRequest":false, "termsOfAccess": "Reason for the restricted access"}'
4092+
40644093
.. _file-uningest:
40654094
40664095
Uningest a File

src/main/java/edu/harvard/iq/dataverse/api/Files.java

+33-4
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,43 @@ public Response restrictFileInDataset(@Context ContainerRequestContext crc, @Pat
141141
return error(BAD_REQUEST, "Could not find datafile with id " + fileToRestrictId);
142142
}
143143

144-
boolean restrict = Boolean.valueOf(restrictStr);
144+
Boolean restrict = null;
145+
Boolean enableAccessRequest = null;
146+
String termsOfAccess = null;
147+
String returnMessage = " ";
148+
// Backward comparability - allow true/false in string(old) or json(new)
149+
if (restrictStr != null && restrictStr.trim().startsWith("{")) {
150+
// process as json
151+
jakarta.json.JsonObject jsonObject;
152+
try (StringReader stringReader = new StringReader(restrictStr)) {
153+
jsonObject = Json.createReader(stringReader).readObject();
154+
if (jsonObject.containsKey("restrict")) {
155+
restrict = Boolean.valueOf(jsonObject.getBoolean("restrict"));
156+
returnMessage += restrict ? "restricted." : "unrestricted.";
157+
} else {
158+
return badRequest("Error parsing Json: 'restrict' is required.");
159+
}
160+
if (jsonObject.containsKey("enableAccessRequest")) {
161+
enableAccessRequest = Boolean.valueOf(jsonObject.getBoolean("enableAccessRequest"));
162+
returnMessage += " Access Request is " + (enableAccessRequest ? "enabled." : "disabled.");
163+
}
164+
if (jsonObject.containsKey("termsOfAccess")) {
165+
termsOfAccess = jsonObject.getString("termsOfAccess");
166+
returnMessage += " Terms of Access for restricted files: " + termsOfAccess;
167+
}
168+
} catch (JsonParsingException jpe) {
169+
return badRequest("Error parsing Json: " + jpe.getMessage());
170+
}
171+
} else {
172+
restrict = Boolean.valueOf(restrictStr);
173+
returnMessage += restrict ? "restricted." : "unrestricted.";
174+
}
145175

146176
dataverseRequest = createDataverseRequest(getRequestUser(crc));
147177

148178
// try to restrict the datafile
149179
try {
150-
engineSvc.submit(new RestrictFileCommand(dataFile, dataverseRequest, restrict));
180+
engineSvc.submit(new RestrictFileCommand(dataFile, dataverseRequest, restrict, enableAccessRequest, termsOfAccess));
151181
} catch (CommandException ex) {
152182
return error(BAD_REQUEST, "Problem trying to update restriction status on " + dataFile.getDisplayName() + ": " + ex.getLocalizedMessage());
153183
}
@@ -165,8 +195,7 @@ public Response restrictFileInDataset(@Context ContainerRequestContext crc, @Pat
165195
return error(BAD_REQUEST, "Problem saving datafile " + dataFile.getDisplayName() + ": " + ex.getLocalizedMessage());
166196
}
167197

168-
String text = restrict ? "restricted." : "unrestricted.";
169-
return ok("File " + dataFile.getDisplayName() + " " + text);
198+
return ok("File " + dataFile.getDisplayName() + returnMessage);
170199
}
171200

172201

src/main/java/edu/harvard/iq/dataverse/engine/command/impl/RestrictFileCommand.java

+23-7
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
*/
66
package edu.harvard.iq.dataverse.engine.command.impl;
77

8-
import edu.harvard.iq.dataverse.DataFile;
9-
import edu.harvard.iq.dataverse.Dataset;
10-
import edu.harvard.iq.dataverse.DatasetVersion;
11-
import edu.harvard.iq.dataverse.FileMetadata;
8+
import edu.harvard.iq.dataverse.*;
129
import edu.harvard.iq.dataverse.authorization.Permission;
1310
import edu.harvard.iq.dataverse.engine.command.AbstractVoidCommand;
1411
import edu.harvard.iq.dataverse.engine.command.CommandContext;
@@ -34,13 +31,25 @@ public class RestrictFileCommand extends AbstractVoidCommand {
3431

3532
private final DataFile file;
3633
private final boolean restrict;
37-
34+
private final Boolean fileAccessRequest;
35+
private final String termsOfAccess;
36+
37+
public RestrictFileCommand(DataFile file, DataverseRequest aRequest, boolean restrict, Boolean fileAccessRequest, String termsOfAccess) {
38+
super(aRequest, file.getOwner());
39+
this.file = file;
40+
this.restrict = restrict;
41+
this.fileAccessRequest = fileAccessRequest;
42+
this.termsOfAccess = termsOfAccess;
43+
}
44+
3845
public RestrictFileCommand(DataFile file, DataverseRequest aRequest, boolean restrict) {
3946
super(aRequest, file.getOwner());
4047
this.file = file;
4148
this.restrict = restrict;
42-
}
43-
49+
this.fileAccessRequest = null;
50+
this.termsOfAccess = null;
51+
}
52+
4453
@Override
4554
protected void executeImpl(CommandContext ctxt) throws CommandException {
4655
// check if public install & don't allow
@@ -64,6 +73,13 @@ protected void executeImpl(CommandContext ctxt) throws CommandException {
6473
else {
6574
Dataset dataset = file.getOwner();
6675
DatasetVersion workingVersion = dataset.getOrCreateEditVersion();
76+
if (restrict && fileAccessRequest != null) {
77+
if (workingVersion.getTermsOfUseAndAccess() == null) {
78+
workingVersion.setTermsOfUseAndAccess(new TermsOfUseAndAccess());
79+
}
80+
workingVersion.getTermsOfUseAndAccess().setFileAccessRequest(fileAccessRequest);
81+
workingVersion.getTermsOfUseAndAccess().setTermsOfAccess(termsOfAccess);
82+
}
6783
// We need the FileMetadata for the file in the draft dataset version and the
6884
// file we have may still reference the fmd from the prior released version
6985
FileMetadata draftFmd = file.getFileMetadata();

src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java

+26-3
Original file line numberDiff line numberDiff line change
@@ -1027,14 +1027,37 @@ public void testRestrictFile() {
10271027
.body("message", equalTo("Problem trying to update restriction status on dataverseproject.png: File dataverseproject.png is already restricted"))
10281028
.statusCode(BAD_REQUEST.getStatusCode());
10291029

1030-
//unrestrict file
1031-
restrict = false;
1032-
Response unrestrictResponse = UtilIT.restrictFile(origFileId.toString(), restrict, apiToken);
1030+
//unrestrict file using json with missing "restrict"
1031+
String restrictJson = "{}";
1032+
Response unrestrictResponse = UtilIT.restrictFile(origFileId.toString(), restrictJson, apiToken);
1033+
unrestrictResponse.prettyPrint();
1034+
unrestrictResponse.then().assertThat()
1035+
.body("message", equalTo("Error parsing Json: 'restrict' is required."))
1036+
.statusCode(BAD_REQUEST.getStatusCode());
1037+
1038+
//unrestrict file using json
1039+
restrictJson = "{\"restrict\":false}";
1040+
unrestrictResponse = UtilIT.restrictFile(origFileId.toString(), restrictJson, apiToken);
10331041
unrestrictResponse.prettyPrint();
10341042
unrestrictResponse.then().assertThat()
10351043
.body("data.message", equalTo("File dataverseproject.png unrestricted."))
10361044
.statusCode(OK.getStatusCode());
10371045

1046+
//restrict file using json with enableAccessRequest false and missing TOA
1047+
restrictJson = "{\"restrict\":true, \"enableAccessRequest\":false}";
1048+
restrictResponse = UtilIT.restrictFile(origFileId.toString(), restrictJson, apiToken);
1049+
restrictResponse.prettyPrint();
1050+
restrictResponse.then().assertThat()
1051+
.body("message", equalTo(BundleUtil.getStringFromBundle("dataset.message.toua.invalid")))
1052+
.statusCode(CONFLICT.getStatusCode());
1053+
//restrict file using json
1054+
restrictJson = "{\"restrict\":true, \"enableAccessRequest\":false, \"termsOfAccess\":\"Testing terms of access\"}";
1055+
restrictResponse = UtilIT.restrictFile(origFileId.toString(), restrictJson, apiToken);
1056+
restrictResponse.prettyPrint();
1057+
restrictResponse.then().assertThat()
1058+
.body("data.message", equalTo("File dataverseproject.png restricted. Access Request is disabled. Terms of Access for restricted files: Testing terms of access"))
1059+
.statusCode(OK.getStatusCode());
1060+
10381061
//reset public install
10391062
UtilIT.setSetting(SettingsServiceBean.Key.PublicInstall, publicInstall);
10401063

src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java

+13
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,19 @@ static Response migrateBuiltinToOAuth(String data, String apiToken) {
18911891
return response;
18921892
}
18931893

1894+
static Response restrictFile(String fileIdOrPersistentId, String restrict, String apiToken) {
1895+
String idInPath = fileIdOrPersistentId; // Assume it's a number.
1896+
String optionalQueryParam = ""; // If idOrPersistentId is a number we'll just put it in the path.
1897+
if (!NumberUtils.isCreatable(fileIdOrPersistentId)) {
1898+
idInPath = ":persistentId";
1899+
optionalQueryParam = "?persistentId=" + fileIdOrPersistentId;
1900+
}
1901+
Response response = given()
1902+
.header(API_TOKEN_HTTP_HEADER, apiToken)
1903+
.body(restrict)
1904+
.put("/api/files/" + idInPath + "/restrict" + optionalQueryParam);
1905+
return response;
1906+
}
18941907
static Response restrictFile(String fileIdOrPersistentId, boolean restrict, String apiToken) {
18951908
String idInPath = fileIdOrPersistentId; // Assume it's a number.
18961909
String optionalQueryParam = ""; // If idOrPersistentId is a number we'll just put it in the path.

0 commit comments

Comments
 (0)