Skip to content

Commit

Permalink
refactor to allow URL token substitution outside tools framework
Browse files Browse the repository at this point in the history
  • Loading branch information
qqmyers committed Jun 21, 2022
1 parent 881e3db commit 208ab95
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -299,64 +299,6 @@ public JsonObjectBuilder toJson() {
return jab;
}

public enum ReservedWord {

// TODO: Research if a format like "{reservedWord}" is easily parse-able or if another format would be
// better. The choice of curly braces is somewhat arbitrary, but has been observed in documenation for
// various REST APIs. For example, "Variable substitutions will be made when a variable is named in {brackets}."
// from https://swagger.io/specification/#fixed-fields-29 but that's for URLs.
FILE_ID("fileId"),
FILE_PID("filePid"),
SITE_URL("siteUrl"),
API_TOKEN("apiToken"),
// datasetId is the database id
DATASET_ID("datasetId"),
// datasetPid is the DOI or Handle
DATASET_PID("datasetPid"),
DATASET_VERSION("datasetVersion"),
FILE_METADATA_ID("fileMetadataId"),
LOCALE_CODE("localeCode"),
ALLOWED_URLS("allowedUrls");

private final String text;
private final String START = "{";
private final String END = "}";

private ReservedWord(final String text) {
this.text = START + text + END;
}

/**
* This is a centralized method that enforces that only reserved words
* are allowed to be used by external tools. External tool authors
* cannot pass their own query parameters through Dataverse such as
* "mode=mode1".
*
* @throws IllegalArgumentException
*/
public static ReservedWord fromString(String text) throws IllegalArgumentException {
if (text != null) {
for (ReservedWord reservedWord : ReservedWord.values()) {
if (text.equals(reservedWord.text)) {
return reservedWord;
}
}
}
// TODO: Consider switching to a more informative message that enumerates the valid reserved words.
boolean moreInformativeMessage = false;
if (moreInformativeMessage) {
throw new IllegalArgumentException("Unknown reserved word: " + text + ". A reserved word must be one of these values: " + Arrays.asList(ReservedWord.values()) + ".");
} else {
throw new IllegalArgumentException("Unknown reserved word: " + text);
}
}

@Override
public String toString() {
return text;
}
}

