diff --git a/extensions/database/module/scripts/database-extension.js b/extensions/database/module/scripts/database-extension.js
index 4c4347935..ab4235945 100644
--- a/extensions/database/module/scripts/database-extension.js
+++ b/extensions/database/module/scripts/database-extension.js
@@ -69,7 +69,7 @@ DatabaseExtension.handleConnectClicked = function(connectionName) {
databaseConfig.initialDatabase = savedConfig.databaseName;
databaseConfig.initialSchema = savedConfig.databaseSchema;
- $.post(
+ Refine.postCSRF(
"command/database/connect",
databaseConfig,
@@ -101,10 +101,10 @@ DatabaseExtension.handleConnectClicked = function(connectionName) {
}
},
- "json"
- ).fail(function( jqXhr, textStatus, errorThrown ){
- alert( textStatus + ':' + errorThrown );
- });
+ "json",
+ function( jqXhr, textStatus, errorThrown ){
+ alert( textStatus + ':' + errorThrown );
+ });
}
diff --git a/extensions/database/module/scripts/index/database-import-controller.js b/extensions/database/module/scripts/index/database-import-controller.js
index 636af183c..30eb34938 100644
--- a/extensions/database/module/scripts/index/database-import-controller.js
+++ b/extensions/database/module/scripts/index/database-import-controller.js
@@ -65,33 +65,36 @@ Refine.DatabaseImportController.prototype.startImportingDocument = function(quer
//alert(queryInfo.query);
var self = this;
- $.post(
+ Refine.postCSRF(
"command/core/create-importing-job",
null,
function(data) {
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "database/database-import-controller",
- "subCommand": "initialize-parser-ui"
- }),
- queryInfo,
-
- function(data2) {
- dismiss();
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "database/database-import-controller",
+ "subCommand": "initialize-parser-ui",
+ "csrf_token": token
+ }),
+ queryInfo,
- if (data2.status == 'ok') {
- self._queryInfo = queryInfo;
- self._jobID = data.jobID;
- self._options = data2.options;
-
- self._showParsingPanel();
-
- } else {
- alert(data2.message);
- }
- },
- "json"
- );
+ function(data2) {
+ dismiss();
+
+ if (data2.status == 'ok') {
+ self._queryInfo = queryInfo;
+ self._jobID = data.jobID;
+ self._options = data2.options;
+
+ self._showParsingPanel();
+
+ } else {
+ alert(data2.message);
+ }
+ },
+ "json"
+ );
+ });
},
"json"
);
@@ -248,40 +251,43 @@ Refine.DatabaseImportController.prototype._updatePreview = function() {
this._queryInfo.options = JSON.stringify(this.getOptions());
//alert("options:" + this._queryInfo.options);
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "database/database-import-controller",
- "jobID": this._jobID,
- "subCommand": "parse-preview"
- }),
-
- this._queryInfo,
-
- function(result) {
- if (result.status == "ok") {
- self._getPreviewData(function(projectData) {
- self._parsingPanelElmts.progressPanel.hide();
- self._parsingPanelElmts.dataPanel.show();
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "database/database-import-controller",
+ "jobID": self._jobID,
+ "subCommand": "parse-preview",
+ "csrf_token": token
+ }),
+
+ this._queryInfo,
+
+ function(result) {
+ if (result.status == "ok") {
+ self._getPreviewData(function(projectData) {
+ self._parsingPanelElmts.progressPanel.hide();
+ self._parsingPanelElmts.dataPanel.show();
- new Refine.PreviewTable(projectData, self._parsingPanelElmts.dataPanel.unbind().empty());
- });
- } else {
-
- alert('Errors:\n' + (result.message) ? result.message : Refine.CreateProjectUI.composeErrorMessage(job));
- self._parsingPanelElmts.progressPanel.hide();
-
- Refine.CreateProjectUI.cancelImportingJob(self._jobID);
-
- delete self._jobID;
- delete self._options;
-
- self._createProjectUI.showSourceSelectionPanel();
-
-
- }
- },
- "json"
- );
+ new Refine.PreviewTable(projectData, self._parsingPanelElmts.dataPanel.unbind().empty());
+ });
+ } else {
+
+ alert('Errors:\n' + (result.message) ? result.message : Refine.CreateProjectUI.composeErrorMessage(job));
+ self._parsingPanelElmts.progressPanel.hide();
+
+ Refine.CreateProjectUI.cancelImportingJob(self._jobID);
+
+ delete self._jobID;
+ delete self._options;
+
+ self._createProjectUI.showSourceSelectionPanel();
+
+
+ }
+ },
+ "json"
+ );
+ });
};
Refine.DatabaseImportController.prototype._getPreviewData = function(callback, numRows) {
@@ -329,51 +335,54 @@ Refine.DatabaseImportController.prototype._createProject = function() {
options.projectName = projectName;
this._queryInfo.options = JSON.stringify(options);
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "database/database-import-controller",
- "jobID": this._jobID,
- "subCommand": "create-project"
- }),
- this._queryInfo,
- function(o) {
- if (o.status == 'error') {
- alert(o.message);
- } else {
- var start = new Date();
- var timerID = window.setInterval(
- function() {
- self._createProjectUI.pollImportJob(
- start,
- self._jobID,
- timerID,
- function(job) {
- return "projectID" in job.config;
- },
- function(jobID, job) {
- //alert("jobID::" + jobID + " job :" + job);
- window.clearInterval(timerID);
- Refine.CreateProjectUI.cancelImportingJob(jobID);
- document.location = "project?project=" + job.config.projectID;
- },
- function(job) {
- alert(Refine.CreateProjectUI.composeErrorMessage(job));
- }
- );
- },
- 1000
- );
- self._createProjectUI.showImportProgressPanel($.i18n('database-import/creating'), function() {
- // stop the timed polling
- window.clearInterval(timerID);
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "database/database-import-controller",
+ "jobID": self._jobID,
+ "subCommand": "create-project",
+ "csrf_token": token
+ }),
+ this._queryInfo,
+ function(o) {
+ if (o.status == 'error') {
+ alert(o.message);
+ } else {
+ var start = new Date();
+ var timerID = window.setInterval(
+ function() {
+ self._createProjectUI.pollImportJob(
+ start,
+ self._jobID,
+ timerID,
+ function(job) {
+ return "projectID" in job.config;
+ },
+ function(jobID, job) {
+ //alert("jobID::" + jobID + " job :" + job);
+ window.clearInterval(timerID);
+ Refine.CreateProjectUI.cancelImportingJob(jobID);
+ document.location = "project?project=" + job.config.projectID;
+ },
+ function(job) {
+ alert(Refine.CreateProjectUI.composeErrorMessage(job));
+ }
+ );
+ },
+ 1000
+ );
+ self._createProjectUI.showImportProgressPanel($.i18n('database-import/creating'), function() {
+ // stop the timed polling
+ window.clearInterval(timerID);
- // explicitly cancel the import job
- Refine.CreateProjectUI.cancelImportingJob(jobID);
+ // explicitly cancel the import job
+ Refine.CreateProjectUI.cancelImportingJob(jobID);
- self._createProjectUI.showSourceSelectionPanel();
- });
- }
- },
- "json"
- );
+ self._createProjectUI.showSourceSelectionPanel();
+ });
+ }
+ },
+ "json"
+ );
+ });
};
diff --git a/extensions/database/module/scripts/index/database-source-ui.js b/extensions/database/module/scripts/index/database-source-ui.js
index 4a4e6e234..14c1d0acd 100644
--- a/extensions/database/module/scripts/index/database-source-ui.js
+++ b/extensions/database/module/scripts/index/database-source-ui.js
@@ -268,7 +268,7 @@ Refine.DatabaseSourceUI.prototype._executeQuery = function(jdbcQueryInfo) {
var dismiss = DialogSystem.showBusy($.i18n('database-import/checking'));
- $.post(
+ Refine.postCSRF(
"command/database/test-query",
jdbcQueryInfo,
function(jdbcConnectionResult) {
@@ -277,8 +277,8 @@ Refine.DatabaseSourceUI.prototype._executeQuery = function(jdbcQueryInfo) {
self._controller.startImportingDocument(jdbcQueryInfo);
},
- "json"
- ).fail(function( jqXhr, textStatus, errorThrown ){
+ "json",
+ function( jqXhr, textStatus, errorThrown ){
dismiss();
alert( textStatus + ':' + errorThrown );
@@ -288,7 +288,7 @@ Refine.DatabaseSourceUI.prototype._executeQuery = function(jdbcQueryInfo) {
Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo) {
var self = this;
- $.post(
+ Refine.postCSRF(
"command/database/saved-connection",
jdbcConnectionInfo,
function(settings) {
@@ -307,8 +307,8 @@ Refine.DatabaseSourceUI.prototype._saveConnection = function(jdbcConnectionInfo)
}
},
- "json"
- ).fail(function( jqXhr, textStatus, errorThrown ){
+ "json",
+ function( jqXhr, textStatus, errorThrown ){
alert( textStatus + ':' + errorThrown );
});
@@ -346,7 +346,7 @@ Refine.DatabaseSourceUI.prototype._loadSavedConnections = function() {
Refine.DatabaseSourceUI.prototype._testDatabaseConnect = function(jdbcConnectionInfo) {
var self = this;
- $.post(
+ Refine.postCSRF(
"command/database/test-connect",
jdbcConnectionInfo,
function(jdbcConnectionResult) {
@@ -357,8 +357,8 @@ Refine.DatabaseSourceUI.prototype._testDatabaseConnect = function(jdbcConnection
}
},
- "json"
- ).fail(function( jqXhr, textStatus, errorThrown ){
+ "json",
+ function( jqXhr, textStatus, errorThrown ){
alert( textStatus + ':' + errorThrown );
});
};
@@ -366,7 +366,7 @@ Refine.DatabaseSourceUI.prototype._testDatabaseConnect = function(jdbcConnection
Refine.DatabaseSourceUI.prototype._connect = function(jdbcConnectionInfo) {
var self = this;
- $.post(
+ Refine.postCSRF(
"command/database/connect",
jdbcConnectionInfo,
function(databaseInfo) {
@@ -398,8 +398,8 @@ Refine.DatabaseSourceUI.prototype._connect = function(jdbcConnectionInfo) {
}
},
- "json"
- ).fail(function( jqXhr, textStatus, errorThrown ){
+ "json",
+ function( jqXhr, textStatus, errorThrown ){
alert( textStatus + ':' + errorThrown );
});
diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java
index 19f06d86a..0e1fa1e6a 100644
--- a/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java
+++ b/extensions/database/src/com/google/refine/extension/database/cmd/ConnectCommand.java
@@ -56,6 +56,10 @@ public class ConnectCommand extends DatabaseCommand {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request);
if(logger.isDebugEnabled()) {
diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java
index 17d70d954..863ec9423 100644
--- a/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java
+++ b/extensions/database/src/com/google/refine/extension/database/cmd/ExecuteQueryCommand.java
@@ -56,7 +56,10 @@ public class ExecuteQueryCommand extends DatabaseCommand {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
-
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request);
String query = request.getParameter("queryString");
diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java
index a30e2d000..0d3816d8e 100644
--- a/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java
+++ b/extensions/database/src/com/google/refine/extension/database/cmd/SavedConnectionCommand.java
@@ -228,6 +228,10 @@ public class SavedConnectionCommand extends DatabaseCommand {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
if(logger.isDebugEnabled()) {
logger.debug("doPost Connection: {}", request.getParameter("connectionName"));
diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java
index 460a3ffc6..af7b6f0f9 100644
--- a/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java
+++ b/extensions/database/src/com/google/refine/extension/database/cmd/TestConnectCommand.java
@@ -54,7 +54,10 @@ public class TestConnectCommand extends DatabaseCommand {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
-
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
DatabaseConfiguration databaseConfiguration = getJdbcConfiguration(request);
if(logger.isDebugEnabled()) {
diff --git a/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java b/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java
index 5ce53961d..82610f38b 100644
--- a/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java
+++ b/extensions/database/src/com/google/refine/extension/database/cmd/TestQueryCommand.java
@@ -56,6 +56,10 @@ public class TestQueryCommand extends DatabaseCommand {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
DatabaseConfiguration dbConfig = getJdbcConfiguration(request);
String query = request.getParameter("query");
diff --git a/extensions/database/tests/src/com/google/refine/extension/database/cmd/ConnectCommandTest.java b/extensions/database/tests/src/com/google/refine/extension/database/cmd/ConnectCommandTest.java
index 1ddc45267..96a2c0337 100644
--- a/extensions/database/tests/src/com/google/refine/extension/database/cmd/ConnectCommandTest.java
+++ b/extensions/database/tests/src/com/google/refine/extension/database/cmd/ConnectCommandTest.java
@@ -20,6 +20,7 @@ import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.refine.commands.Command;
import com.google.refine.extension.database.DBExtensionTests;
import com.google.refine.extension.database.DatabaseConfiguration;
import com.google.refine.extension.database.DatabaseService;
@@ -75,6 +76,7 @@ public class ConnectCommandTest extends DBExtensionTests {
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -94,5 +96,18 @@ public class ConnectCommandTest extends DBExtensionTests {
Assert.assertNotNull(databaseInfo);
}
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ when(response.getWriter()).thenReturn(pw);
+ ConnectCommand connectCommand = new ConnectCommand();
+
+ connectCommand.doPost(request, response);
+ Assert.assertEquals(
+ ParsingUtilities.mapper.readValue("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", ObjectNode.class),
+ ParsingUtilities.mapper.readValue(sw.toString(), ObjectNode.class));
+ }
}
diff --git a/extensions/database/tests/src/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java b/extensions/database/tests/src/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java
index 92fb66b28..1dfafe3ce 100644
--- a/extensions/database/tests/src/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java
+++ b/extensions/database/tests/src/com/google/refine/extension/database/cmd/ExecuteQueryCommandTest.java
@@ -19,6 +19,7 @@ import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.refine.commands.Command;
import com.google.refine.extension.database.DBExtensionTests;
import com.google.refine.extension.database.DatabaseConfiguration;
import com.google.refine.extension.database.DatabaseService;
@@ -72,6 +73,7 @@ public class ExecuteQueryCommandTest extends DBExtensionTests {
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
when(request.getParameter("queryString")).thenReturn("SELECT count(*) FROM " + testTable);
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
@@ -93,4 +95,17 @@ public class ExecuteQueryCommandTest extends DBExtensionTests {
Assert.assertNotNull(queryResult);
}
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ when(response.getWriter()).thenReturn(pw);
+ ConnectCommand connectCommand = new ConnectCommand();
+
+ connectCommand.doPost(request, response);
+ Assert.assertEquals(
+ ParsingUtilities.mapper.readValue("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", ObjectNode.class),
+ ParsingUtilities.mapper.readValue(sw.toString(), ObjectNode.class));
+ }
}
diff --git a/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java b/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java
index 32ea6fb27..2225b41f7 100644
--- a/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java
+++ b/extensions/database/tests/src/com/google/refine/extension/database/cmd/SavedConnectionCommandTest.java
@@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.refine.ProjectManager;
import com.google.refine.ProjectMetadata;
import com.google.refine.RefineServlet;
+import com.google.refine.commands.Command;
import com.google.refine.extension.database.DBExtensionTestUtils;
import com.google.refine.extension.database.DBExtensionTests;
import com.google.refine.extension.database.DatabaseConfiguration;
@@ -125,6 +126,7 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -150,6 +152,7 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -187,6 +190,7 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -227,6 +231,7 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
SUT.doPut(request, response);
@@ -309,6 +314,7 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
@@ -320,7 +326,18 @@ public class SavedConnectionCommandTest extends DBExtensionTests{
verify(response, times(1)).sendError(HttpStatus.SC_BAD_REQUEST, "Connection Name is Invalid. Expecting [a-zA-Z0-9._-]");
}
-
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ when(response.getWriter()).thenReturn(pw);
+
+ SUT.doPost(request, response);
+ Assert.assertEquals(
+ ParsingUtilities.mapper.readValue("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", ObjectNode.class),
+ ParsingUtilities.mapper.readValue(sw.toString(), ObjectNode.class));
+ }
}
diff --git a/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestConnectCommandTest.java b/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestConnectCommandTest.java
index 064f29de9..7666e3828 100644
--- a/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestConnectCommandTest.java
+++ b/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestConnectCommandTest.java
@@ -19,6 +19,7 @@ import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.refine.commands.Command;
import com.google.refine.extension.database.DBExtensionTests;
import com.google.refine.extension.database.DatabaseConfiguration;
import com.google.refine.extension.database.DatabaseService;
@@ -74,6 +75,7 @@ public class TestConnectCommandTest extends DBExtensionTests{
when(request.getParameter("databaseUser")).thenReturn(testDbConfig.getDatabaseUser());
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
@@ -92,5 +94,19 @@ public class TestConnectCommandTest extends DBExtensionTests{
Assert.assertEquals(code, "ok");
}
+
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ when(response.getWriter()).thenReturn(pw);
+ ConnectCommand connectCommand = new ConnectCommand();
+
+ connectCommand.doPost(request, response);
+ Assert.assertEquals(
+ ParsingUtilities.mapper.readValue("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", ObjectNode.class),
+ ParsingUtilities.mapper.readValue(sw.toString(), ObjectNode.class));
+ }
}
diff --git a/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestQueryCommandTest.java b/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestQueryCommandTest.java
index 5a87fbc2b..ab56dc7e6 100644
--- a/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestQueryCommandTest.java
+++ b/extensions/database/tests/src/com/google/refine/extension/database/cmd/TestQueryCommandTest.java
@@ -19,6 +19,7 @@ import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.refine.commands.Command;
import com.google.refine.extension.database.DBExtensionTests;
import com.google.refine.extension.database.DatabaseConfiguration;
import com.google.refine.extension.database.DatabaseService;
@@ -73,7 +74,7 @@ public class TestQueryCommandTest extends DBExtensionTests {
when(request.getParameter("databasePassword")).thenReturn(testDbConfig.getDatabasePassword());
when(request.getParameter("initialDatabase")).thenReturn(testDbConfig.getDatabaseName());
when(request.getParameter("query")).thenReturn("SELECT count(*) FROM " + testTable);
-
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
StringWriter sw = new StringWriter();
@@ -94,5 +95,19 @@ public class TestQueryCommandTest extends DBExtensionTests {
Assert.assertNotNull(queryResult);
}
+
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+
+ when(response.getWriter()).thenReturn(pw);
+ TestQueryCommand connectCommand = new TestQueryCommand();
+
+ connectCommand.doPost(request, response);
+ Assert.assertEquals(
+ ParsingUtilities.mapper.readValue("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", ObjectNode.class),
+ ParsingUtilities.mapper.readValue(sw.toString(), ObjectNode.class));
+ }
}
diff --git a/extensions/gdata/module/scripts/index/gdata-source-ui.js b/extensions/gdata/module/scripts/index/gdata-source-ui.js
index d67e6ed50..d7773ba42 100644
--- a/extensions/gdata/module/scripts/index/gdata-source-ui.js
+++ b/extensions/gdata/module/scripts/index/gdata-source-ui.js
@@ -109,17 +109,20 @@ Refine.GDataSourceUI.prototype._listDocuments = function() {
this._elmts.progressPage.show();
var self = this;
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "gdata/gdata-importing-controller",
- "subCommand": "list-documents"
- }),
- null,
- function(o) {
- self._renderDocuments(o);
- },
- "json"
- );
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "gdata/gdata-importing-controller",
+ "subCommand": "list-documents",
+ "csrf_token": token
+ }),
+ null,
+ function(o) {
+ self._renderDocuments(o);
+ },
+ "json"
+ );
+ });
};
Refine.GDataSourceUI.prototype._renderDocuments = function(o) {
diff --git a/extensions/gdata/module/scripts/index/importing-controller.js b/extensions/gdata/module/scripts/index/importing-controller.js
index eda0afa16..d8fbde184 100644
--- a/extensions/gdata/module/scripts/index/importing-controller.js
+++ b/extensions/gdata/module/scripts/index/importing-controller.js
@@ -71,33 +71,36 @@ Refine.GDataImportingController.prototype.startImportingDocument = function(doc)
var dismiss = DialogSystem.showBusy($.i18n('gdata-import/preparing'));
var self = this;
- $.post(
+ Refine.postCSRF(
"command/core/create-importing-job",
null,
function(data) {
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "gdata/gdata-importing-controller",
- "subCommand": "initialize-parser-ui",
- "docUrl": doc.docSelfLink,
- "docType": doc.type
- }),
- null,
- function(data2) {
- dismiss();
-
- if (data2.status == 'ok') {
- self._doc = doc;
- self._jobID = data.jobID;
- self._options = data2.options;
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "gdata/gdata-importing-controller",
+ "subCommand": "initialize-parser-ui",
+ "docUrl": doc.docSelfLink,
+ "docType": doc.type,
+ "csrf_token": token
+ }),
+ null,
+ function(data2) {
+ dismiss();
- self._showParsingPanel();
- } else {
- alert(data2.message);
- }
- },
- "json"
- );
+ if (data2.status == 'ok') {
+ self._doc = doc;
+ self._jobID = data.jobID;
+ self._options = data2.options;
+
+ self._showParsingPanel();
+ } else {
+ alert(data2.message);
+ }
+ },
+ "json"
+ );
+ });
},
"json"
);
@@ -315,31 +318,34 @@ Refine.GDataImportingController.prototype._updatePreview = function() {
this._parsingPanelElmts.dataPanel.hide();
this._parsingPanelElmts.progressPanel.show();
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "gdata/gdata-importing-controller",
- "jobID": this._jobID,
- "subCommand": "parse-preview"
- }),
- {
- "options" : JSON.stringify(this.getOptions())
- },
- function(result) {
- if (result.status == "ok") {
- self._getPreviewData(function(projectData) {
- self._parsingPanelElmts.progressPanel.hide();
- self._parsingPanelElmts.dataPanel.show();
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "gdata/gdata-importing-controller",
+ "jobID": self._jobID,
+ "subCommand": "parse-preview",
+ "csrf_token": token
+ }),
+ {
+ "options" : JSON.stringify(self.getOptions())
+ },
+ function(result) {
+ if (result.status == "ok") {
+ self._getPreviewData(function(projectData) {
+ self._parsingPanelElmts.progressPanel.hide();
+ self._parsingPanelElmts.dataPanel.show();
- new Refine.PreviewTable(projectData, self._parsingPanelElmts.dataPanel.unbind().empty());
- });
- } else {
- self._parsingPanelElmts.progressPanel.hide();
- alert('Errors:\n' +
- (result.message) ? result.message : Refine.CreateProjectUI.composeErrorMessage(job));
- }
- },
- "json"
- );
+ new Refine.PreviewTable(projectData, self._parsingPanelElmts.dataPanel.unbind().empty());
+ });
+ } else {
+ self._parsingPanelElmts.progressPanel.hide();
+ alert('Errors:\n' +
+ (result.message) ? result.message : Refine.CreateProjectUI.composeErrorMessage(job));
+ }
+ },
+ "json"
+ );
+ });
};
Refine.GDataImportingController.prototype._getPreviewData = function(callback, numRows) {
@@ -385,52 +391,55 @@ Refine.GDataImportingController.prototype._createProject = function() {
var self = this;
var options = this.getOptions();
options.projectName = projectName;
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "gdata/gdata-importing-controller",
- "jobID": this._jobID,
- "subCommand": "create-project"
- }),
- {
- "options" : JSON.stringify(options)
- },
- function(o) {
- if (o.status == 'error') {
- alert(o.message);
- } else {
- var start = new Date();
- var timerID = window.setInterval(
- function() {
- self._createProjectUI.pollImportJob(
- start,
- self._jobID,
- timerID,
- function(job) {
- return "projectID" in job.config;
- },
- function(jobID, job) {
- window.clearInterval(timerID);
- Refine.CreateProjectUI.cancelImportingJob(jobID);
- document.location = "project?project=" + job.config.projectID;
- },
- function(job) {
- alert(Refine.CreateProjectUI.composeErrorMessage(job));
- }
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "gdata/gdata-importing-controller",
+ "jobID": self._jobID,
+ "subCommand": "create-project",
+ "csrf_token": token
+ }),
+ {
+ "options" : JSON.stringify(options)
+ },
+ function(o) {
+ if (o.status == 'error') {
+ alert(o.message);
+ } else {
+ var start = new Date();
+ var timerID = window.setInterval(
+ function() {
+ self._createProjectUI.pollImportJob(
+ start,
+ self._jobID,
+ timerID,
+ function(job) {
+ return "projectID" in job.config;
+ },
+ function(jobID, job) {
+ window.clearInterval(timerID);
+ Refine.CreateProjectUI.cancelImportingJob(jobID);
+ document.location = "project?project=" + job.config.projectID;
+ },
+ function(job) {
+ alert(Refine.CreateProjectUI.composeErrorMessage(job));
+ }
+ );
+ },
+ 1000
);
- },
- 1000
- );
- self._createProjectUI.showImportProgressPanel($.i18n('gdata-import/creating'), function() {
- // stop the timed polling
- window.clearInterval(timerID);
+ self._createProjectUI.showImportProgressPanel($.i18n('gdata-import/creating'), function() {
+ // stop the timed polling
+ window.clearInterval(timerID);
- // explicitly cancel the import job
- Refine.CreateProjectUI.cancelImportingJob(jobID);
+ // explicitly cancel the import job
+ Refine.CreateProjectUI.cancelImportingJob(jobID);
- self._createProjectUI.showSourceSelectionPanel();
- });
- }
- },
- "json"
- );
+ self._createProjectUI.showSourceSelectionPanel();
+ });
+ }
+ },
+ "json"
+ );
+ });
};
diff --git a/extensions/gdata/module/scripts/project/exporters.js b/extensions/gdata/module/scripts/project/exporters.js
index 92e244746..6a3f46256 100644
--- a/extensions/gdata/module/scripts/project/exporters.js
+++ b/extensions/gdata/module/scripts/project/exporters.js
@@ -54,7 +54,7 @@ $.i18n().load(dictionary, lang);
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/extensions/gdata/pom.xml b/extensions/gdata/pom.xml
index 787a5d13c..934720a9c 100644
--- a/extensions/gdata/pom.xml
+++ b/extensions/gdata/pom.xml
@@ -66,7 +66,9 @@
maven-surefire-plugin
${surefire.version}
- true
+
+ tests/conf/tests.xml
+
@@ -162,6 +164,12 @@
6.9.10
test
+
+ org.mockito
+ mockito-core
+ 2.23.4
+ test
+
diff --git a/extensions/gdata/src/com/google/refine/extension/gdata/UploadCommand.java b/extensions/gdata/src/com/google/refine/extension/gdata/UploadCommand.java
index e2272fecd..122b6f47b 100644
--- a/extensions/gdata/src/com/google/refine/extension/gdata/UploadCommand.java
+++ b/extensions/gdata/src/com/google/refine/extension/gdata/UploadCommand.java
@@ -43,6 +43,10 @@ public class UploadCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
String token = TokenCookie.getToken(request);
if (token == null) {
diff --git a/extensions/gdata/tests/conf/tests.xml b/extensions/gdata/tests/conf/tests.xml
new file mode 100644
index 000000000..df5920edc
--- /dev/null
+++ b/extensions/gdata/tests/conf/tests.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/gdata/tests/src/com/google/refine/extension/gdata/UploadCommandTest.java b/extensions/gdata/tests/src/com/google/refine/extension/gdata/UploadCommandTest.java
new file mode 100644
index 000000000..5f04f585c
--- /dev/null
+++ b/extensions/gdata/tests/src/com/google/refine/extension/gdata/UploadCommandTest.java
@@ -0,0 +1,49 @@
+package com.google.refine.extension.gdata;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.refine.commands.Command;
+import com.google.refine.util.ParsingUtilities;
+
+public class UploadCommandTest {
+ protected HttpServletRequest request = null;
+ protected HttpServletResponse response = null;
+ protected Command command = null;
+ protected StringWriter writer = null;
+
+ @BeforeMethod
+ public void setUpRequestResponse() {
+ request = mock(HttpServletRequest.class);
+ response = mock(HttpServletResponse.class);
+ writer = new StringWriter();
+ command = new UploadCommand();
+ try {
+ when(response.getWriter()).thenReturn(new PrintWriter(writer));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ Assert.assertEquals(
+ ParsingUtilities.mapper.readValue("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", ObjectNode.class),
+ ParsingUtilities.mapper.readValue(writer.toString(), ObjectNode.class));
+
+ }
+}
diff --git a/extensions/wikidata/module/scripts/dialogs/manage-account-dialog.js b/extensions/wikidata/module/scripts/dialogs/manage-account-dialog.js
index 22fe0d35e..fb35fd32e 100644
--- a/extensions/wikidata/module/scripts/dialogs/manage-account-dialog.js
+++ b/extensions/wikidata/module/scripts/dialogs/manage-account-dialog.js
@@ -56,7 +56,7 @@ ManageAccountDialog.display = function(logged_in_username, saved_credentials, ca
elmts.loginButton.click(function() {
frame.hide();
- $.post(
+ Refine.postCSRF(
"command/wikidata/login",
elmts.loginForm.serialize(),
function(data) {
@@ -71,7 +71,7 @@ ManageAccountDialog.display = function(logged_in_username, saved_credentials, ca
});
elmts.logoutButton.click(function() {
- $.post(
+ Refine.postCSRF(
"command/wikidata/login",
"logout=true",
function(data) {
diff --git a/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js b/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js
index 089cfedab..fa2bfd718 100644
--- a/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js
+++ b/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js
@@ -94,7 +94,7 @@ PerformEditsDialog.checkAndLaunch = function () {
ManageAccountDialog.ensureLoggedIn(function(logged_in_username) {
if (logged_in_username) {
var discardWaiter = DialogSystem.showBusy($.i18n('perform-wikidata-edits/analyzing-edits'));
- $.post(
+ Refine.postCSRF(
"command/wikidata/preview-wikibase-schema?" + $.param({ project: theProject.id }),
{ engine: JSON.stringify(ui.browsingEngine.getJSON()) },
function(data) {
diff --git a/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js b/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js
index 6c982d6a2..cc6760e17 100644
--- a/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js
+++ b/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js
@@ -1283,7 +1283,7 @@ SchemaAlignmentDialog.preview = function() {
$('.invalid-schema-warning').show();
return;
}
- $.post(
+ Refine.postCSRF(
"command/wikidata/preview-wikibase-schema?" + $.param({ project: theProject.id }),
{ schema: JSON.stringify(schema), engine: JSON.stringify(ui.browsingEngine.getJSON()) },
function(data) {
diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java
index 83c194582..cc3330934 100644
--- a/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java
+++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java
@@ -41,6 +41,11 @@ public class LoginCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
+
String username = request.getParameter("wb-username");
String password = request.getParameter("wb-password");
String remember = request.getParameter("remember-credentials");
diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java
index 7c1c25621..1ebdfb1dd 100644
--- a/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java
+++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java
@@ -45,6 +45,13 @@ import com.google.refine.commands.Command;
import com.google.refine.model.Project;
public class PreviewWikibaseSchemaCommand extends Command {
+
+ /**
+ * This command uses POST but is left CSRF-unprotected since it does not
+ * incur a side effect or state change in the backend.
+ * The reason why it uses POST is to make sure large schemas and engines
+ * can be passed as parameters.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommand.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommand.java
index 0115ff8d0..728eeefd2 100644
--- a/extensions/wikidata/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommand.java
+++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommand.java
@@ -46,6 +46,10 @@ public class SaveWikibaseSchemaCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java
index 36ca6c4bd..10221863b 100644
--- a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java
+++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java
@@ -1,6 +1,7 @@
package org.openrefine.wikidata.commands;
import static org.testng.Assert.assertEquals;
+import static org.mockito.Mockito.when;
import java.io.IOException;
@@ -9,6 +10,9 @@ import javax.servlet.ServletException;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.refine.commands.Command;
+import com.google.refine.util.TestUtils;
+
public class LoginCommandTest extends CommandTest {
@BeforeMethod
@@ -18,8 +22,16 @@ public class LoginCommandTest extends CommandTest {
@Test
public void testNoCredentials() throws ServletException, IOException {
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
+
command.doPost(request, response);
assertEquals("{\"logged_in\":false,\"username\":null}", writer.toString());
}
+
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ TestUtils.assertEqualAsJson("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", writer.toString());
+ }
}
diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java
index cd981382b..26d094c67 100644
--- a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java
+++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java
@@ -34,6 +34,9 @@ import javax.servlet.ServletException;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.refine.commands.Command;
+import com.google.refine.util.TestUtils;
+
public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest {
@BeforeMethod
@@ -44,6 +47,8 @@ public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest {
@Test
public void testValidSchema()
throws ServletException, IOException {
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
+
String schemaJson = jsonFromFile("schema/inception.json").toString();
when(request.getParameter("schema")).thenReturn(schemaJson);
@@ -54,6 +59,8 @@ public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest {
@Test
public void testInvalidSchema() throws ServletException, IOException {
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
+
String schemaJson = "{\"itemDocuments\":[{\"statementGroups\":[{\"statements\":[]}],"
+"\"nameDescs\":[]}],\"wikibasePrefix\":\"http://www.wikidata.org/entity/\"}";
@@ -62,4 +69,13 @@ public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest {
assertTrue(writer.toString().contains("\"error\""));
}
+
+ @Test
+ public void testCsrfProtection() throws ServletException, IOException {
+ String schemaJson = jsonFromFile("schema/inception.json").toString();
+ when(request.getParameter("schema")).thenReturn(schemaJson);
+
+ command.doPost(request, response);
+ TestUtils.assertEqualAsJson("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", writer.toString());
+ }
}
diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SchemaCommandTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SchemaCommandTest.java
index 8af1528cd..7316f2906 100644
--- a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SchemaCommandTest.java
+++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SchemaCommandTest.java
@@ -32,6 +32,7 @@ import javax.servlet.ServletException;
import org.testng.annotations.Test;
+import com.google.refine.commands.Command;
import com.google.refine.util.ParsingUtilities;
public abstract class SchemaCommandTest extends CommandTest {
@@ -39,9 +40,11 @@ public abstract class SchemaCommandTest extends CommandTest {
@Test
public void testNoSchema()
throws ServletException, IOException {
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
+
command.doPost(request, response);
- assertEquals("{\"code\":\"error\",\"message\":\"No Wikibase schema provided.\"}", writer.toString());
+ assertEquals(writer.toString(), "{\"code\":\"error\",\"message\":\"No Wikibase schema provided.\"}");
}
@Test
diff --git a/main/src/com/google/refine/commands/CSRFTokenFactory.java b/main/src/com/google/refine/commands/CSRFTokenFactory.java
new file mode 100644
index 000000000..af9c1ac36
--- /dev/null
+++ b/main/src/com/google/refine/commands/CSRFTokenFactory.java
@@ -0,0 +1,94 @@
+package com.google.refine.commands;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang.RandomStringUtils;
+
+import java.security.SecureRandom;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+/**
+ * Generates CSRF tokens and checks their validity.
+ * @author Antonin Delpeuch
+ *
+ */
+public class CSRFTokenFactory {
+
+ /**
+ * Maps each token to the time it was generated
+ */
+ protected final LoadingCache tokenCache;
+
+ /**
+ * Time to live for tokens, in seconds
+ */
+ protected final long timeToLive;
+
+ /**
+ * Length of the tokens to generate
+ */
+ protected final int tokenLength;
+
+ /**
+ * Random number generator used to create tokens
+ */
+ protected final SecureRandom rng;
+
+ /**
+ * Constructs a new CSRF token factory.
+ *
+ * @param timeToLive
+ * Time to live for tokens, in seconds
+ * @param tokenLength
+ * Length of the tokens generated
+ */
+ public CSRFTokenFactory(long timeToLive, int tokenLength) {
+ tokenCache = CacheBuilder.newBuilder()
+ .expireAfterWrite(timeToLive, TimeUnit.SECONDS)
+ .build(
+ new CacheLoader() {
+ @Override
+ public Instant load(String key) {
+ return Instant.now();
+ }
+
+ });
+ this.timeToLive = timeToLive;
+ this.rng = new SecureRandom();
+ this.tokenLength = tokenLength;
+ }
+
+ /**
+ * Generates a fresh CSRF token, which will remain valid for the configured amount of time.
+ */
+ public String getFreshToken() {
+ // Generate a random token
+ String token = RandomStringUtils.random(tokenLength, 0, 0, true, true, null, rng);
+ // Put it in the cache
+ try {
+ tokenCache.get(token);
+ } catch (ExecutionException e) {
+ // cannot happen
+ }
+ return token;
+ }
+
+ /**
+ * Checks that a given CSRF token is valid.
+ * @param token
+ * the token to verify
+ * @return
+ * true if the token is valid
+ */
+ public boolean validToken(String token) {
+ Map map = tokenCache.asMap();
+ Instant cutoff = Instant.now().minusSeconds(timeToLive);
+ return map.containsKey(token) && map.get(token).isAfter(cutoff);
+ }
+}
diff --git a/main/src/com/google/refine/commands/Command.java b/main/src/com/google/refine/commands/Command.java
index 5c605f7e5..7cfa2bda0 100644
--- a/main/src/com/google/refine/commands/Command.java
+++ b/main/src/com/google/refine/commands/Command.java
@@ -37,6 +37,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletException;
@@ -66,6 +68,8 @@ import com.google.refine.util.ParsingUtilities;
public abstract class Command {
final static protected Logger logger = LoggerFactory.getLogger("command");
+
+ final static public CSRFTokenFactory csrfFactory = new CSRFTokenFactory(3600, 32);
protected RefineServlet servlet;
@@ -215,6 +219,42 @@ public abstract class Command {
return def;
}
+ /**
+ * Utility method for retrieving the CSRF token stored in the "csrf_token" parameter of the request,
+ * and checking that it is valid.
+ *
+ * @param request
+ * @return
+ * @throws ServletException
+ */
+ protected boolean hasValidCSRFToken(HttpServletRequest request) throws ServletException {
+ if (request == null) {
+ throw new IllegalArgumentException("parameter 'request' should not be null");
+ }
+ try {
+ String token = request.getParameter("csrf_token");
+ return token != null && csrfFactory.validToken(token);
+ } catch (Exception e) {
+ // ignore
+ }
+ throw new ServletException("Can't find CSRF token: missing or bad URL parameter");
+ }
+
+
+ /**
+ * Checks the validity of a CSRF token, without reading the whole POST body.
+ * Useful when we need to control how the POST body is read (for instance if it
+ * contains files).
+ */
+ protected boolean hasValidCSRFTokenAsGET(HttpServletRequest request) {
+ if (request == null) {
+ throw new IllegalArgumentException("parameter 'request' should not be null");
+ }
+ Properties options = ParsingUtilities.parseUrlParameters(request);
+ String token = options.getProperty("csrf_token");
+ return token != null && csrfFactory.validToken(token);
+ }
+
protected static class HistoryEntryResponse {
@JsonProperty("code")
protected String getCode() { return "ok"; }
@@ -277,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());
@@ -297,6 +337,13 @@ public abstract class Command {
w.flush();
w.close();
}
+
+ static protected void respondCSRFError(HttpServletResponse response) throws IOException {
+ Map responseJSON = new HashMap<>();
+ responseJSON.put("code", "error");
+ responseJSON.put("message", "Missing or invalid csrf_token parameter");
+ respondJSON(response, responseJSON);
+ }
static protected void respondException(HttpServletResponse response, Exception e)
throws IOException, ServletException {
diff --git a/main/src/com/google/refine/commands/EngineDependentCommand.java b/main/src/com/google/refine/commands/EngineDependentCommand.java
index a13330ae5..bc091803d 100644
--- a/main/src/com/google/refine/commands/EngineDependentCommand.java
+++ b/main/src/com/google/refine/commands/EngineDependentCommand.java
@@ -70,6 +70,10 @@ abstract public class EngineDependentCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/GetAllPreferencesCommand.java b/main/src/com/google/refine/commands/GetAllPreferencesCommand.java
index cdf641e08..4cc2212a6 100644
--- a/main/src/com/google/refine/commands/GetAllPreferencesCommand.java
+++ b/main/src/com/google/refine/commands/GetAllPreferencesCommand.java
@@ -46,6 +46,10 @@ import com.google.refine.model.Project;
import com.google.refine.preference.PreferenceStore;
public class GetAllPreferencesCommand extends Command {
+ /**
+ * The command uses POST (not sure why?) but does not actually modify any state
+ * so it does not require CSRF.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
diff --git a/main/src/com/google/refine/commands/GetCSRFTokenCommand.java b/main/src/com/google/refine/commands/GetCSRFTokenCommand.java
new file mode 100644
index 000000000..f5f668c35
--- /dev/null
+++ b/main/src/com/google/refine/commands/GetCSRFTokenCommand.java
@@ -0,0 +1,18 @@
+package com.google.refine.commands;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Generates a fresh CSRF token.
+ */
+public class GetCSRFTokenCommand extends Command {
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ respondJSON(response, Collections.singletonMap("token", csrfFactory.getFreshToken()));
+ }
+}
diff --git a/main/src/com/google/refine/commands/OpenWorkspaceDirCommand.java b/main/src/com/google/refine/commands/OpenWorkspaceDirCommand.java
index 49296707a..b09ffb0fe 100644
--- a/main/src/com/google/refine/commands/OpenWorkspaceDirCommand.java
+++ b/main/src/com/google/refine/commands/OpenWorkspaceDirCommand.java
@@ -48,6 +48,10 @@ public class OpenWorkspaceDirCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
String serverName = request.getServerName();
diff --git a/main/src/com/google/refine/commands/SetPreferenceCommand.java b/main/src/com/google/refine/commands/SetPreferenceCommand.java
index 43857e11f..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;
@@ -49,6 +50,10 @@ public class SetPreferenceCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
Project project = request.getParameter("project") != null ? getProject(request) : null;
PreferenceStore ps = project != null ?
@@ -63,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/browsing/ComputeClustersCommand.java b/main/src/com/google/refine/commands/browsing/ComputeClustersCommand.java
index bc86ae763..c499015f7 100644
--- a/main/src/com/google/refine/commands/browsing/ComputeClustersCommand.java
+++ b/main/src/com/google/refine/commands/browsing/ComputeClustersCommand.java
@@ -52,7 +52,11 @@ import com.google.refine.util.ParsingUtilities;
public class ComputeClustersCommand extends Command {
final static Logger logger = LoggerFactory.getLogger("compute-clusters_command");
-
+
+ /**
+ * This command uses POST (probably to allow for larger parameters) but does not actually modify any state
+ * so we do not add CSRF protection to it.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
diff --git a/main/src/com/google/refine/commands/browsing/ComputeFacetsCommand.java b/main/src/com/google/refine/commands/browsing/ComputeFacetsCommand.java
index b0d91aef1..b27fbd1bc 100644
--- a/main/src/com/google/refine/commands/browsing/ComputeFacetsCommand.java
+++ b/main/src/com/google/refine/commands/browsing/ComputeFacetsCommand.java
@@ -44,6 +44,11 @@ import com.google.refine.commands.Command;
import com.google.refine.model.Project;
public class ComputeFacetsCommand extends Command {
+
+ /**
+ * This command uses POST (probably to allow for larger parameters) but does not actually modify any state
+ * so we do not add CSRF protection to it.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
diff --git a/main/src/com/google/refine/commands/cell/EditOneCellCommand.java b/main/src/com/google/refine/commands/cell/EditOneCellCommand.java
index e299000ce..55cf39252 100644
--- a/main/src/com/google/refine/commands/cell/EditOneCellCommand.java
+++ b/main/src/com/google/refine/commands/cell/EditOneCellCommand.java
@@ -84,6 +84,10 @@ public class EditOneCellCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
request.setCharacterEncoding("UTF-8");
diff --git a/main/src/com/google/refine/commands/cell/JoinMultiValueCellsCommand.java b/main/src/com/google/refine/commands/cell/JoinMultiValueCellsCommand.java
index 29d043d47..f65c1c0ee 100644
--- a/main/src/com/google/refine/commands/cell/JoinMultiValueCellsCommand.java
+++ b/main/src/com/google/refine/commands/cell/JoinMultiValueCellsCommand.java
@@ -50,6 +50,10 @@ public class JoinMultiValueCellsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/cell/KeyValueColumnizeCommand.java b/main/src/com/google/refine/commands/cell/KeyValueColumnizeCommand.java
index f941e5e8a..b926dfc4d 100644
--- a/main/src/com/google/refine/commands/cell/KeyValueColumnizeCommand.java
+++ b/main/src/com/google/refine/commands/cell/KeyValueColumnizeCommand.java
@@ -50,6 +50,10 @@ public class KeyValueColumnizeCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/cell/SplitMultiValueCellsCommand.java b/main/src/com/google/refine/commands/cell/SplitMultiValueCellsCommand.java
index 071614259..0918b04ba 100644
--- a/main/src/com/google/refine/commands/cell/SplitMultiValueCellsCommand.java
+++ b/main/src/com/google/refine/commands/cell/SplitMultiValueCellsCommand.java
@@ -52,6 +52,10 @@ public class SplitMultiValueCellsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommand.java b/main/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommand.java
index db26bd954..24c16b95f 100644
--- a/main/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommand.java
+++ b/main/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommand.java
@@ -50,6 +50,10 @@ public class TransposeColumnsIntoRowsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommand.java b/main/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommand.java
index 7803d4860..8ace53158 100644
--- a/main/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommand.java
+++ b/main/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommand.java
@@ -50,6 +50,10 @@ public class TransposeRowsIntoColumnsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/column/MoveColumnCommand.java b/main/src/com/google/refine/commands/column/MoveColumnCommand.java
index d88791763..75a5463ca 100644
--- a/main/src/com/google/refine/commands/column/MoveColumnCommand.java
+++ b/main/src/com/google/refine/commands/column/MoveColumnCommand.java
@@ -50,6 +50,10 @@ public class MoveColumnCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/column/RemoveColumnCommand.java b/main/src/com/google/refine/commands/column/RemoveColumnCommand.java
index 2a8e0915e..362691748 100644
--- a/main/src/com/google/refine/commands/column/RemoveColumnCommand.java
+++ b/main/src/com/google/refine/commands/column/RemoveColumnCommand.java
@@ -50,6 +50,10 @@ public class RemoveColumnCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/column/RenameColumnCommand.java b/main/src/com/google/refine/commands/column/RenameColumnCommand.java
index 5ff00f811..725887c8b 100644
--- a/main/src/com/google/refine/commands/column/RenameColumnCommand.java
+++ b/main/src/com/google/refine/commands/column/RenameColumnCommand.java
@@ -50,6 +50,10 @@ public class RenameColumnCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/expr/LogExpressionCommand.java b/main/src/com/google/refine/commands/expr/LogExpressionCommand.java
index 818da044a..71df8ca24 100644
--- a/main/src/com/google/refine/commands/expr/LogExpressionCommand.java
+++ b/main/src/com/google/refine/commands/expr/LogExpressionCommand.java
@@ -48,7 +48,11 @@ public class LogExpressionCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
-
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
+
try {
String expression = request.getParameter("expression");
diff --git a/main/src/com/google/refine/commands/expr/PreviewExpressionCommand.java b/main/src/com/google/refine/commands/expr/PreviewExpressionCommand.java
index 1ba11ac7e..a31b8c94c 100644
--- a/main/src/com/google/refine/commands/expr/PreviewExpressionCommand.java
+++ b/main/src/com/google/refine/commands/expr/PreviewExpressionCommand.java
@@ -111,6 +111,10 @@ public class PreviewExpressionCommand extends Command {
this.results = evaluated;
}
}
+ /**
+ * The command uses POST but does not actually modify any state so it does
+ * not require CSRF.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
diff --git a/main/src/com/google/refine/commands/expr/ToggleStarredExpressionCommand.java b/main/src/com/google/refine/commands/expr/ToggleStarredExpressionCommand.java
index 7a06011ed..fce0c7418 100644
--- a/main/src/com/google/refine/commands/expr/ToggleStarredExpressionCommand.java
+++ b/main/src/com/google/refine/commands/expr/ToggleStarredExpressionCommand.java
@@ -40,6 +40,11 @@ public class ToggleStarredExpressionCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
+
String expression = request.getParameter("expression");
TopList starredExpressions = ((TopList) ProjectManager.singleton.getPreferenceStore().get(
diff --git a/main/src/com/google/refine/commands/history/ApplyOperationsCommand.java b/main/src/com/google/refine/commands/history/ApplyOperationsCommand.java
index df93d964f..c57ac44c3 100644
--- a/main/src/com/google/refine/commands/history/ApplyOperationsCommand.java
+++ b/main/src/com/google/refine/commands/history/ApplyOperationsCommand.java
@@ -54,6 +54,10 @@ public class ApplyOperationsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
Project project = getProject(request);
String jsonString = request.getParameter("operations");
diff --git a/main/src/com/google/refine/commands/history/CancelProcessesCommand.java b/main/src/com/google/refine/commands/history/CancelProcessesCommand.java
index 8f1192bf6..d416efbde 100644
--- a/main/src/com/google/refine/commands/history/CancelProcessesCommand.java
+++ b/main/src/com/google/refine/commands/history/CancelProcessesCommand.java
@@ -53,6 +53,10 @@ public class CancelProcessesCommand extends Command {
if( response == null ) {
throw new IllegalArgumentException("parameter 'request' should not be null");
}
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/history/UndoRedoCommand.java b/main/src/com/google/refine/commands/history/UndoRedoCommand.java
index aa7d4aa62..0bf9de91e 100644
--- a/main/src/com/google/refine/commands/history/UndoRedoCommand.java
+++ b/main/src/com/google/refine/commands/history/UndoRedoCommand.java
@@ -48,6 +48,10 @@ public class UndoRedoCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/importing/CancelImportingJobCommand.java b/main/src/com/google/refine/commands/importing/CancelImportingJobCommand.java
index c501ddcd3..7ec5cdd7e 100644
--- a/main/src/com/google/refine/commands/importing/CancelImportingJobCommand.java
+++ b/main/src/com/google/refine/commands/importing/CancelImportingJobCommand.java
@@ -48,6 +48,10 @@ public class CancelImportingJobCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
long jobID = Long.parseLong(request.getParameter("jobID"));
ImportingJob job = ImportingManager.getJob(jobID);
diff --git a/main/src/com/google/refine/commands/importing/CreateImportingJobCommand.java b/main/src/com/google/refine/commands/importing/CreateImportingJobCommand.java
index 27ed84979..3f8cea114 100644
--- a/main/src/com/google/refine/commands/importing/CreateImportingJobCommand.java
+++ b/main/src/com/google/refine/commands/importing/CreateImportingJobCommand.java
@@ -52,6 +52,10 @@ public class CreateImportingJobCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
long id = ImportingManager.createJob().id;
diff --git a/main/src/com/google/refine/commands/importing/GetImportingConfigurationCommand.java b/main/src/com/google/refine/commands/importing/GetImportingConfigurationCommand.java
index 2899114d6..b31204a39 100644
--- a/main/src/com/google/refine/commands/importing/GetImportingConfigurationCommand.java
+++ b/main/src/com/google/refine/commands/importing/GetImportingConfigurationCommand.java
@@ -49,6 +49,10 @@ public class GetImportingConfigurationCommand extends Command {
@JsonProperty("config")
ImportingConfiguration config = new ImportingConfiguration();
}
+ /**
+ * This command uses POST but does not actually modify any state so
+ * it is not CSRF-protected.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
diff --git a/main/src/com/google/refine/commands/importing/GetImportingJobStatusCommand.java b/main/src/com/google/refine/commands/importing/GetImportingJobStatusCommand.java
index 930b22032..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 {
@@ -66,6 +64,10 @@ public class GetImportingJobStatusCommand extends Command {
}
}
+ /**
+ * This command uses POST but does not actually modify any state so
+ * it is not CSRF-protected.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
@@ -73,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 02a279db9..1acb732b0 100644
--- a/main/src/com/google/refine/commands/importing/ImportingControllerCommand.java
+++ b/main/src/com/google/refine/commands/importing/ImportingControllerCommand.java
@@ -56,9 +56,15 @@ public class ImportingControllerCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFTokenAsGET(request)) {
+ respondCSRFError(response);
+ return;
+ }
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");
@@ -71,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/commands/lang/GetLanguagesCommand.java b/main/src/com/google/refine/commands/lang/GetLanguagesCommand.java
index 7af237b3d..a07bf1e98 100644
--- a/main/src/com/google/refine/commands/lang/GetLanguagesCommand.java
+++ b/main/src/com/google/refine/commands/lang/GetLanguagesCommand.java
@@ -93,12 +93,6 @@ public class GetLanguagesCommand extends Command {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
- doPost(request, response);
- }
-
- @Override
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
String modname = request.getParameter("module");
if (modname == null) {
diff --git a/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java b/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java
index 15336ec1d..892dab86d 100644
--- a/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java
+++ b/main/src/com/google/refine/commands/lang/LoadLanguageCommand.java
@@ -62,11 +62,16 @@ public class LoadLanguageCommand extends Command {
throws ServletException, IOException {
doPost(request, response);
}
+
+ /**
+ * POST is supported but does not actually change any state so we do
+ * not add CSRF protection to it. This ensures existing extensions will not
+ * have to be updated to add a CSRF token to their requests (2019-11-10)
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
-
String modname = request.getParameter("module");
if (modname == null) {
modname = "core";
diff --git a/main/src/com/google/refine/commands/project/CreateProjectCommand.java b/main/src/com/google/refine/commands/project/CreateProjectCommand.java
index f5f99dc24..8a0f133d5 100644
--- a/main/src/com/google/refine/commands/project/CreateProjectCommand.java
+++ b/main/src/com/google/refine/commands/project/CreateProjectCommand.java
@@ -64,6 +64,10 @@ public class CreateProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFTokenAsGET(request)) {
+ respondCSRFError(response);
+ return;
+ }
ProjectManager.singleton.setBusy(true);
try {
diff --git a/main/src/com/google/refine/commands/project/DeleteProjectCommand.java b/main/src/com/google/refine/commands/project/DeleteProjectCommand.java
index 9d146a01e..b04037198 100644
--- a/main/src/com/google/refine/commands/project/DeleteProjectCommand.java
+++ b/main/src/com/google/refine/commands/project/DeleteProjectCommand.java
@@ -49,6 +49,11 @@ public class DeleteProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
+
response.setHeader("Content-Type", "application/json");
try {
long projectID = Long.parseLong(request.getParameter("project"));
diff --git a/main/src/com/google/refine/commands/project/ExportProjectCommand.java b/main/src/com/google/refine/commands/project/ExportProjectCommand.java
index 3252f6a2a..ee129f7c9 100644
--- a/main/src/com/google/refine/commands/project/ExportProjectCommand.java
+++ b/main/src/com/google/refine/commands/project/ExportProjectCommand.java
@@ -46,6 +46,10 @@ import com.google.refine.io.FileProjectManager;
import com.google.refine.model.Project;
public class ExportProjectCommand extends Command {
+
+ /**
+ * This command uses POST but is left CSRF-unprotected as it does not incur a state change.
+ */
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
diff --git a/main/src/com/google/refine/commands/project/ExportRowsCommand.java b/main/src/com/google/refine/commands/project/ExportRowsCommand.java
index 44c031da4..dd6444a1f 100644
--- a/main/src/com/google/refine/commands/project/ExportRowsCommand.java
+++ b/main/src/com/google/refine/commands/project/ExportRowsCommand.java
@@ -61,6 +61,10 @@ import com.google.refine.model.Project;
public class ExportRowsCommand extends Command {
private static final Logger logger = LoggerFactory.getLogger("ExportRowsCommand");
+
+ /**
+ * This command uses POST but is left CSRF-unprotected as it does not incur a state change.
+ */
@SuppressWarnings("unchecked")
static public Properties getRequestParameters(HttpServletRequest request) {
diff --git a/main/src/com/google/refine/commands/project/GetModelsCommand.java b/main/src/com/google/refine/commands/project/GetModelsCommand.java
index ebf707d7b..b980154dc 100644
--- a/main/src/com/google/refine/commands/project/GetModelsCommand.java
+++ b/main/src/com/google/refine/commands/project/GetModelsCommand.java
@@ -55,6 +55,11 @@ import com.google.refine.model.Project;
import com.google.refine.model.RecordModel;
public class GetModelsCommand extends Command {
+
+ /**
+ * This command uses POST but is left CSRF-unprotected as it does not incur a state change.
+ */
+
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
diff --git a/main/src/com/google/refine/commands/project/ImportProjectCommand.java b/main/src/com/google/refine/commands/project/ImportProjectCommand.java
index 740b72c9b..aac3ba489 100644
--- a/main/src/com/google/refine/commands/project/ImportProjectCommand.java
+++ b/main/src/com/google/refine/commands/project/ImportProjectCommand.java
@@ -63,6 +63,10 @@ public class ImportProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFTokenAsGET(request)) {
+ respondCSRFError(response);
+ return;
+ }
ProjectManager.singleton.setBusy(true);
try {
diff --git a/main/src/com/google/refine/commands/project/RenameProjectCommand.java b/main/src/com/google/refine/commands/project/RenameProjectCommand.java
index b8023df4a..b3365edf3 100644
--- a/main/src/com/google/refine/commands/project/RenameProjectCommand.java
+++ b/main/src/com/google/refine/commands/project/RenameProjectCommand.java
@@ -46,7 +46,11 @@ public class RenameProjectCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
-
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
+
try {
String name = request.getParameter("name");
ProjectMetadata pm = getProjectMetadata(request);
diff --git a/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java b/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java
index c121c6b10..55297c3f3 100644
--- a/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java
+++ b/main/src/com/google/refine/commands/project/SetProjectMetadataCommand.java
@@ -41,6 +41,10 @@ public class SetProjectMetadataCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
Project project = request.getParameter("project") != null ? getProject(request) : null;
String metaName = request.getParameter("name");
diff --git a/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java b/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java
index 71fbba7f1..b6a928dba 100644
--- a/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java
+++ b/main/src/com/google/refine/commands/project/SetProjectTagsCommand.java
@@ -43,6 +43,11 @@ public class SetProjectTagsCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
+
response.setHeader("Content-Type", "application/json");
Project project;
diff --git a/main/src/com/google/refine/commands/recon/GuessTypesOfColumnCommand.java b/main/src/com/google/refine/commands/recon/GuessTypesOfColumnCommand.java
index 7e747706f..0d5f38951 100644
--- a/main/src/com/google/refine/commands/recon/GuessTypesOfColumnCommand.java
+++ b/main/src/com/google/refine/commands/recon/GuessTypesOfColumnCommand.java
@@ -93,6 +93,10 @@ public class GuessTypesOfColumnCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/recon/PreviewExtendDataCommand.java b/main/src/com/google/refine/commands/recon/PreviewExtendDataCommand.java
index 57bdffab8..6775f1c28 100644
--- a/main/src/com/google/refine/commands/recon/PreviewExtendDataCommand.java
+++ b/main/src/com/google/refine/commands/recon/PreviewExtendDataCommand.java
@@ -79,6 +79,10 @@ public class PreviewExtendDataCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/recon/ReconClearOneCellCommand.java b/main/src/com/google/refine/commands/recon/ReconClearOneCellCommand.java
index 6234f872e..30dc67ec6 100644
--- a/main/src/com/google/refine/commands/recon/ReconClearOneCellCommand.java
+++ b/main/src/com/google/refine/commands/recon/ReconClearOneCellCommand.java
@@ -75,6 +75,10 @@ public class ReconClearOneCellCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/recon/ReconJudgeOneCellCommand.java b/main/src/com/google/refine/commands/recon/ReconJudgeOneCellCommand.java
index 778ec2cd4..f37dc1838 100644
--- a/main/src/com/google/refine/commands/recon/ReconJudgeOneCellCommand.java
+++ b/main/src/com/google/refine/commands/recon/ReconJudgeOneCellCommand.java
@@ -59,6 +59,10 @@ public class ReconJudgeOneCellCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
request.setCharacterEncoding("UTF-8");
diff --git a/main/src/com/google/refine/commands/row/AnnotateOneRowCommand.java b/main/src/com/google/refine/commands/row/AnnotateOneRowCommand.java
index 6555f8b47..1d27cb819 100644
--- a/main/src/com/google/refine/commands/row/AnnotateOneRowCommand.java
+++ b/main/src/com/google/refine/commands/row/AnnotateOneRowCommand.java
@@ -50,6 +50,10 @@ public class AnnotateOneRowCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Type", "application/json");
diff --git a/main/src/com/google/refine/commands/row/DenormalizeCommand.java b/main/src/com/google/refine/commands/row/DenormalizeCommand.java
index 6ba38d6a1..0a13177d1 100644
--- a/main/src/com/google/refine/commands/row/DenormalizeCommand.java
+++ b/main/src/com/google/refine/commands/row/DenormalizeCommand.java
@@ -50,6 +50,10 @@ public class DenormalizeCommand extends Command {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
+ if(!hasValidCSRFToken(request)) {
+ respondCSRFError(response);
+ return;
+ }
try {
Project project = getProject(request);
diff --git a/main/src/com/google/refine/commands/row/GetRowsCommand.java b/main/src/com/google/refine/commands/row/GetRowsCommand.java
index cfeb21cdc..5467b4246 100644
--- a/main/src/com/google/refine/commands/row/GetRowsCommand.java
+++ b/main/src/com/google/refine/commands/row/GetRowsCommand.java
@@ -111,6 +111,10 @@ public class GetRowsCommand extends Command {
}
}
+ /**
+ * This command accepts both POST and GET. It is not CSRF-protected as it does not incur any state change.
+ */
+
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
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/tests/server/src/com/google/refine/commands/CSRFTokenFactoryTests.java b/main/tests/server/src/com/google/refine/commands/CSRFTokenFactoryTests.java
new file mode 100644
index 000000000..f966e971b
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/CSRFTokenFactoryTests.java
@@ -0,0 +1,49 @@
+package com.google.refine.commands;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.time.Instant;
+
+import org.testng.annotations.Test;
+
+public class CSRFTokenFactoryTests {
+
+ static class CSRFTokenFactoryStub extends CSRFTokenFactory{
+ public CSRFTokenFactoryStub(long timeToLive, int tokenLength) {
+ super(timeToLive, tokenLength);
+ }
+ public void tamperWithToken(String token, Instant newGenerationTime) {
+ tokenCache.asMap().put(token, newGenerationTime);
+ }
+ }
+
+ @Test
+ public void testGenerateValidToken() {
+ CSRFTokenFactory factory = new CSRFTokenFactory(10, 25);
+ // Generate a fresh token
+ String token = factory.getFreshToken();
+ // Immediately after, the token is still valid
+ assertTrue(factory.validToken(token));
+ // The token has the right length
+ assertEquals(25, token.length());
+ }
+
+ @Test
+ public void testInvalidToken() {
+ CSRFTokenFactory factory = new CSRFTokenFactory(10, 25);
+ assertFalse(factory.validToken("bogusToken"));
+ }
+
+ @Test
+ public void testOldToken() {
+ CSRFTokenFactoryStub stub = new CSRFTokenFactoryStub(10, 25);
+ // Generate a fresh token
+ String token = stub.getFreshToken();
+ // Manually change the generation time
+ stub.tamperWithToken(token, Instant.now().minusSeconds(100));
+ // The token should now be invalid
+ assertFalse(stub.validToken(token));
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/CommandTestBase.java b/main/tests/server/src/com/google/refine/commands/CommandTestBase.java
new file mode 100644
index 000000000..7e7c84915
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/CommandTestBase.java
@@ -0,0 +1,41 @@
+package com.google.refine.commands;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.testng.annotations.BeforeMethod;
+
+import com.google.refine.util.TestUtils;
+
+public class CommandTestBase {
+ protected HttpServletRequest request = null;
+ protected HttpServletResponse response = null;
+ protected Command command = null;
+ protected StringWriter writer = null;
+
+ @BeforeMethod
+ public void setUpRequestResponse() {
+ request = mock(HttpServletRequest.class);
+ response = mock(HttpServletResponse.class);
+ writer = new StringWriter();
+ try {
+ when(response.getWriter()).thenReturn(new PrintWriter(writer));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Convenience method to check that CSRF protection was triggered
+ */
+ protected void assertCSRFCheckFailed() {
+ TestUtils.assertEqualAsJson("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", writer.toString());
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/EngineDependentCommandTests.java b/main/tests/server/src/com/google/refine/commands/EngineDependentCommandTests.java
new file mode 100644
index 000000000..923613aa3
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/EngineDependentCommandTests.java
@@ -0,0 +1,38 @@
+package com.google.refine.commands;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.browsing.EngineConfig;
+import com.google.refine.model.AbstractOperation;
+import com.google.refine.model.Project;
+
+public class EngineDependentCommandTests extends CommandTestBase {
+
+ private static class EngineDependentCommandStub extends EngineDependentCommand {
+
+ @Override
+ protected AbstractOperation createOperation(Project project, HttpServletRequest request,
+ EngineConfig engineConfig) throws Exception {
+ return null;
+ }
+
+ }
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new EngineDependentCommandStub();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/GetCSRFTokenCommandTest.java b/main/tests/server/src/com/google/refine/commands/GetCSRFTokenCommandTest.java
new file mode 100644
index 000000000..6133a2e91
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/GetCSRFTokenCommandTest.java
@@ -0,0 +1,49 @@
+package com.google.refine.commands;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.refine.util.ParsingUtilities;
+
+public class GetCSRFTokenCommandTest {
+ protected HttpServletRequest request = null;
+ protected HttpServletResponse response = null;
+ protected StringWriter writer = null;
+ protected Command command = null;
+
+ @BeforeMethod
+ public void setUp() {
+ request = mock(HttpServletRequest.class);
+ response = mock(HttpServletResponse.class);
+ command = new GetCSRFTokenCommand();
+ writer = new StringWriter();
+ try {
+ when(response.getWriter()).thenReturn(new PrintWriter(writer));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testGetToken() throws JsonParseException, JsonMappingException, IOException, ServletException {
+ command.doGet(request, response);
+ ObjectNode result = ParsingUtilities.mapper.readValue(writer.toString(), ObjectNode.class);
+ String token = result.get("token").asText();
+ assertTrue(Command.csrfFactory.validToken(token));
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/OpenWorkspaceDirCommandTests.java b/main/tests/server/src/com/google/refine/commands/OpenWorkspaceDirCommandTests.java
new file mode 100644
index 000000000..770b86a66
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/OpenWorkspaceDirCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands;
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class OpenWorkspaceDirCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new OpenWorkspaceDirCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/SetPreferenceCommandTests.java b/main/tests/server/src/com/google/refine/commands/SetPreferenceCommandTests.java
new file mode 100644
index 000000000..9ad95db7c
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/SetPreferenceCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class SetPreferenceCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new SetPreferenceCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/cell/EditOneCellCommandTests.java b/main/tests/server/src/com/google/refine/commands/cell/EditOneCellCommandTests.java
new file mode 100644
index 000000000..d9fcc4c07
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/cell/EditOneCellCommandTests.java
@@ -0,0 +1,78 @@
+package com.google.refine.commands.cell;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.RefineTest;
+import com.google.refine.commands.Command;
+import com.google.refine.model.Project;
+import com.google.refine.util.TestUtils;
+
+public class EditOneCellCommandTests extends RefineTest {
+
+ protected Project project = null;
+ protected HttpServletRequest request = null;
+ protected HttpServletResponse response = null;
+ protected Command command = null;
+ protected StringWriter writer = null;
+
+ @BeforeMethod
+ public void setUpProject() {
+ project = createCSVProject(
+ "first_column,second_column\n"
+ + "a,b\n"
+ + "c,d\n");
+ command = new EditOneCellCommand();
+ request = mock(HttpServletRequest.class);
+ response = mock(HttpServletResponse.class);
+ writer = new StringWriter();
+ try {
+ when(response.getWriter()).thenReturn(new PrintWriter(writer));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testEditOneCell() throws ServletException, IOException {
+ when(request.getParameter("project")).thenReturn(Long.toString(project.id));
+ when(request.getParameter("row")).thenReturn("1");
+ when(request.getParameter("cell")).thenReturn("0");
+ when(request.getParameter("type")).thenReturn("string");
+ when(request.getParameter("value")).thenReturn("e");
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
+
+ command.doPost(request, response);
+
+ assertEquals("a", project.rows.get(0).cells.get(0).value);
+ assertEquals("b", project.rows.get(0).cells.get(1).value);
+ assertEquals("e", project.rows.get(1).cells.get(0).value);
+ assertEquals("d", project.rows.get(1).cells.get(1).value);
+ }
+
+ @Test
+ public void testMissingCSRFToken() throws ServletException, IOException {
+ when(request.getParameter("project")).thenReturn(Long.toString(project.id));
+ when(request.getParameter("row")).thenReturn("1");
+ when(request.getParameter("cell")).thenReturn("0");
+ when(request.getParameter("type")).thenReturn("string");
+ when(request.getParameter("value")).thenReturn("e");
+
+ command.doPost(request, response);
+
+ assertEquals("c", project.rows.get(1).cells.get(0).value);
+ TestUtils.assertEqualAsJson("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", writer.toString());
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/cell/JoinMultiValueCellsCommandTests.java b/main/tests/server/src/com/google/refine/commands/cell/JoinMultiValueCellsCommandTests.java
new file mode 100644
index 000000000..e9678d367
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/cell/JoinMultiValueCellsCommandTests.java
@@ -0,0 +1,25 @@
+package com.google.refine.commands.cell;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.commands.CommandTestBase;
+import com.google.refine.commands.cell.JoinMultiValueCellsCommand;
+
+public class JoinMultiValueCellsCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new JoinMultiValueCellsCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/cell/KeyValueColumnizeCommandTests.java b/main/tests/server/src/com/google/refine/commands/cell/KeyValueColumnizeCommandTests.java
new file mode 100644
index 000000000..efb533700
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/cell/KeyValueColumnizeCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands.cell;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.commands.CommandTestBase;
+
+public class KeyValueColumnizeCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new KeyValueColumnizeCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/cell/SplitMultiValueCellsCommandTests.java b/main/tests/server/src/com/google/refine/commands/cell/SplitMultiValueCellsCommandTests.java
new file mode 100644
index 000000000..ed3adbc54
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/cell/SplitMultiValueCellsCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.cell;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.commands.CommandTestBase;
+
+public class SplitMultiValueCellsCommandTests extends CommandTestBase {
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new SplitMultiValueCellsCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommandTests.java b/main/tests/server/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommandTests.java
new file mode 100644
index 000000000..c6562c624
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/cell/TransposeColumnsIntoRowsCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.cell;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.commands.CommandTestBase;
+
+public class TransposeColumnsIntoRowsCommandTests extends CommandTestBase {
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new TransposeColumnsIntoRowsCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommandTests.java b/main/tests/server/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommandTests.java
new file mode 100644
index 000000000..105b1be31
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/cell/TransposeRowsIntoColumnsCommandTests.java
@@ -0,0 +1,22 @@
+package com.google.refine.commands.cell;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.refine.commands.CommandTestBase;
+
+public class TransposeRowsIntoColumnsCommandTests extends CommandTestBase {
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new TransposeRowsIntoColumnsCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/column/MoveColumnCommandTests.java b/main/tests/server/src/com/google/refine/commands/column/MoveColumnCommandTests.java
new file mode 100644
index 000000000..da7f25b3b
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/column/MoveColumnCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands.column;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class MoveColumnCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new MoveColumnCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/column/RemoveColumnCommandTests.java b/main/tests/server/src/com/google/refine/commands/column/RemoveColumnCommandTests.java
new file mode 100644
index 000000000..d666f0c3c
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/column/RemoveColumnCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.column;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class RemoveColumnCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new RemoveColumnCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/column/RenameColumnCommandTests.java b/main/tests/server/src/com/google/refine/commands/column/RenameColumnCommandTests.java
new file mode 100644
index 000000000..a0d8d94bc
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/column/RenameColumnCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.column;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class RenameColumnCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new RenameColumnCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/expr/LogExpressionCommandTests.java b/main/tests/server/src/com/google/refine/commands/expr/LogExpressionCommandTests.java
new file mode 100644
index 000000000..1283820ef
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/expr/LogExpressionCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.expr;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class LogExpressionCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new LogExpressionCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/expr/ToggleStarredExpressionCommandTests.java b/main/tests/server/src/com/google/refine/commands/expr/ToggleStarredExpressionCommandTests.java
index 2b8376db4..d4166cd62 100644
--- a/main/tests/server/src/com/google/refine/commands/expr/ToggleStarredExpressionCommandTests.java
+++ b/main/tests/server/src/com/google/refine/commands/expr/ToggleStarredExpressionCommandTests.java
@@ -35,7 +35,9 @@ import javax.servlet.ServletException;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.refine.commands.Command;
import com.google.refine.commands.expr.ToggleStarredExpressionCommand;
+import com.google.refine.util.TestUtils;
public class ToggleStarredExpressionCommandTests extends ExpressionCommandTestBase {
@@ -70,7 +72,14 @@ public class ToggleStarredExpressionCommandTests extends ExpressionCommandTestBa
" }";
when(request.getParameter("expression")).thenReturn("grel:facetCount(value, 'value', 'Column 1')");
when(request.getParameter("returnList")).thenReturn("yes");
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
command.doPost(request, response);
assertResponseJsonIs(json);
}
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ TestUtils.assertEqualAsJson("{\"code\":\"error\",\"message\":\"Missing or invalid csrf_token parameter\"}", writer.toString());
+ }
}
diff --git a/main/tests/server/src/com/google/refine/commands/history/ApplyOperationsCommandTests.java b/main/tests/server/src/com/google/refine/commands/history/ApplyOperationsCommandTests.java
new file mode 100644
index 000000000..d7422eb18
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/history/ApplyOperationsCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands.history;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ApplyOperationsCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new ApplyOperationsCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/history/CancelProcessesCommandTests.java b/main/tests/server/src/com/google/refine/commands/history/CancelProcessesCommandTests.java
new file mode 100644
index 000000000..dcf3ec07e
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/history/CancelProcessesCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.history;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CancelProcessesCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new CancelProcessesCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/history/UndoRedoCommandTests.java b/main/tests/server/src/com/google/refine/commands/history/UndoRedoCommandTests.java
new file mode 100644
index 000000000..dc4ed459e
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/history/UndoRedoCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.history;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class UndoRedoCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new UndoRedoCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/importing/CancelImportingJobCommandTests.java b/main/tests/server/src/com/google/refine/commands/importing/CancelImportingJobCommandTests.java
new file mode 100644
index 000000000..b7c69e977
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/importing/CancelImportingJobCommandTests.java
@@ -0,0 +1,22 @@
+package com.google.refine.commands.importing;
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CancelImportingJobCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new CancelImportingJobCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/importing/CreateImportingJobCommandTests.java b/main/tests/server/src/com/google/refine/commands/importing/CreateImportingJobCommandTests.java
new file mode 100644
index 000000000..70f44ff52
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/importing/CreateImportingJobCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.importing;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CreateImportingJobCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new CreateImportingJobCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/importing/ImportingControllerCommandTests.java b/main/tests/server/src/com/google/refine/commands/importing/ImportingControllerCommandTests.java
new file mode 100644
index 000000000..7f3a29da6
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/importing/ImportingControllerCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands.importing;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ImportingControllerCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new ImportingControllerCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java b/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java
new file mode 100644
index 000000000..f239673df
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/lang/LoadLanguageCommandTests.java
@@ -0,0 +1,48 @@
+package com.google.refine.commands.lang;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertTrue;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.refine.RefineServlet;
+import com.google.refine.commands.CommandTestBase;
+import com.google.refine.util.ParsingUtilities;
+
+import edu.mit.simile.butterfly.ButterflyModule;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class LoadLanguageCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new LoadLanguageCommand();
+ ButterflyModule coreModule = mock(ButterflyModule.class);
+
+ when(coreModule.getName()).thenReturn("core");
+ when(coreModule.getPath()).thenReturn(new File("webapp/modules/core"));
+ RefineServlet servlet = mock(RefineServlet.class);
+ when(servlet.getModule("core")).thenReturn(coreModule);
+ command.init(servlet);
+ }
+
+ @Test
+ public void testLoadLanguages() throws ServletException, IOException {
+ when(request.getParameter("module")).thenReturn("core");
+ when(request.getParameterValues("lang")).thenReturn(new String[] {"en"});
+
+ command.doPost(request, response);
+
+ JsonNode response = ParsingUtilities.mapper.readValue(writer.toString(), JsonNode.class);
+ assertTrue(response.has("dictionary"));
+ assertTrue(response.has("lang"));
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/project/ImportProjectCommandTests.java b/main/tests/server/src/com/google/refine/commands/project/ImportProjectCommandTests.java
new file mode 100644
index 000000000..063f526e6
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/project/ImportProjectCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands.project;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ImportProjectCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new ImportProjectCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/project/RenameProjectCommandTests.java b/main/tests/server/src/com/google/refine/commands/project/RenameProjectCommandTests.java
new file mode 100644
index 000000000..4b60756e0
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/project/RenameProjectCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.project;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class RenameProjectCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new RenameProjectCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/project/SetProjectMetadataCommandTests.java b/main/tests/server/src/com/google/refine/commands/project/SetProjectMetadataCommandTests.java
index 3771bd05a..008dc08fb 100644
--- a/main/tests/server/src/com/google/refine/commands/project/SetProjectMetadataCommandTests.java
+++ b/main/tests/server/src/com/google/refine/commands/project/SetProjectMetadataCommandTests.java
@@ -58,6 +58,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.refine.ProjectManager;
import com.google.refine.ProjectMetadata;
import com.google.refine.RefineTest;
+import com.google.refine.commands.Command;
import com.google.refine.commands.project.SetProjectMetadataCommand;
import com.google.refine.model.Project;
import com.google.refine.util.ParsingUtilities;
@@ -101,6 +102,7 @@ public class SetProjectMetadataCommandTests extends RefineTest {
// mock dependencies
when(request.getParameter("project")).thenReturn(PROJECT_ID);
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
when(projMan.getProject(anyLong())).thenReturn(proj);
when(proj.getMetadata()).thenReturn(metadata);
diff --git a/main/tests/server/src/com/google/refine/commands/project/SetProjectTagsCommandTests.java b/main/tests/server/src/com/google/refine/commands/project/SetProjectTagsCommandTests.java
new file mode 100644
index 000000000..3b073fac4
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/project/SetProjectTagsCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.project;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class SetProjectTagsCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new SetProjectTagsCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/recon/GuessTypesOfColumnCommandTests.java b/main/tests/server/src/com/google/refine/commands/recon/GuessTypesOfColumnCommandTests.java
new file mode 100644
index 000000000..03010589b
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/recon/GuessTypesOfColumnCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.recon;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GuessTypesOfColumnCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new GuessTypesOfColumnCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/recon/PreviewExtendDataCommandTests.java b/main/tests/server/src/com/google/refine/commands/recon/PreviewExtendDataCommandTests.java
new file mode 100644
index 000000000..b4c04eddd
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/recon/PreviewExtendDataCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.recon;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class PreviewExtendDataCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new PreviewExtendDataCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/recon/ReconClearOneCellCommandTests.java b/main/tests/server/src/com/google/refine/commands/recon/ReconClearOneCellCommandTests.java
new file mode 100644
index 000000000..72ca77b73
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/recon/ReconClearOneCellCommandTests.java
@@ -0,0 +1,24 @@
+package com.google.refine.commands.recon;
+
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ReconClearOneCellCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new ReconClearOneCellCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/recon/ReconJudgeOneCellCommandTest.java b/main/tests/server/src/com/google/refine/commands/recon/ReconJudgeOneCellCommandTest.java
index 3dc741b6b..b53b11249 100644
--- a/main/tests/server/src/com/google/refine/commands/recon/ReconJudgeOneCellCommandTest.java
+++ b/main/tests/server/src/com/google/refine/commands/recon/ReconJudgeOneCellCommandTest.java
@@ -82,6 +82,7 @@ public class ReconJudgeOneCellCommandTest extends RefineTest {
response = mock(HttpServletResponse.class);
when(request.getParameter("project")).thenReturn(String.valueOf(project.id));
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
writer = mock(PrintWriter.class);
try {
diff --git a/main/tests/server/src/com/google/refine/commands/row/AnnotateOneRowCommandTests.java b/main/tests/server/src/com/google/refine/commands/row/AnnotateOneRowCommandTests.java
new file mode 100644
index 000000000..1b1e19e91
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/row/AnnotateOneRowCommandTests.java
@@ -0,0 +1,22 @@
+package com.google.refine.commands.row;
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class AnnotateOneRowCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new AnnotateOneRowCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
diff --git a/main/tests/server/src/com/google/refine/commands/row/DenormalizeCommandTests.java b/main/tests/server/src/com/google/refine/commands/row/DenormalizeCommandTests.java
new file mode 100644
index 000000000..885806bc5
--- /dev/null
+++ b/main/tests/server/src/com/google/refine/commands/row/DenormalizeCommandTests.java
@@ -0,0 +1,23 @@
+package com.google.refine.commands.row;
+import com.google.refine.commands.CommandTestBase;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class DenormalizeCommandTests extends CommandTestBase {
+
+ @BeforeMethod
+ public void setUpCommand() {
+ command = new DenormalizeCommand();
+ }
+
+ @Test
+ public void testCSRFProtection() throws ServletException, IOException {
+ command.doPost(request, response);
+ assertCSRFCheckFailed();
+ }
+}
+
diff --git a/main/tests/server/src/com/google/refine/commands/util/CancelProcessesCommandTests.java b/main/tests/server/src/com/google/refine/commands/util/CancelProcessesCommandTests.java
index 03cd8c70c..3be08e976 100644
--- a/main/tests/server/src/com/google/refine/commands/util/CancelProcessesCommandTests.java
+++ b/main/tests/server/src/com/google/refine/commands/util/CancelProcessesCommandTests.java
@@ -56,6 +56,7 @@ import org.testng.annotations.Test;
import com.google.refine.ProjectManager;
import com.google.refine.RefineTest;
+import com.google.refine.commands.Command;
import com.google.refine.commands.history.CancelProcessesCommand;
import com.google.refine.model.Project;
import com.google.refine.process.ProcessManager;
@@ -159,6 +160,7 @@ public class CancelProcessesCommandTests extends RefineTest {
// mock dependencies
when(request.getParameter("project")).thenReturn(PROJECT_ID);
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
when(projMan.getProject(anyLong())).thenReturn(proj);
when(proj.getProcessManager()).thenReturn(processMan);
try {
@@ -197,6 +199,7 @@ public class CancelProcessesCommandTests extends RefineTest {
public void doPostThrowsIfCommand_getProjectReturnsNull(){
// mock dependencies
when(request.getParameter("project")).thenReturn(PROJECT_ID);
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
when(projMan.getProject(anyLong()))
.thenReturn(null);
try {
@@ -225,6 +228,7 @@ public class CancelProcessesCommandTests extends RefineTest {
// mock dependencies
when(request.getParameter("project")).thenReturn(PROJECT_ID);
+ when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken());
when(projMan.getProject(anyLong())).thenReturn(proj);
when(proj.getProcessManager()).thenReturn(processMan);
try {
diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js
index cae639734..237c1ac3b 100644
--- a/main/webapp/modules/core/MOD-INF/controller.js
+++ b/main/webapp/modules/core/MOD-INF/controller.js
@@ -54,6 +54,7 @@ function registerCommands() {
var RS = Packages.com.google.refine.RefineServlet;
RS.registerCommand(module, "get-version", new Packages.com.google.refine.commands.GetVersionCommand());
+ RS.registerCommand(module, "get-csrf-token", new Packages.com.google.refine.commands.GetCSRFTokenCommand());
RS.registerCommand(module, "get-importing-configuration", new Packages.com.google.refine.commands.importing.GetImportingConfigurationCommand());
RS.registerCommand(module, "create-importing-job", new Packages.com.google.refine.commands.importing.CreateImportingJobCommand());
@@ -68,7 +69,7 @@ function registerCommands() {
RS.registerCommand(module, "get-project-metadata", new Packages.com.google.refine.commands.project.GetProjectMetadataCommand());
RS.registerCommand(module, "get-all-project-metadata", new Packages.com.google.refine.commands.workspace.GetAllProjectMetadataCommand());
- RS.registerCommand(module, "set-metaData", new Packages.com.google.refine.commands.project.SetProjectMetadataCommand());
+ RS.registerCommand(module, "set-project-metadata", new Packages.com.google.refine.commands.project.SetProjectMetadataCommand());
RS.registerCommand(module, "get-all-project-tags", new Packages.com.google.refine.commands.workspace.GetAllProjectTagsCommand());
RS.registerCommand(module, "set-project-tags", new Packages.com.google.refine.commands.project.SetProjectTagsCommand());
diff --git a/main/webapp/modules/core/scripts/dialogs/expression-preview-dialog.js b/main/webapp/modules/core/scripts/dialogs/expression-preview-dialog.js
index ae6b337fd..78f77f284 100644
--- a/main/webapp/modules/core/scripts/dialogs/expression-preview-dialog.js
+++ b/main/webapp/modules/core/scripts/dialogs/expression-preview-dialog.js
@@ -157,7 +157,7 @@ ExpressionPreviewDialog.Widget.prototype.getExpression = function(commit) {
s = this._getLanguage() + ":" + s;
if (commit) {
- $.post(
+ Refine.postCSRF(
"command/core/log-expression?" + $.param({ project: theProject.id }),
{ expression: s },
function(data) {
@@ -284,9 +284,11 @@ ExpressionPreviewDialog.Widget.prototype._renderExpressionHistory = function(dat
.addClass(entry.starred ? "data-table-star-on" : "data-table-star-off")
.appendTo(tr.insertCell(0))
.click(function() {
- $.post(
+ Refine.postCSRF(
"command/core/toggle-starred-expression",
- { expression: entry.code },
+ {
+ expression: entry.code
+ },
function(data) {
entry.starred = !entry.starred;
renderEntry(self,tr,entry);
@@ -348,7 +350,7 @@ ExpressionPreviewDialog.Widget.prototype._renderStarredExpressions = function(da
var o = Scripting.parse(entry.code);
$(''+$.i18n('core-dialogs/remove')+'').appendTo(tr.insertCell(0)).click(function() {
- $.post(
+ Refine.postCSRF(
"command/core/toggle-starred-expression",
{ expression: entry.code, returnList: true },
function(data) {
diff --git a/main/webapp/modules/core/scripts/dialogs/extend-data-preview-dialog.js b/main/webapp/modules/core/scripts/dialogs/extend-data-preview-dialog.js
index d60d9440c..6acb515dc 100644
--- a/main/webapp/modules/core/scripts/dialogs/extend-data-preview-dialog.js
+++ b/main/webapp/modules/core/scripts/dialogs/extend-data-preview-dialog.js
@@ -185,7 +185,7 @@ ExtendReconciledDataPreviewDialog.prototype._update = function() {
this._elmts.previewContainer.empty();
} else {
// otherwise, refresh the preview
- $.post(
+ Refine.postCSRF(
"command/core/preview-extend-data?" + $.param(params),
{
rowIndices: JSON.stringify(this._rowIndices),
@@ -194,10 +194,10 @@ ExtendReconciledDataPreviewDialog.prototype._update = function() {
function(data) {
self._renderPreview(data);
},
- "json"
- ).fail(function(data) {
- alert($.i18n('core-views/internal-err'));
- });
+ "json",
+ function(data) {
+ alert($.i18n('core-views/internal-err'));
+ });
}
};
diff --git a/main/webapp/modules/core/scripts/facets/list-facet.js b/main/webapp/modules/core/scripts/facets/list-facet.js
index a02c57650..cdd7daccf 100644
--- a/main/webapp/modules/core/scripts/facets/list-facet.js
+++ b/main/webapp/modules/core/scripts/facets/list-facet.js
@@ -721,7 +721,7 @@ ListFacet.prototype._setChoiceCountLimit = function(choiceCount) {
if (!isNaN(n)) {
var self = this;
- $.post(
+ Refine.postCSRF(
"command/core/set-preference",
{
name : "ui.browsing.listFacet.limit",
diff --git a/main/webapp/modules/core/scripts/index.js b/main/webapp/modules/core/scripts/index.js
index 1a22bfba2..731b59857 100644
--- a/main/webapp/modules/core/scripts/index.js
+++ b/main/webapp/modules/core/scripts/index.js
@@ -37,6 +37,37 @@ var Refine = {
actionAreas: []
};
+// 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 || {};
+ if (typeof fullData == 'string') {
+ fullData = fullData + "&" + $.param({csrf_token: token});
+ } else {
+ 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/index/create-project-ui.js b/main/webapp/modules/core/scripts/index/create-project-ui.js
index 7e97009a4..4b176830f 100644
--- a/main/webapp/modules/core/scripts/index/create-project-ui.js
+++ b/main/webapp/modules/core/scripts/index/create-project-ui.js
@@ -271,5 +271,8 @@ Refine.CreateProjectUI.composeErrorMessage = function(job) {
};
Refine.CreateProjectUI.cancelImportingJob = function(jobID) {
- $.post("command/core/cancel-importing-job?" + $.param({ "jobID": jobID }));
+ Refine.wrapCSRF(function(token) {
+ $.post("command/core/cancel-importing-job?" + $.param({ "jobID": jobID }),
+ {csrf_token: token});
+ });
};
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 83e85ef74..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
@@ -78,61 +78,64 @@ Refine.DefaultImportingController.prototype.startImportJob = function(form, prog
return this.value === "";
}).attr("disabled", "disabled");
- $.post(
- "command/core/create-importing-job",
- null,
- function(data) {
- var jobID = self._jobID = data.jobID;
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/create-importing-job",
+ { csrf_token: token },
+ function(data) {
+ var jobID = self._jobID = data.jobID;
- form.attr("method", "post")
- .attr("enctype", "multipart/form-data")
- .attr("accept-charset", "UTF-8")
- .attr("target", "create-project-iframe")
- .attr("action", "command/core/importing-controller?" + $.param({
- "controller": "core/default-importing-controller",
- "jobID": jobID,
- "subCommand": "load-raw-data"
- }));
- form[0].submit();
+ form.attr("method", "post")
+ .attr("enctype", "multipart/form-data")
+ .attr("accept-charset", "UTF-8")
+ .attr("target", "create-project-iframe")
+ .attr("action", "command/core/importing-controller?" + $.param({
+ "controller": "core/default-importing-controller",
+ "jobID": jobID,
+ "subCommand": "load-raw-data",
+ "csrf_token": token
+ }));
+ form[0].submit();
- var start = new Date();
- var timerID = window.setInterval(
- function() {
- self._createProjectUI.pollImportJob(
- start, jobID, timerID,
- function(job) {
- return job.config.hasData;
- },
- function(jobID, job) {
- self._job = job;
- self._onImportJobReady();
- if (callback) {
- callback(jobID, job);
+ var start = new Date();
+ var timerID = window.setInterval(
+ function() {
+ self._createProjectUI.pollImportJob(
+ start, jobID, timerID,
+ function(job) {
+ return job.config.hasData;
+ },
+ function(jobID, job) {
+ self._job = job;
+ self._onImportJobReady();
+ if (callback) {
+ callback(jobID, job);
+ }
+ },
+ function(job) {
+ alert(job.config.error + '\n' + job.config.errorDetails);
+ self._startOver();
}
- },
- function(job) {
- alert(job.config.error + '\n' + job.config.errorDetails);
- self._startOver();
- }
+ );
+ },
+ 1000
);
- },
- 1000
- );
- self._createProjectUI.showImportProgressPanel(progressMessage, function() {
- // stop the iframe
- $('#create-project-iframe')[0].contentWindow.stop();
+ self._createProjectUI.showImportProgressPanel(progressMessage, function() {
+ // stop the iframe
+ $('#create-project-iframe')[0].contentWindow.stop();
- // stop the timed polling
- window.clearInterval(timerID);
+ // stop the timed polling
+ window.clearInterval(timerID);
- // explicitly cancel the import job
- Refine.CreateProjectUI.cancelImportingJob(jobID);
+ // explicitly cancel the import job
+ Refine.CreateProjectUI.cancelImportingJob(jobID);
- self._createProjectUI.showSourceSelectionPanel();
- });
- },
- "json"
- );
+ self._createProjectUI.showSourceSelectionPanel();
+ });
+ },
+ "json"
+ );
+ });
};
Refine.DefaultImportingController.prototype._onImportJobReady = function() {
@@ -180,27 +183,30 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
if (!(format in this._parserOptions)) {
var self = this;
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting'));
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "core/default-importing-controller",
- "jobID": this._jobID,
- "subCommand": "initialize-parser-ui",
- "format": format
- }),
- null,
- function(data) {
- dismissBusy();
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "core/default-importing-controller",
+ "jobID": self._jobID,
+ "subCommand": "initialize-parser-ui",
+ "format": format,
+ "csrf_token": token
+ }),
+ null,
+ function(data) {
+ dismissBusy();
- if (data.options) {
- self._parserOptions[format] = data.options;
- onDone();
- }
- },
- "json"
- )
- .fail(function() {
- dismissBusy();
- alert($.i18n('core-views/check-format'));
+ if (data.options) {
+ self._parserOptions[format] = data.options;
+ onDone();
+ }
+ },
+ "json"
+ )
+ .fail(function() {
+ dismissBusy();
+ alert($.i18n('core-views/check-format'));
+ });
});
} else {
onDone();
@@ -209,32 +215,35 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
Refine.DefaultImportingController.prototype.updateFormatAndOptions = function(options, callback, finallyCallBack) {
var self = this;
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "core/default-importing-controller",
- "jobID": this._jobID,
- "subCommand": "update-format-and-options"
- }),
- {
- "format" : this._format,
- "options" : JSON.stringify(options)
- },
- function(o) {
- if (o.status == 'error') {
- if (o.message) {
- alert(o.message);
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "core/default-importing-controller",
+ "jobID": self._jobID,
+ "subCommand": "update-format-and-options",
+ "csrf_token": token
+ }),
+ {
+ "format" : self._format,
+ "options" : JSON.stringify(options)
+ },
+ function(o) {
+ if (o.status == 'error') {
+ if (o.message) {
+ alert(o.message);
+ } else {
+ var messages = [];
+ $.each(o.errors, function() { messages.push(this.message); });
+ alert(messages.join('\n\n'));
+ }
+ finallyCallBack();
} else {
- var messages = [];
- $.each(o.errors, function() { messages.push(this.message); });
- alert(messages.join('\n\n'));
+ callback(o);
}
- finallyCallBack();
- } else {
- callback(o);
- }
- },
- "json"
- );
+ },
+ "json"
+ );
+ });
};
Refine.DefaultImportingController.prototype.getPreviewData = function(callback, numRows) {
@@ -242,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) {
@@ -284,56 +293,59 @@ Refine.DefaultImportingController.prototype._createProject = function() {
var options = this._formatParserUI.getOptions();
options.projectName = projectName;
options.projectTags = projectTags;
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "core/default-importing-controller",
- "jobID": this._jobID,
- "subCommand": "create-project"
- }),
- {
- "format" : this._format,
- "options" : JSON.stringify(options)
- },
- function(o) {
- if (o.status == 'error') {
- alert(o.message);
- return;
- }
-
- var start = new Date();
- var timerID = window.setInterval(
- function() {
- self._createProjectUI.pollImportJob(
- start,
- self._jobID,
- timerID,
- function(job) {
- return "projectID" in job.config;
- },
- function(jobID, job) {
- Refine.CreateProjectUI.cancelImportingJob(jobID);
- document.location = "project?project=" + job.config.projectID;
- },
- function(job) {
- alert($.i18n('core-index-import/errors')+'\n' + Refine.CreateProjectUI.composeErrorMessage(job));
- self._onImportJobReady();
- }
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "core/default-importing-controller",
+ "jobID": self._jobID,
+ "subCommand": "create-project",
+ "csrf_token": token
+ }),
+ {
+ "format" : self._format,
+ "options" : JSON.stringify(options)
+ },
+ function(o) {
+ if (o.status == 'error') {
+ alert(o.message);
+ return;
+ }
+
+ var start = new Date();
+ var timerID = window.setInterval(
+ function() {
+ self._createProjectUI.pollImportJob(
+ start,
+ self._jobID,
+ timerID,
+ function(job) {
+ return "projectID" in job.config;
+ },
+ function(jobID, job) {
+ Refine.CreateProjectUI.cancelImportingJob(jobID);
+ document.location = "project?project=" + job.config.projectID;
+ },
+ function(job) {
+ alert($.i18n('core-index-import/errors')+'\n' + Refine.CreateProjectUI.composeErrorMessage(job));
+ self._onImportJobReady();
+ }
+ );
+ },
+ 1000
);
- },
- 1000
+ self._createProjectUI.showImportProgressPanel($.i18n('core-index-import/creating-proj'), function() {
+ // stop the timed polling
+ window.clearInterval(timerID);
+
+ // explicitly cancel the import job
+ Refine.CreateProjectUI.cancelImportingJob(self._jobID);
+
+ self._createProjectUI.showSourceSelectionPanel();
+ });
+ },
+ "json"
);
- self._createProjectUI.showImportProgressPanel($.i18n('core-index-import/creating-proj'), function() {
- // stop the timed polling
- window.clearInterval(timerID);
-
- // explicitly cancel the import job
- Refine.CreateProjectUI.cancelImportingJob(self._jobID);
-
- self._createProjectUI.showSourceSelectionPanel();
- });
- },
- "json"
- );
+ });
}
};
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 90c63b722..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
@@ -328,30 +328,33 @@ Refine.DefaultImportingController.prototype._commitFileSelection = function() {
var self = this;
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting-files'));
- $.post(
- "command/core/importing-controller?" + $.param({
- "controller": "core/default-importing-controller",
- "jobID": this._jobID,
- "subCommand": "update-file-selection"
- }),
- {
- "fileSelection" : JSON.stringify(this._job.config.fileSelection)
- },
- function(data) {
- dismissBusy();
+ Refine.wrapCSRF(function(token) {
+ $.post(
+ "command/core/importing-controller?" + $.param({
+ "controller": "core/default-importing-controller",
+ "jobID": self._jobID,
+ "subCommand": "update-file-selection",
+ "csrf_token": token
+ }),
+ {
+ "fileSelection" : JSON.stringify(self._job.config.fileSelection)
+ },
+ function(data) {
+ dismissBusy();
- if (!(data)) {
- self._createProjectUI.showImportJobError($.i18n('core-index-import/unknown-err'));
- } else if (data.code == "error" || !("job" in data)) {
- 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;
+ if (!(data)) {
+ self._createProjectUI.showImportJobError($.i18n('core-index-import/unknown-err'));
+ } else if (data.code == "error" || !("job" in data)) {
+ 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 self._parserOptions;
- self._job = data.job;
- self._showParsingPanel(true);
- }
- },
- "json"
- );
+ self._job = data.job;
+ self._showParsingPanel(true);
+ }
+ },
+ "json"
+ );
+ });
};
diff --git a/main/webapp/modules/core/scripts/index/edit-metadata-dialog.js b/main/webapp/modules/core/scripts/index/edit-metadata-dialog.js
index 6fb7ea275..b58c1c823 100644
--- a/main/webapp/modules/core/scripts/index/edit-metadata-dialog.js
+++ b/main/webapp/modules/core/scripts/index/edit-metadata-dialog.js
@@ -31,16 +31,16 @@ function EditMetadataDialog(metaData, targetRowElem) {
if (newTags !== null) {
$(td1).text(newTags);
metaData[key] = newTags;
- $.ajax({
- type : "POST",
- url : "command/core/set-project-tags",
- data : {
+ Refine.postCSRF(
+ "command/core/set-project-tags",
+ {
"project" : project,
"old" : oldTags,
"new" : newTags
},
- dataType : "json",
- });
+ function(data) {},
+ "json"
+ );
}
Refine.OpenProjectUI.refreshProject(targetRowElem, metaData, project);
@@ -58,8 +58,8 @@ function EditMetadataDialog(metaData, targetRowElem) {
if (newValue !== null) {
$(td1).text(newValue);
metaData[key] = newValue;
- $.post(
- "command/core/set-metaData",
+ Refine.postCSRF(
+ "command/core/set-project-metadata",
{
project : project,
name : key,
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 5229cd69e..8535cec5f 100644
--- a/main/webapp/modules/core/scripts/index/import-project-ui.js
+++ b/main/webapp/modules/core/scripts/index/import-project-ui.js
@@ -33,6 +33,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Refine.ImportProjectUI = function(elmt) {
elmt.html(DOM.loadHTML("core", "scripts/index/import-project-ui.html"));
+
+ Refine.wrapCSRF(function(token) {
+ elmt.attr('action', "command/core/import-project?" + $.param({ csrf_token: token}));
+ });
this._elmt = elmt;
this._elmts = DOM.bind(elmt);
diff --git a/main/webapp/modules/core/scripts/index/lang-settings-ui.js b/main/webapp/modules/core/scripts/index/lang-settings-ui.js
index b322ba825..ceb1bc8d1 100644
--- a/main/webapp/modules/core/scripts/index/lang-settings-ui.js
+++ b/main/webapp/modules/core/scripts/index/lang-settings-ui.js
@@ -28,19 +28,22 @@ Refine.SetLanguageUI = function(elmt) {
});
this._elmts.set_lan_btn.bind('click', function(e) {
- $.ajax({
- url : "command/core/set-preference?",
- type : "POST",
- async : false,
- data : {
- name : "userLang",
- value : JSON.stringify($("#langDD option:selected").val())
- },
- success : function(data) {
- alert($.i18n('core-index-lang/page-reload'));
- location.reload(true);
- }
- });
+ Refine.wrapCSRF(function(token) {
+ $.ajax({
+ url : "command/core/set-preference?",
+ type : "POST",
+ async : false,
+ data : {
+ name : "userLang",
+ value : JSON.stringify($("#langDD option:selected").val()),
+ csrf_token: token
+ },
+ success : function(data) {
+ alert($.i18n('core-index-lang/page-reload'));
+ location.reload(true);
+ }
+ });
+ });
});
};
diff --git a/main/webapp/modules/core/scripts/index/open-project-ui.js b/main/webapp/modules/core/scripts/index/open-project-ui.js
index c3340f950..142c4f3bf 100644
--- a/main/webapp/modules/core/scripts/index/open-project-ui.js
+++ b/main/webapp/modules/core/scripts/index/open-project-ui.js
@@ -59,16 +59,16 @@ Refine.OpenProjectUI = function(elmt) {
$('#projects-workspace-open').text($.i18n('core-index-open/browse'));
$('#projects-workspace-open').click(function() {
- $.ajax({
- type: "POST",
- url: "command/core/open-workspace-dir",
- dataType: "json",
- success: function (data) {
+ Refine.postCSRF(
+ "command/core/open-workspace-dir",
+ {},
+ function (data) {
if (data.code != "ok" && "message" in data) {
alert(data.message);
}
- }
- });
+ },
+ "json"
+ );
});
Refine.TagsManager.allProjectTags = [];
this._buildTagsAndFetchProjects();
@@ -221,18 +221,17 @@ Refine.OpenProjectUI.prototype._renderProjects = function(data) {
.html("")
.click(function() {
if (window.confirm($.i18n('core-index-open/del-body') + project.name + "\"?")) {
- $.ajax({
- type: "POST",
- url: "command/core/delete-project",
- data: { "project" : project.id },
- dataType: "json",
- success: function (data) {
+ Refine.postCSRF(
+ "command/core/delete-project",
+ { "project" : project.id },
+ function (data) {
if (data && typeof data.code != 'undefined' && data.code == "ok") {
Refine.TagsManager.allProjectTags = [];
self._buildTagsAndFetchProjects();
}
- }
- });
+ },
+ "json"
+ );
}
return false;
}).appendTo(
diff --git a/main/webapp/modules/core/scripts/preferences.js b/main/webapp/modules/core/scripts/preferences.js
index 19bf905eb..36d7afcc2 100644
--- a/main/webapp/modules/core/scripts/preferences.js
+++ b/main/webapp/modules/core/scripts/preferences.js
@@ -33,6 +33,41 @@ 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 || {};
+ if (typeof fullData == 'string') {
+ fullData = fullData + "&" + $.param({csrf_token: token});
+ } else {
+ 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({
@@ -78,7 +113,7 @@ function PreferenceUI(tr, key, value) {
}
$(td1).text(newValue);
- $.post(
+ Refine.postCSRF(
"command/core/set-preference",
{
name : key,
@@ -96,7 +131,7 @@ function PreferenceUI(tr, key, value) {
$('