Skip to content

Commit

Permalink
Merge pull request #31 from poikilotherm/7715-jvmsetting
Browse files Browse the repository at this point in the history
feat: make API signing secret a JvmSetting IQSS#7715
  • Loading branch information
qqmyers authored Nov 17, 2022
2 parents dbca546 + 79f4c85 commit 70e350f
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 12 deletions.
2 changes: 2 additions & 0 deletions doc/sphinx-guides/source/api/external-tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ Reserved Words
``{localeCode}`` optional The code for the language ("en" for English, "fr" for French, etc.) that user has selected from the language toggle in a Dataverse installation. See also :ref:`i18n`.
=========================== ========== ===========

.. _api-exttools-auth:

Authorization Options
+++++++++++++++++++++

Expand Down
5 changes: 4 additions & 1 deletion doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4106,7 +4106,8 @@ The fully expanded example above (without environment variables) looks like this
.. code-block:: bash
curl -X DELETE https://demo.dataverse.org/api/admin/template/24
.. _api-native-signed-url:

Request Signed URL
~~~~~~~~~~~~~~~~~~
Expand All @@ -4133,3 +4134,5 @@ A curl example using allowing access to a dataset's metadata
curl -H 'X-Dataverse-key:$API_KEY' -d $JSON $SERVER_URL/api/admin/requestSignedUrl
Please see :ref:`dataverse.api.signature-secret` for the configuration option to add a shared secret, enabling extra
security.
37 changes: 35 additions & 2 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -580,8 +580,7 @@ Optionally, you may provide static credentials for each S3 storage using MicroPr
- ``dataverse.files.<id>.access-key`` for this storage's "access key ID"
- ``dataverse.files.<id>.secret-key`` for this storage's "secret access key"

You may provide the values for these via any of the
`supported config sources <https://docs.payara.fish/community/docs/documentation/microprofile/config/README.html>`_.
You may provide the values for these via any `supported MicroProfile Config API source`_.

**WARNING:**

Expand Down Expand Up @@ -1670,6 +1669,36 @@ This setting is useful in cases such as running your Dataverse installation behi
"HTTP_VIA",
"REMOTE_ADDR"
.. _dataverse.api.signature-secret:

dataverse.api.signature-secret
++++++++++++++++++++++++++++++

Context: Dataverse has the ability to create "Signed URLs" for it's API calls. Using a signed URL makes it obsolete to
provide API tokens to tools, which carries the risk of leaking extremely sensitive information on exposure. Signed URLs
can be limited to certain allowed actions, which is much more secure. See :ref:`api-exttools-auth` and
:ref:`api-native-signed-url` for more details. The key to sign a URL is created from the secret API token of the
creating user plus a shared secret provided by an administrator.

This setting will default to an empty string, but you should provide it for extra security.

Here is an example how to set your shared secret with the secure method "password alias":

.. code-block:: shell
echo "AS_ADMIN_ALIASPASSWORD=change-me-super-secret" > /tmp/password.txt
asadmin create-password-alias --passwordfile /tmp/password.txt dataverse.api.signature-secret
rm /tmp/password.txt
Can also be set via any `supported MicroProfile Config API source`_, e.g. the environment variable
``DATAVERSE_API_SIGNATURE_SECRET``.

**WARNING:** For security, do not use the sources "environment variable" or "system property" (JVM option) in a
production context! Rely on password alias, secrets directory or cloud based sources instead!



.. _:ApplicationServerSettings:

Application Server Settings
Expand Down Expand Up @@ -3067,3 +3096,7 @@ The interval in seconds between Dataverse calls to Globus to check on upload pro
+++++++++++++++++++++++++

A true/false option to add a Globus transfer option to the file download menu which is not yet fully supported in the dataverse-globus app. See :ref:`globus-support` for details.



