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

41 add auth to harvest api #86

Open
wants to merge 20 commits into
base: 8117_sphinx_warnings_as_errors
Choose a base branch
from
Open
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
109 changes: 109 additions & 0 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2784,6 +2784,115 @@ Each user can get a dump of their basic information in JSON format by passing in

.. _pids-api:

Managing Harvesting Server and Sets
-----------------------------------

This API can be used to manage the Harvesting sets that your installation makes available over OAI-PMH. For more information, see the :doc:`/admin/harvestserver` section of the Admin Guide.

List All Harvesting Sets
~~~~~~~~~~~~~~~~~~~~~~~~

Shows all Harvesting Sets defined in the installation::

GET http://$SERVER/api/harvest/server/oaisets/

List A Specific Harvesting Set
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Shows a Harvesting Set with a defined specname::

GET http://$SERVER/api/harvest/server/oaisets/$specname

Create a Harvesting Set
~~~~~~~~~~~~~~~~~~~~~~~

To create a harvesting set you must supply a JSON file that contains the following fields:

- Name: Alpha-numeric may also contain -, _, or %, but no spaces. Must also be unique in the installation.
- Definition: A search query to select the datasets to be harvested. For example, a query containing authorName:YYY would include all datasets where ‘YYY’ is the authorName.
- Description: Text that describes the harvesting set. The description appears in the Manage Harvesting Sets dashboard and in API responses. This field is optional.

An example JSON file would look like this:

{
"name”:"ffAuthor",
"definition":"authorName:Finch, Fiona",
"description":"Fiona Finch’s Datasets"
}

.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of export below.

.. code-block:: bashbloopbloopbloopbloopbloopbloopbloop

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org

curl -H X-Dataverse-key:$API_TOKEN -X POST "$SERVER_URL/api/harvest/server/oaisets/" --upload-file harvestset-finch.json

The fully expanded example above (without the environment variables) looks like this:

.. code-block:: bash

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X POST "https://demo.dataverse.org/api/harvest/server/oaisets/" --upload-file "harvestset-finch.json"

Only users with superuser permissions may create harvesting sets.

Modify an Existing Harvesting Set
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To modify a harvesting set, you must supply a JSON file that contains one or both of the following fields:

- Definition: A search query to select the datasets to be harvested. For example, a query containing authorName:YYY would include all datasets where ‘YYY’ is the authorName.
- Description: Text that describes the harvesting set. The description appears in the Manage Harvesting Sets dashboard and in API responses. This field is optional.

Note that you may not modify the name of an existing harvesting set.

An example JSON file would look like this:

{
"definition":"authorName:Finch, Fiona AND subject:trees",
"description":"Fiona Finch’s Datasets with subject of trees"
}

.. note:: See :ref:`curl-examples-and-environment-variables` if you are unfamiliar with the use of export below.

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export SPECNAME=ffAuthor

curl -H X-Dataverse-key:$API_TOKEN -X PUT "$SERVER_URL/api/harvest/server/oaisets/$SPECNAME” --upload-file modify-harvestset-finch.json

The fully expanded example above (without the environment variables) looks like this:

.. code-block:: bash

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X PUT "https://demo.dataverse.org/api/harvest/server/oaisets/ffAuthor” --upload-file "modify-harvestset-finch.json"

Only users with superuser permissions may modify harvesting sets.

Delete an Existing Harvesting Set
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To delete a harvesting set, use the set's database name. For example, to delete an existing harvesting set whose database name is “ffAuthor”:

.. code-block:: bash

export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
export SPECNAME=ffAuthor

curl -H X-Dataverse-key:$API_TOKEN -X DELETE "$SERVER_URL/api/harvest/server/oaisets/$SPECNAME”

The fully expanded example above (without the environment variables) looks like this:

.. code-block:: bash

curl -H "X-Dataverse-key:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -X DELETE "https://demo.dataverse.org/api/harvest/server/oaisets/ffAuthor”

Only users with superuser permissions may delete harvesting sets.

PIDs
----

Expand Down
112 changes: 77 additions & 35 deletions src/main/java/edu/harvard/iq/dataverse/api/HarvestingServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,20 @@
*/
package edu.harvard.iq.dataverse.api;

import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.DataverseServiceBean;
import edu.harvard.iq.dataverse.harvest.client.HarvestingClient;

