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

5716 edit file api perms #6847

Merged
merged 15 commits into from Apr 23, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Expand Up @@ -481,6 +481,8 @@ The fully expanded example above (without the environment variables) looks like

You should expect an HTTP 200 ("OK") response and JSON indicating the database ID and Persistent ID (PID such as DOI or Handle) that has been assigned to your newly created dataset.

.. note:: Only a Dataverse account with superuser permissions is allowed to include files when creating a dataset via this API. Adding files this way only adds their file metadata to the database, you will need to manually add the physical files to the file system.

Import a Dataset into a Dataverse
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Datasets.java
Expand Up @@ -572,6 +572,11 @@ public Response updateDraftVersion( String jsonBody, @PathParam("id") String id,
incomingVersion.setDataset(ds);
incomingVersion.setCreateTime(null);
incomingVersion.setLastUpdateTime(null);

if (!incomingVersion.getFileMetadatas().isEmpty()){
return error( Response.Status.BAD_REQUEST, "You may not add files via this api.");
}

boolean updateDraft = ds.getLatestVersion().isDraft();

DatasetVersion managedVersion;
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Expand Up @@ -224,6 +224,10 @@ public Response createDataset(String jsonBody, @PathParam("identifier") String p
if (ds.getVersions().isEmpty()) {
return badRequest("Please provide initial version in the dataset json");
}

if (!ds.getFiles().isEmpty() && !u.isSuperuser()){
return badRequest("Only a superuser may add files via this api");
}

// clean possible version metadata
DatasetVersion version = ds.getVersions().get(0);
Expand Down Expand Up @@ -253,6 +257,9 @@ public Response createDataset(String jsonBody, @PathParam("identifier") String p
public Response importDataset(String jsonBody, @PathParam("identifier") String parentIdtf, @QueryParam("pid") String pidParam, @QueryParam("release") String releaseParam) {
try {
User u = findUserOrDie();
if (!u.isSuperuser()) {
return error(Status.FORBIDDEN, "Not a superuser");
}
Dataverse owner = findDataverseOrDie(parentIdtf);
Dataset ds = parseDataset(jsonBody);
ds.setOwner(owner);
Expand Down Expand Up @@ -382,7 +389,7 @@ public Response importDatasetDdi(String xml, @PathParam("identifier") String par
return ex.getResponse();
}
}

private Dataset parseDataset(String datasetJson) throws WrappedResponse {
try (StringReader rdr = new StringReader(datasetJson)) {
return jsonParser().parseDataset(Json.createReader(rdr).readObject());
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/edu/harvard/iq/dataverse/util/json/JsonParser.java
Expand Up @@ -395,7 +395,6 @@ private List<DatasetField> parseFieldsFromArray(JsonArray fieldsArray, Boolean t

public List<FileMetadata> parseFiles(JsonArray metadatasJson, DatasetVersion dsv) throws JsonParseException {
List<FileMetadata> fileMetadatas = new LinkedList<>();

if (metadatasJson != null) {
for (JsonObject filemetadataJson : metadatasJson.getValuesAs(JsonObject.class)) {
String label = filemetadataJson.getString("label");
Expand All @@ -413,13 +412,14 @@ public List<FileMetadata> parseFiles(JsonArray metadatasJson, DatasetVersion dsv
dataFile.getFileMetadatas().add(fileMetadata);
dataFile.setOwner(dsv.getDataset());
fileMetadata.setDataFile(dataFile);
if (dsv.getDataset().getFiles() == null) {
dsv.getDataset().setFiles(new ArrayList<>());
if (dsv.getDataset() != null) {
if (dsv.getDataset().getFiles() == null) {
dsv.getDataset().setFiles(new ArrayList<>());
}
dsv.getDataset().getFiles().add(dataFile);
}
dsv.getDataset().getFiles().add(dataFile);
}


fileMetadatas.add(fileMetadata);
fileMetadata.setCategories(getCategories(filemetadataJson, dsv.getDataset()));
}
Expand Down
69 changes: 69 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/DatasetsIT.java
Expand Up @@ -41,10 +41,17 @@
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObjectBuilder;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static javax.ws.rs.core.Response.Status.OK;
import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import org.junit.AfterClass;
import org.junit.Assert;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.containsString;
Expand Down Expand Up @@ -1869,4 +1876,66 @@ public void testUpdatePIDMetadataAPI() {

}

@Test
public void testUpdateDatasetVersionWithFiles() throws InterruptedException {
Response createCurator = UtilIT.createRandomUser();
createCurator.prettyPrint();
createCurator.then().assertThat()
.statusCode(OK.getStatusCode());
String curatorUsername = UtilIT.getUsernameFromResponse(createCurator);
String curatorApiToken = UtilIT.getApiTokenFromResponse(createCurator);

Response createDataverseResponse = UtilIT.createRandomDataverse(curatorApiToken);
createDataverseResponse.prettyPrint();
createDataverseResponse.then().assertThat()
.statusCode(CREATED.getStatusCode());

String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse);

Response createAuthor = UtilIT.createRandomUser();
createAuthor.prettyPrint();
createAuthor.then().assertThat()
.statusCode(OK.getStatusCode());
String authorUsername = UtilIT.getUsernameFromResponse(createAuthor);
String authorApiToken = UtilIT.getApiTokenFromResponse(createAuthor);


Response grantAuthorAddDataset = UtilIT.grantRoleOnDataverse(dataverseAlias, DataverseRole.DS_CONTRIBUTOR.toString(), "@" + authorUsername, curatorApiToken);
grantAuthorAddDataset.prettyPrint();
grantAuthorAddDataset.then().assertThat()
.body("data.assignee", equalTo("@" + authorUsername))
.body("data._roleAlias", equalTo("dsContributor"))
.statusCode(OK.getStatusCode());

Response createDataset = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, authorApiToken);
createDataset.prettyPrint();
createDataset.then().assertThat()
.statusCode(CREATED.getStatusCode());

Integer datasetId = UtilIT.getDatasetIdFromResponse(createDataset);

// FIXME: have the initial create return the DOI or Handle to obviate the need for this call.
Response getDatasetJsonBeforePublishing = UtilIT.nativeGet(datasetId, authorApiToken);
getDatasetJsonBeforePublishing.prettyPrint();
String protocol = JsonPath.from(getDatasetJsonBeforePublishing.getBody().asString()).getString("data.protocol");
String authority = JsonPath.from(getDatasetJsonBeforePublishing.getBody().asString()).getString("data.authority");
String identifier = JsonPath.from(getDatasetJsonBeforePublishing.getBody().asString()).getString("data.identifier");

String datasetPersistentId = protocol + ":" + authority + "/" + identifier;
System.out.println("datasetPersistentId: " + datasetPersistentId);

String pathToJsonFile = "src/test/resources/json/update-dataset-version-with-files.json";

Response updateMetadataAddFilesViaNative = UtilIT.updateDatasetMetadataViaNative(datasetPersistentId, pathToJsonFile, authorApiToken);
updateMetadataAddFilesViaNative.prettyPrint();
updateMetadataAddFilesViaNative.then().assertThat()
.statusCode(BAD_REQUEST.getStatusCode());

// These println's are here in case you want to log into the GUI to see what notifications look like.
System.out.println("Curator username/password: " + curatorUsername);
System.out.println("Author username/password: " + authorUsername);

}


}
44 changes: 44 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java
Expand Up @@ -2,6 +2,7 @@

import com.jayway.restassured.RestAssured;
import static com.jayway.restassured.RestAssured.given;
import com.jayway.restassured.path.json.JsonPath;
import static com.jayway.restassured.path.json.JsonPath.with;
import com.jayway.restassured.response.Response;
import edu.harvard.iq.dataverse.Dataverse;
Expand All @@ -18,8 +19,11 @@
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import javax.ws.rs.core.Response.Status;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.OK;
import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.fail;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
Expand Down Expand Up @@ -453,4 +457,44 @@ public void testUpdateDefaultContributorRole() {

}

@Test
public void testDataFileAPIPermissions() {

Response createUser = UtilIT.createRandomUser();
createUser.prettyPrint();
String username = UtilIT.getUsernameFromResponse(createUser);
String apiToken = UtilIT.getApiTokenFromResponse(createUser);

Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken);
createDataverseResponse.prettyPrint();
String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse);

String pathToJsonFile = "src/test/resources/json/complete-dataset-with-files.json";
Response createDatasetResponse = UtilIT.createDatasetViaNativeApi(dataverseAlias, pathToJsonFile, apiToken);

//should fail if non-super user and attempting to
//create a dataset with files
createDatasetResponse.prettyPrint();
createDatasetResponse.then().assertThat()
.statusCode(BAD_REQUEST.getStatusCode());

//should be ok to create a dataset without files...
pathToJsonFile = "scripts/api/data/dataset-create-new.json";
createDatasetResponse = UtilIT.createDatasetViaNativeApi(dataverseAlias, pathToJsonFile, apiToken);

createDatasetResponse.prettyPrint();
createDatasetResponse.then().assertThat()
.statusCode(CREATED.getStatusCode());
Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse);

//As non-super user should be able to add a real file
String pathToFile1 = "src/main/webapp/resources/images/cc0.png";
Response authorAttemptsToAddFileViaNative = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile1, apiToken);

authorAttemptsToAddFileViaNative.prettyPrint();
authorAttemptsToAddFileViaNative.then().assertThat()
.statusCode(OK.getStatusCode());

}

}
Expand Up @@ -134,6 +134,8 @@ public void testJsonParserWithDirectoryLabels() {

// create dataset and set id
System.out.println("Creating dataset....");
//SEK 4/14/2020 need to be super user to add a dataset with files
UtilIT.makeSuperUser(testName);
dsId = given()
.header(keyString, token)
.body(IOUtils.toString(classLoader.getResourceAsStream("json/complete-dataset-with-files.json")))
Expand Down
6 changes: 5 additions & 1 deletion src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
Expand Up @@ -356,7 +356,8 @@ private static String getDatasetJson() {

static Response createDatasetViaNativeApi(String dataverseAlias, String pathToJsonFile, String apiToken) {
String jsonIn = getDatasetJson(pathToJsonFile);
Response createDatasetResponse = given()

Response createDatasetResponse = given()
.header(API_TOKEN_HTTP_HEADER, apiToken)
.body(jsonIn)
.contentType("application/json")
Expand All @@ -372,6 +373,9 @@ private static String getDatasetJson(String pathToJsonFile) {
} catch (IOException ex) {
Logger.getLogger(UtilIT.class.getName()).log(Level.SEVERE, null, ex);
return null;
} catch (Exception e){
Logger.getLogger(UtilIT.class.getName()).log(Level.SEVERE, null, e);
return null;
}
}

Expand Down
129 changes: 129 additions & 0 deletions src/test/resources/json/update-dataset-version-with-files.json
@@ -0,0 +1,129 @@
{
"metadataBlocks": {
"citation": {
"displayName": "Citation Metadata",
"fields": [
{
"typeName": "title",
"multiple": false,
"typeClass": "primitive",
"value": "Update Version with Files"
},
{
"typeName": "author",
"multiple": true,
"typeClass": "compound",
"value": [
{
"authorName": {
"typeName": "authorName",
"multiple": false,
"typeClass": "primitive",
"value": "Spruce, Sabrina"
}
}
]
},
{
"typeName": "datasetContact",
"multiple": true,
"typeClass": "compound",
"value": [
{
"datasetContactName": {
"typeName": "datasetContactName",
"multiple": false,
"typeClass": "primitive",
"value": "Spruce, Sabrina"
},
"datasetContactEmail": {
"typeName": "datasetContactEmail",
"multiple": false,
"typeClass": "primitive",
"value": "spruce@mailinator.com"
}
}
]
},
{
"typeName": "dsDescription",
"multiple": true,
"typeClass": "compound",
"value": [
{
"dsDescriptionValue": {
"typeName": "dsDescriptionValue",
"multiple": false,
"typeClass": "primitive",
"value": "test"
}
}
]
},
{
"typeName": "subject",
"multiple": true,
"typeClass": "controlledVocabulary",
"value": [
"Other"
]
},
{
"typeName": "depositor",
"multiple": false,
"typeClass": "primitive",
"value": "Spruce, Sabrina"
},
{
"typeName": "dateOfDeposit",
"multiple": false,
"typeClass": "primitive",
"value": "2017-04-19"
}
]
}
},
"files": [
{
"description": "",
"label": "yn152_4_001.img",
"directoryLabel": "data/subdir1",
"version": 1,
"datasetVersionId": 2,
"dataFile": {
"id": 424,
"filename": "yn152_4_001.img",
"contentType": "application/octet-stream",
"storageIdentifier": "data/subdir/yn152_4_001.img",
"originalFormatLabel": "UNKNOWN",
"checksum": {
"type": "MD5",
"value": "fc4ac06da6fd84785ed1b79cab9b714a73621ac7"
},
"description": ""
}
},
{
"description": "",
"label": "yn152_4_002.img",
"directoryLabel": "data/subdir2",
"version": 1,
"datasetVersionId": 2,
"categories": ["Data"],
"dataFile": {
"id": 425,
"filename": "yn152_4_002.img",
"contentType": "application/octet-stream",
"storageIdentifier": "data/subdir/yn152_4_002.img",
"originalFormatLabel": "UNKNOWN",
"checksum": {
"type": "MD5",
"value": "756c1963e34eb6dd8bb5b48333c4f20ce64a0307"
},
"description": ""
}
}
]
}