Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
6 changes: 6 additions & 0 deletions doc/release-notes/12141-storage-driver-endpoints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Breaking Changes

All the endpoints related to Storage Drivers have been moved out of the Admin API.

- The endpoints GET, PUT AND DELETE for `/api/admin/dataverse/{alias}/storageDriver` has been moved to `/api/dataverses/{alias}/storageDriver`.
- The endpoint `/api/admin/dataverse/storageDrivers` has been moved and renamed to `/api/dataverses/{alias}/allowedStorageDrivers`. Regarding the change of the name, this endpoint will in the future only display the storageDrivers that are allowed on the specified collection, as of now, it will display the entire list of available Drivers on the installation.
12 changes: 6 additions & 6 deletions doc/sphinx-guides/source/admin/dataverses-datasets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,29 @@ Configure a Dataverse Collection to Store All New Files in a Specific File Store

To direct new files (uploaded when datasets are created or edited) for all datasets in a given Dataverse collection, the store can be specified via the API as shown below, or by editing the 'General Information' for a Dataverse collection on the Dataverse collection page. Only accessible to superusers. ::
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph still says “Only accessible to superusers” but the new implementation in Dataverses appears to allow any authenticated user with the relevant object permission (e.g., EditDataverse) to set/reset the storage driver. Either enforce superuser-only access in the API or update this doc text to match the new authorization behavior.

Suggested change
To direct new files (uploaded when datasets are created or edited) for all datasets in a given Dataverse collection, the store can be specified via the API as shown below, or by editing the 'General Information' for a Dataverse collection on the Dataverse collection page. Only accessible to superusers. ::
To direct new files (uploaded when datasets are created or edited) for all datasets in a given Dataverse collection, the store can be specified via the API as shown below, or by editing the 'General Information' for a Dataverse collection on the Dataverse collection page. Requires permission to edit the Dataverse collection (for example, the ``EditDataverse`` permission). ::

Copilot uses AI. Check for mistakes.

curl -H "X-Dataverse-key: $API_TOKEN" -X PUT -d $storageDriverLabel http://$SERVER/api/admin/dataverse/$dataverse-alias/storageDriver
curl -H "X-Dataverse-key: $API_TOKEN" -X PUT -d $storageDriverLabel http://$SERVER/api/dataverses/$dataverse-alias/storageDriver

(Note that for ``dataverse.files.store1.label=MyLabel``, you should pass ``MyLabel``.)

A store assigned directly to a collection can be seen using::

curl -H "X-Dataverse-key: $API_TOKEN" http://$SERVER/api/admin/dataverse/$dataverse-alias/storageDriver
curl -H "X-Dataverse-key: $API_TOKEN" http://$SERVER/api/dataverses/$dataverse-alias/storageDriver

This may be null. To get the effective storageDriver for a collection, which may be inherited from a parent collection or be the installation default, you can use::

curl -H "X-Dataverse-key: $API_TOKEN" http://$SERVER/api/admin/dataverse/$dataverse-alias/storageDriver?getEffective=true
curl -H "X-Dataverse-key: $API_TOKEN" http://$SERVER/api/dataverses/$dataverse-alias/storageDriver?getEffective=true

This will never be null.

(Note that for ``dataverse.files.store1.label=MyLabel``, the JSON response will include "name":"store1" and "label":"MyLabel".)

To delete a store assigned directly to a collection (so that the colllection's effective store is inherted from it's parent or is the global default), use::

curl -H "X-Dataverse-key: $API_TOKEN" -X DELETE http://$SERVER/api/admin/dataverse/$dataverse-alias/storageDriver
curl -H "X-Dataverse-key: $API_TOKEN" -X DELETE http://$SERVER/api/dataverses/$dataverse-alias/storageDriver

The available drivers can be listed with::
The available drivers within a collection can be listed with::

curl -H "X-Dataverse-key: $API_TOKEN" http://$SERVER/api/admin/dataverse/storageDrivers
curl -H "X-Dataverse-key: $API_TOKEN" http://$SERVER/api/dataverses/$dataverse-alias/allowedStorageDrivers

(Individual datasets can be configured to use specific file stores as well. See the "Datasets" section below.)

Expand Down
7 changes: 6 additions & 1 deletion doc/sphinx-guides/source/api/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ This API changelog is experimental and we would love feedback on its usefulness.
:local:
:depth: 1

v6.11
-----

- The endpoints GET, PUT AND DELETE for `/api/admin/dataverse/{alias}/storageDriver` has been moved to `/api/dataverses/{alias}/storageDriver`.
- The endpoint `/api/admin/dataverse/storageDrivers` has been moved and renamed to `/api/dataverses/{alias}/allowedStorageDrivers`. Regarding the change of the name, this endpoint will in the future only display the storageDrivers that are allowed on the specified collection, as of now, it will display the entire list of available Drivers on the installation.

