From 9ae6a7a581b852f0bd7cdf96d4e05360acdfbbf1 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Mon, 14 Oct 2019 16:08:15 +0100 Subject: [PATCH] Tie up CSRF tokens in the frontend --- .../com/google/refine/commands/Command.java | 2 +- .../refine/commands/SetPreferenceCommand.java | 3 +- .../GetImportingJobStatusCommand.java | 7 ++--- .../importing/ImportingControllerCommand.java | 4 +++ .../importing/DefaultImportingController.java | 3 +- .../controller.js | 2 +- .../file-selection-panel.js | 6 ++-- .../core/scripts/index/import-project-ui.js | 2 +- .../modules/core/scripts/preferences.js | 31 +++++++++++++++++++ .../modules/core/scripts/project/exporters.js | 2 +- .../core/scripts/project/process-panel.js | 2 +- 11 files changed, 49 insertions(+), 15 deletions(-) diff --git a/main/src/com/google/refine/commands/Command.java b/main/src/com/google/refine/commands/Command.java index eb42a23f0..7cfa2bda0 100644 --- a/main/src/com/google/refine/commands/Command.java +++ b/main/src/com/google/refine/commands/Command.java @@ -317,7 +317,7 @@ public abstract class Command { w.close(); } - static protected void respondJSON(HttpServletResponse response, Object o) + public static void respondJSON(HttpServletResponse response, Object o) throws IOException { respondJSON(response, o, new Properties()); diff --git a/main/src/com/google/refine/commands/SetPreferenceCommand.java b/main/src/com/google/refine/commands/SetPreferenceCommand.java index f6c1d2c6a..bc897f8a9 100644 --- a/main/src/com/google/refine/commands/SetPreferenceCommand.java +++ b/main/src/com/google/refine/commands/SetPreferenceCommand.java @@ -34,6 +34,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.commands; import java.io.IOException; +import java.util.Collections; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -67,7 +68,7 @@ public class SetPreferenceCommand extends Command { ps.put(prefName, PreferenceStore.loadObject(o)); - respond(response, "{ \"code\" : \"ok\" }"); + respondJSON(response, Collections.singletonMap("code", "ok")); } catch (IOException e) { respondException(response, e); } diff --git a/main/src/com/google/refine/commands/importing/GetImportingJobStatusCommand.java b/main/src/com/google/refine/commands/importing/GetImportingJobStatusCommand.java index fbfe4d0a6..cc6ec9afe 100644 --- a/main/src/com/google/refine/commands/importing/GetImportingJobStatusCommand.java +++ b/main/src/com/google/refine/commands/importing/GetImportingJobStatusCommand.java @@ -34,7 +34,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.refine.commands.importing; import java.io.IOException; -import java.io.Writer; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -46,7 +45,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.refine.commands.Command; import com.google.refine.importing.ImportingJob; import com.google.refine.importing.ImportingManager; -import com.google.refine.util.ParsingUtilities; public class GetImportingJobStatusCommand extends Command { protected static class JobStatusResponse { @@ -77,11 +75,10 @@ public class GetImportingJobStatusCommand extends Command { long jobID = Long.parseLong(request.getParameter("jobID")); ImportingJob job = ImportingManager.getJob(jobID); - Writer w = response.getWriter(); if (job == null) { - ParsingUtilities.defaultWriter.writeValue(w, new JobStatusResponse("error", "No such import job", null)); + respondJSON(response, new JobStatusResponse("error", "No such import job", null)); } else { - ParsingUtilities.defaultWriter.writeValue(w, new JobStatusResponse("ok", null, job)); + respondJSON(response, new JobStatusResponse("ok", null, job)); } } } diff --git a/main/src/com/google/refine/commands/importing/ImportingControllerCommand.java b/main/src/com/google/refine/commands/importing/ImportingControllerCommand.java index db7097a24..1acb732b0 100644 --- a/main/src/com/google/refine/commands/importing/ImportingControllerCommand.java +++ b/main/src/com/google/refine/commands/importing/ImportingControllerCommand.java @@ -63,6 +63,8 @@ public class ImportingControllerCommand extends Command { ImportingController controller = getController(request); if (controller != null) { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); controller.doPost(request, response); } else { HttpUtilities.respond(response, "error", "No such import controller"); @@ -75,6 +77,8 @@ public class ImportingControllerCommand extends Command { ImportingController controller = getController(request); if (controller != null) { + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Type", "application/json"); controller.doPost(request, response); } else { HttpUtilities.respond(response, "error", "No such import controller"); diff --git a/main/src/com/google/refine/importing/DefaultImportingController.java b/main/src/com/google/refine/importing/DefaultImportingController.java index ed8dbba75..a11812169 100644 --- a/main/src/com/google/refine/importing/DefaultImportingController.java +++ b/main/src/com/google/refine/importing/DefaultImportingController.java @@ -50,6 +50,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.refine.RefineServlet; +import com.google.refine.commands.Command; import com.google.refine.commands.HttpUtilities; import com.google.refine.importing.ImportingManager.Format; import com.google.refine.util.JSONUtilities; @@ -218,7 +219,7 @@ public class DefaultImportingController implements ImportingController { JSONUtilities.safePut(result, "status", "ok"); JSONUtilities.safePut(result, "options", options); - HttpUtilities.respond(response, result.toString()); + Command.respondJSON(response, result); } else { HttpUtilities.respond(response, "error", "Unrecognized format or format has no parser"); } diff --git a/main/webapp/modules/core/scripts/index/default-importing-controller/controller.js b/main/webapp/modules/core/scripts/index/default-importing-controller/controller.js index cb749bb79..a1eb4492a 100644 --- a/main/webapp/modules/core/scripts/index/default-importing-controller/controller.js +++ b/main/webapp/modules/core/scripts/index/default-importing-controller/controller.js @@ -251,7 +251,7 @@ Refine.DefaultImportingController.prototype.getPreviewData = function(callback, var result = {}; $.post( - "command/core/get-models?" + $.param({ "importingJobID" : this._jobID }), + "command/core/get-models?" + $.param({ "importingJobID" : self._jobID }), null, function(data) { for (var n in data) { diff --git a/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js b/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js index 30469ec56..d7bc53f05 100644 --- a/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js +++ b/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js @@ -332,12 +332,12 @@ Refine.DefaultImportingController.prototype._commitFileSelection = function() { $.post( "command/core/importing-controller?" + $.param({ "controller": "core/default-importing-controller", - "jobID": this._jobID, + "jobID": self._jobID, "subCommand": "update-file-selection", "csrf_token": token }), { - "fileSelection" : JSON.stringify(this._job.config.fileSelection) + "fileSelection" : JSON.stringify(self._job.config.fileSelection) }, function(data) { dismissBusy(); @@ -348,7 +348,7 @@ Refine.DefaultImportingController.prototype._commitFileSelection = function() { self._createProjectUI.showImportJobError((data.message) ? ($.i18n('core-index-import/error')+ ' ' + data.message) : $.i18n('core-index-import/unknown-err')); } else { // Different files might be selected. We start over again. - delete this._parserOptions; + delete self._parserOptions; self._job = data.job; self._showParsingPanel(true); diff --git a/main/webapp/modules/core/scripts/index/import-project-ui.js b/main/webapp/modules/core/scripts/index/import-project-ui.js index 3561611d0..8535cec5f 100644 --- a/main/webapp/modules/core/scripts/index/import-project-ui.js +++ b/main/webapp/modules/core/scripts/index/import-project-ui.js @@ -35,7 +35,7 @@ Refine.ImportProjectUI = function(elmt) { elmt.html(DOM.loadHTML("core", "scripts/index/import-project-ui.html")); Refine.wrapCSRF(function(token) { - elem.attr('action', "command/core/import-project?" + $.param({ csrf_token: token}); + elmt.attr('action', "command/core/import-project?" + $.param({ csrf_token: token})); }); this._elmt = elmt; diff --git a/main/webapp/modules/core/scripts/preferences.js b/main/webapp/modules/core/scripts/preferences.js index 2a799ae8f..a4bd9af52 100644 --- a/main/webapp/modules/core/scripts/preferences.js +++ b/main/webapp/modules/core/scripts/preferences.js @@ -33,6 +33,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var preferenceUIs = []; +var Refine = { +}; + +// Requests a CSRF token and calls the supplied callback +// with the token +Refine.wrapCSRF = function(onCSRF) { + $.get( + "command/core/get-csrf-token", + {}, + function(response) { + onCSRF(response['token']); + }, + "json" + ); +}; + +// Performs a POST request where an additional CSRF token +// is supplied in the POST data. The arguments match those +// of $.post(). +Refine.postCSRF = function(url, data, success, dataType, failCallback) { + return Refine.wrapCSRF(function(token) { + var fullData = data || {}; + fullData['csrf_token'] = token; + var req = $.post(url, fullData, success, dataType); + if (failCallback !== undefined) { + req.fail(failCallback); + } + }); +}; + + var lang = (navigator.language|| navigator.userLanguage).split("-")[0]; var dictionary = ""; $.ajax({ diff --git a/main/webapp/modules/core/scripts/project/exporters.js b/main/webapp/modules/core/scripts/project/exporters.js index ef6206bf4..bf0e821e6 100644 --- a/main/webapp/modules/core/scripts/project/exporters.js +++ b/main/webapp/modules/core/scripts/project/exporters.js @@ -190,7 +190,7 @@ ExporterManager.handlers.exportProject = function() { var name = window.prompt(prompt, theProject.metadata.name); if (name) { var dismiss = DialogSystem.showBusy($.i18n('gdata-exporter/uploading')); - $.post( + Refine.postCSRF( "command/gdata/upload", { "project" : theProject.id, diff --git a/main/webapp/modules/core/scripts/project/process-panel.js b/main/webapp/modules/core/scripts/project/process-panel.js index 29d38ba35..67f3d18ee 100644 --- a/main/webapp/modules/core/scripts/project/process-panel.js +++ b/main/webapp/modules/core/scripts/project/process-panel.js @@ -200,7 +200,7 @@ ProcessPanel.prototype._render = function(newData) { } else { if (window.confirm($.i18n('core-project/last-op-er')+':\n' + messages + '\n\n'+$.i18n('core-project/continue-remaining')+'?')) { - $.post( + Refine.postCSRF( "command/core/apply-operations?" + $.param({ project: theProject.id }), { operations: '[]' }, function(o) {},