.. _supported MicroProfile Config API source: https://docs.payara.fish/community/docs/Technical%20Documentation/MicroProfile/Config/Overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import edu.harvard.iq.dataverse.privateurl.PrivateUrlServiceBean;
import edu.harvard.iq.dataverse.locality.StorageSiteServiceBean;
import edu.harvard.iq.dataverse.search.savedsearch.SavedSearchServiceBean;
import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
import edu.harvard.iq.dataverse.util.BundleUtil;
import edu.harvard.iq.dataverse.util.SystemConfig;
Expand Down Expand Up @@ -440,7 +441,7 @@ private AuthenticatedUser getAuthenticatedUserFromSignedUrl() {
// ToDo - add null checks/ verify that calling methods catch things.
String user = httpRequest.getParameter("user");
AuthenticatedUser targetUser = authSvc.getAuthenticatedUser(user);
String key = System.getProperty(SystemConfig.API_SIGNING_SECRET, "")
String key = JvmSettings.API_SIGNING_SECRET.lookupOptional().orElse("")
+ authSvc.findApiTokenByUser(targetUser).getTokenString();
String signedUrl = httpRequest.getRequestURL().toString() + "?" + httpRequest.getQueryString();
String method = httpRequest.getMethod();
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/api/Admin.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import edu.harvard.iq.dataverse.DataverseServiceBean;
import edu.harvard.iq.dataverse.DataverseSession;
import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.validation.EMailValidator;
import edu.harvard.iq.dataverse.EjbDataverseEngine;
import edu.harvard.iq.dataverse.GlobalId;
Expand Down Expand Up @@ -2277,7 +2278,7 @@ public Response getSignedUrl(JsonObject urlInfo) throws WrappedResponse {
if (key == null) {
return error(Response.Status.CONFLICT, "Do not have a valid user with apiToken");
}
key = System.getProperty(SystemConfig.API_SIGNING_SECRET, "") + key;
key = JvmSettings.API_SIGNING_SECRET.lookupOptional().orElse("") + key;
}

String baseUrl = urlInfo.getString("url");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.FileMetadata;
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.util.SystemConfig;
import edu.harvard.iq.dataverse.util.URLTokenUtil;

Expand Down Expand Up @@ -117,7 +118,7 @@ public String handleRequest(boolean preview) {
}
if (apiToken != null) {
callback = UrlSignerUtil.signUrl(callback, 5, apiToken.getAuthenticatedUser().getUserIdentifier(), HttpMethod.GET,
System.getProperty(SystemConfig.API_SIGNING_SECRET, "") + apiToken.getTokenString());
JvmSettings.API_SIGNING_SECRET.lookupOptional().orElse("") + apiToken.getTokenString());
}
paramsString= "?callback=" + Base64.getEncoder().encodeToString(StringUtils.getBytesUtf8(callback));
if (getLocaleCode() != null) {
Expand Down Expand Up @@ -189,7 +190,7 @@ public JsonObjectBuilder createPostBody(JsonObject params) {
ApiToken apiToken = getApiToken();
if (apiToken != null) {
url = UrlSignerUtil.signUrl(apiPath, timeout, apiToken.getAuthenticatedUser().getUserIdentifier(), httpmethod,
System.getProperty(SystemConfig.API_SIGNING_SECRET, "") + getApiToken().getTokenString());
JvmSettings.API_SIGNING_SECRET.lookupOptional().orElse("") + getApiToken().getTokenString());
}
logger.fine("Signed URL: " + url);
apisBuilder.add(Json.createObjectBuilder().add(NAME, name).add(HTTP_METHOD, httpmethod)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public enum JvmSettings {
VERSION(PREFIX, "version"),
BUILD(PREFIX, "build"),

// API SETTINGS
SCOPE_API(PREFIX, "api"),
API_SIGNING_SECRET(SCOPE_API, "signing-secret"),

;

private static final String SCOPE_SEPARATOR = ".";
Expand Down
5 changes: 0 additions & 5 deletions src/main/java/edu/harvard/iq/dataverse/util/SystemConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,6 @@ public class SystemConfig {
public final static String DEFAULTCURATIONLABELSET = "DEFAULT";
public final static String CURATIONLABELSDISABLED = "DISABLED";

// A secret used in signing URLs - individual urls are signed using this and the
// intended user's apiKey, creating an aggregate key that is unique to the user
// but not known to the user (as their apiKey is)
public final static String API_SIGNING_SECRET = "dataverse.api-signing-secret";

public String getVersion() {
return getVersion(false);
}
Expand Down

0 comments on commit 70e350f

Please sign in to comment.