v6.10
-----
- The following GET APIs will now return ``400`` if a required Guestbook Response is not supplied. A Guestbook Response can be passed to these APIs in the JSON body using a POST call. See the notes under :ref:`basic-file-access` and :ref:`download-by-dataset-by-version` for details.
Expand All @@ -29,7 +35,6 @@ v6.10

- **/api/access/datafile/{id}/requestAccess**


v6.9
----

Expand Down
90 changes: 1 addition & 89 deletions src/main/java/edu/harvard/iq/dataverse/api/Admin.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -2257,94 +2256,6 @@ public Response addRoleAssignementsToChildren(@Context ContainerRequestContext c
"InheritParentRoleAssignments does not list any roles on this instance");
}

@GET
@AuthRequired
@Path("/dataverse/{alias}/storageDriver")
public Response getStorageDriver(@Context ContainerRequestContext crc, @PathParam("alias") String alias,
@QueryParam("getEffective") Boolean getEffective) throws WrappedResponse {
Dataverse dataverse = dataverseSvc.findByAlias(alias);
if (dataverse == null) {
return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
}
try {
AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
if (!user.isSuperuser()) {
return error(Response.Status.FORBIDDEN, "Superusers only.");
}
} catch (WrappedResponse wr) {
return wr.getResponse();
}

if (getEffective != null && getEffective) {
return ok(JsonPrinter.jsonStorageDriver(dataverse.getEffectiveStorageDriverId()));
} else {
return ok(JsonPrinter.jsonStorageDriver(dataverse.getStorageDriverId()));
}
}

@PUT
@AuthRequired
@Path("/dataverse/{alias}/storageDriver")
public Response setStorageDriver(@Context ContainerRequestContext crc, @PathParam("alias") String alias, String label) throws WrappedResponse {
Dataverse dataverse = dataverseSvc.findByAlias(alias);
if (dataverse == null) {
return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
}
try {
AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
if (!user.isSuperuser()) {
return error(Response.Status.FORBIDDEN, "Superusers only.");
}
} catch (WrappedResponse wr) {
return wr.getResponse();
}
for (Entry<String, String> store: DataAccess.getStorageDriverLabels().entrySet()) {
if(store.getKey().equals(label)) {
dataverse.setStorageDriverId(store.getValue());
return ok("Storage set to: " + store.getKey() + "/" + store.getValue());
}
}
return error(Response.Status.BAD_REQUEST,
"No Storage Driver found for : " + label);
}

@DELETE
@AuthRequired
@Path("/dataverse/{alias}/storageDriver")
public Response resetStorageDriver(@Context ContainerRequestContext crc, @PathParam("alias") String alias) throws WrappedResponse {
Dataverse dataverse = dataverseSvc.findByAlias(alias);
if (dataverse == null) {
return error(Response.Status.NOT_FOUND, "Could not find dataverse based on alias supplied: " + alias + ".");
}
try {
AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
if (!user.isSuperuser()) {
return error(Response.Status.FORBIDDEN, "Superusers only.");
}
} catch (WrappedResponse wr) {
return wr.getResponse();
}
dataverse.setStorageDriverId("");
return ok("Storage reset to default: " + DataAccess.DEFAULT_STORAGE_DRIVER_IDENTIFIER);
}

@GET
@AuthRequired
@Path("/dataverse/storageDrivers")
public Response listStorageDrivers(@Context ContainerRequestContext crc) throws WrappedResponse {
try {
AuthenticatedUser user = getRequestAuthenticatedUserOrDie(crc);
if (!user.isSuperuser()) {
return error(Response.Status.FORBIDDEN, "Superusers only.");
}
} catch (WrappedResponse wr) {
return wr.getResponse();
}
JsonObjectBuilder bld = jsonObjectBuilder();
DataAccess.getStorageDriverLabels().entrySet().forEach(s -> bld.add(s.getKey(), s.getValue()));
return ok(bld);
}

@GET
@AuthRequired
@Path("/dataverse/{alias}/curationLabelSet")
Expand Down Expand Up @@ -2821,4 +2732,5 @@ public Response rateLimitStats(@Context ContainerRequestContext crc,
String csvData = cacheFactory.getStats(CacheFactoryBean.RATE_LIMIT_CACHE, deltaMinutesFilter != null ? String.valueOf(deltaMinutesFilter) : null);
return Response.ok(csvData).header("Content-Disposition", "attachment; filename=\"data.csv\"").build();
}

}
Loading
Loading