import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.impl.CreateHarvestingClientCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetHarvestingClientCommand;
import edu.harvard.iq.dataverse.engine.command.impl.UpdateHarvestingClientCommand;
import edu.harvard.iq.dataverse.harvest.client.ClientHarvestRun;
import edu.harvard.iq.dataverse.harvest.client.HarvesterServiceBean;
import edu.harvard.iq.dataverse.harvest.client.HarvestingClientServiceBean;
import edu.harvard.iq.dataverse.harvest.server.OAISet;
import edu.harvard.iq.dataverse.harvest.server.OAISetServiceBean;
import edu.harvard.iq.dataverse.util.BundleUtil;
import edu.harvard.iq.dataverse.util.json.JsonParseException;
import edu.harvard.iq.dataverse.authorization.users.User;
import static edu.harvard.iq.dataverse.util.JsfHelper.JH;
import javax.json.JsonObjectBuilder;
import static edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder.jsonObjectBuilder;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.faces.application.FacesMessage;
import javax.json.Json;
import javax.json.JsonReader;
import javax.json.JsonArrayBuilder;
Expand Down Expand Up @@ -119,8 +104,7 @@ public Response oaiSet(@PathParam("specname") String spec, @QueryParam("key") St
* "description":$optional_set_description,"definition":$set_search_query_string}.
*/
@POST
@Path("{specname}")
public Response createOaiSet(String jsonBody, @PathParam("specname") String spec, @QueryParam("key") String apiKey) throws IOException, JsonParseException {
public Response createOaiSet(String jsonBody, @QueryParam("key") String apiKey) throws IOException, JsonParseException {
/*
* authorization modeled after the UI (aka HarvestingSetsPage)
*/
Expand All @@ -141,30 +125,33 @@ public Response createOaiSet(String jsonBody, @PathParam("specname") String spec
JsonObject json = jrdr.readObject();

OAISet set = new OAISet();
//Validating spec
if (!StringUtils.isEmpty(spec)) {
if (spec.length() > 30) {


String name, desc, defn;

try {
name = json.getString("name");
} catch (NullPointerException npe_name) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.required"));
}
//Validating spec
if (!StringUtils.isEmpty(name)) {
if (name.length() > 30) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.sizelimit"));
}
if (!Pattern.matches("^[a-zA-Z0-9\\_\\-]+$", spec)) {
if (!Pattern.matches("^[a-zA-Z0-9\\_\\-]+$", name)) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.invalid"));
// If it passes the regex test, check
}
if (oaiSetService.findBySpec(spec) != null) {
if (oaiSetService.findBySpec(name) != null) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.alreadyused"));
}

} else {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.required"));
}
set.setSpec(spec);
String name, desc, defn;

try {
name = json.getString("name");
} catch (NullPointerException npe_name) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.required"));
}

set.setSpec(name);
try {
defn = json.getString("definition");
} catch (NullPointerException npe_defn) {
Expand All @@ -179,22 +166,77 @@ public Response createOaiSet(String jsonBody, @PathParam("specname") String spec
set.setDescription(desc);
set.setDefinition(defn);
oaiSetService.save(set);
return created("/harvest/server/oaisets" + spec, oaiSetAsJson(set));
return created("/harvest/server/oaisets" + name, oaiSetAsJson(set));
}

}

@PUT
@Path("{nickName}")
@Path("{specname}")
public Response modifyOaiSet(String jsonBody, @PathParam("specname") String spec, @QueryParam("key") String apiKey) throws IOException, JsonParseException {
// TODO:
// ...
return created("/harvest/server/oaisets" + spec, null);

AuthenticatedUser dvUser;
try {
dvUser = findAuthenticatedUserOrDie();
} catch (WrappedResponse wr) {
return wr.getResponse();
}
if (!dvUser.isSuperuser()) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.superUser.required"));
}

StringReader rdr = new StringReader(jsonBody);

try (JsonReader jrdr = Json.createReader(rdr)) {
JsonObject json = jrdr.readObject();
OAISet update;
//Validating spec
if (!StringUtils.isEmpty(spec)) {
update = oaiSetService.findBySpec(spec);
if (update == null) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.editSetDialog.setspec.notFound"));
}

} else {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.required"));
}

String desc, defn;

try {
defn = json.getString("definition");
} catch (NullPointerException npe_defn) {
defn = ""; // if they're updating description but not definition;
}
try {
desc = json.getString("description");
} catch (NullPointerException npe_desc) {
desc = ""; //treating description as optional
}
if (defn.isEmpty() && desc.isEmpty()) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.newSetDialog.setspec.required"));
}
update.setDescription(desc);
update.setDefinition(defn);
oaiSetService.save(update);
return ok("/harvest/server/oaisets" + spec, oaiSetAsJson(update));
}
}

@DELETE
@Path("{specname}")
public Response deleteOaiSet(@PathParam("specname") String spec, @QueryParam("key") String apiKey) {

AuthenticatedUser dvUser;
try {
dvUser = findAuthenticatedUserOrDie();
} catch (WrappedResponse wr) {
return wr.getResponse();
}
if (!dvUser.isSuperuser()) {
return badRequest(BundleUtil.getStringFromBundle("harvestserver.deleteSetDialog.setspec.superUser.required"));
}

OAISet set = null;
try {
set = oaiSetService.findBySpec(spec);
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/propertyFiles/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,8 @@ harvestserver.newSetDialog.setspec.tip=A unique name (OAI setSpec) identifying t
harvestserver.newSetDialog.setspec.helptext=Consists of letters, digits, underscores (_) and dashes (-).
harvestserver.editSetDialog.setspec.helptext=The name can not be changed once the set has been created.
harvestserver.editSetDialog.setspec.helptext.default=this is the default, unnamed set
harvestserver.editSetDialog.setspec.notFound=Set for editing not found!
harvestserver.editSetDialog.setspec.noChanges=Invalid update! You must provide a definition and/or description to be updated.
harvestserver.newSetDialog.setspec.required=Name (OAI setSpec) cannot be empty!
harvestserver.newSetDialog.setspec.invalid=Name (OAI setSpec) can contain only letters, digits, underscores (_) and dashes (-).
harvestserver.newSetDialog.setspec.alreadyused=This set name (OAI setSpec) is already used.
Expand All @@ -594,6 +596,7 @@ harvestserver.newSetDialog.btn.create=Create Set
harvestserver.newSetDialog.success=Successfully created harvesting set "{0}".
harvestserver.viewEditDialog.title=Edit Harvesting Set
harvestserver.viewEditDialog.btn.save=Save Changes
harvestserver.deleteSetDialog.setspec.superUser.required=Only superusers may delete OAI sets.

#dashboard-users.xhtml
dashboard.card.users=Users
Expand Down
Loading