public String getDescriptionLang() {
String description = "";
if (this.toolName != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.FileMetadata;
import edu.harvard.iq.dataverse.GlobalId;
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
import edu.harvard.iq.dataverse.externaltools.ExternalTool.ReservedWord;
import edu.harvard.iq.dataverse.util.SystemConfig;
import edu.harvard.iq.dataverse.util.URLTokenUtil;

import edu.harvard.iq.dataverse.util.UrlSignerUtil;
import java.io.IOException;
import java.io.StringReader;
Expand All @@ -19,6 +18,7 @@
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
Expand All @@ -31,8 +31,7 @@
* instantiated. Applies logic based on an {@link ExternalTool} specification,
* such as constructing a URL to access that file.
*/
public class ExternalToolHandler {

public class ExternalToolHandler extends URLTokenUtil {
/**
* @return the allowedUrls
*/
Expand All @@ -54,15 +53,8 @@ public void setUser(String user) {
this.user = user;
}

private static final Logger logger = Logger.getLogger(ExternalToolHandler.class.getCanonicalName());

private final ExternalTool externalTool;
private final DataFile dataFile;
private final Dataset dataset;
private final FileMetadata fileMetadata;

private ApiToken apiToken;
private String localeCode;
private String requestMethod;
private String toolContext;
private String user;
Expand All @@ -78,23 +70,9 @@ public void setUser(String user) {
* used anonymously.
*/
public ExternalToolHandler(ExternalTool externalTool, DataFile dataFile, ApiToken apiToken, FileMetadata fileMetadata, String localeCode) {
super(dataFile, apiToken, fileMetadata, localeCode);
this.externalTool = externalTool;
toolContext = externalTool.getToolUrl();
if (dataFile == null) {
String error = "A DataFile is required.";
logger.warning("Error in ExternalToolHandler constructor: " + error);
throw new IllegalArgumentException(error);
}
if (fileMetadata == null) {
String error = "A FileMetadata is required.";
logger.warning("Error in ExternalToolHandler constructor: " + error);
throw new IllegalArgumentException(error);
}
this.dataFile = dataFile;
this.apiToken = apiToken;
this.fileMetadata = fileMetadata;
dataset = fileMetadata.getDatasetVersion().getDataset();
this.localeCode = localeCode;
}

/**
Expand All @@ -106,33 +84,8 @@ public ExternalToolHandler(ExternalTool externalTool, DataFile dataFile, ApiToke
* used anonymously.
*/
public ExternalToolHandler(ExternalTool externalTool, Dataset dataset, ApiToken apiToken, String localeCode) {
super(dataset, apiToken, localeCode);
this.externalTool = externalTool;
if (dataset == null) {
String error = "A Dataset is required.";
logger.warning("Error in ExternalToolHandler constructor: " + error);
throw new IllegalArgumentException(error);
}
this.dataset = dataset;
this.apiToken = apiToken;
this.dataFile = null;
this.fileMetadata = null;
this.localeCode = localeCode;
}

public DataFile getDataFile() {
return dataFile;
}

public FileMetadata getFileMetadata() {
return fileMetadata;
}

public ApiToken getApiToken() {
return apiToken;
}

public String getLocaleCode() {
return localeCode;
}

// TODO: rename to handleRequest() to someday handle sending headers as well as query parameters.
Expand Down Expand Up @@ -175,63 +128,6 @@ public String handleRequest(boolean preview) {
}
}

private String getQueryParam(String key, String value) {
ReservedWord reservedWord = ReservedWord.fromString(value);
switch (reservedWord) {
case FILE_ID:
// getDataFile is never null for file tools because of the constructor
return key + "=" + getDataFile().getId();
case FILE_PID:
GlobalId filePid = getDataFile().getGlobalId();
if (filePid != null) {
return key + "=" + getDataFile().getGlobalId();
}
break;
case SITE_URL:
siteUrl = SystemConfig.getDataverseSiteUrlStatic();
return key + "=" + siteUrl;
case API_TOKEN:
String apiTokenString = null;
ApiToken theApiToken = getApiToken();
if (theApiToken != null) {
apiTokenString = theApiToken.getTokenString();
return key + "=" + apiTokenString;
}
break;
case DATASET_ID:
return key + "=" + dataset.getId();
case DATASET_PID:
return key + "=" + dataset.getGlobalId().asString();
case DATASET_VERSION:
String versionString = null;
if(fileMetadata!=null) { //true for file case
versionString = fileMetadata.getDatasetVersion().getFriendlyVersionNumber();
} else { //Dataset case - return the latest visible version (unless/until the dataset case allows specifying a version)
if (getApiToken() != null) {
versionString = dataset.getLatestVersion().getFriendlyVersionNumber();
} else {
versionString = dataset.getLatestVersionForCopy().getFriendlyVersionNumber();
}
}
if (("DRAFT").equals(versionString)) {
versionString = ":draft"; // send the token needed in api calls that can be substituted for a numeric
// version.
}
return key + "=" + versionString;
case FILE_METADATA_ID:
if(fileMetadata!=null) { //true for file case
return key + "=" + fileMetadata.getId();
}
case LOCALE_CODE:
return key + "=" + getLocaleCode();
case ALLOWED_URLS:
return key + "=" + getAllowedUrls();
default:
break;
}
return null;
}


private String postFormData(Integer timeout,List<String> params ) throws IOException, InterruptedException{
String url = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.DataFileServiceBean;
import edu.harvard.iq.dataverse.authorization.users.ApiToken;
import edu.harvard.iq.dataverse.externaltools.ExternalTool.ReservedWord;
import edu.harvard.iq.dataverse.externaltools.ExternalTool.Type;
import edu.harvard.iq.dataverse.util.URLTokenUtil;
import edu.harvard.iq.dataverse.util.URLTokenUtil.ReservedWord;
import edu.harvard.iq.dataverse.externaltools.ExternalTool.Scope;

import java.io.StringReader;
Expand Down
Loading

0 comments on commit 208ab95

Please sign in to comment.