CSRF protection for ImportingController
This commit is contained in:
parent
70e37b9085
commit
91cead27f8
@ -56,6 +56,10 @@ public class ImportingControllerCommand extends Command {
|
|||||||
@Override
|
@Override
|
||||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
if(!checkCSRF(request)) {
|
||||||
|
respondCSRFError(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImportingController controller = getController(request);
|
ImportingController controller = getController(request);
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
@ -92,4 +96,14 @@ public class ImportingControllerCommand extends Command {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the validity of a CSRF token, without reading the whole POST body.
|
||||||
|
* See above for details.
|
||||||
|
*/
|
||||||
|
private boolean checkCSRF(HttpServletRequest request) {
|
||||||
|
Properties options = ParsingUtilities.parseUrlParameters(request);
|
||||||
|
String token = options.getProperty("csrf_token");
|
||||||
|
return token != null && csrfFactory.validToken(token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -157,15 +157,13 @@ ExpressionPreviewDialog.Widget.prototype.getExpression = function(commit) {
|
|||||||
|
|
||||||
s = this._getLanguage() + ":" + s;
|
s = this._getLanguage() + ":" + s;
|
||||||
if (commit) {
|
if (commit) {
|
||||||
Refine.wrapCSRF(function(token) {
|
Refine.postCSRF(
|
||||||
$.post(
|
"command/core/log-expression?" + $.param({ project: theProject.id }),
|
||||||
"command/core/log-expression?" + $.param({ project: theProject.id }),
|
{ expression: s },
|
||||||
{ expression: s, csrf_token: token },
|
function(data) {
|
||||||
function(data) {
|
},
|
||||||
},
|
"json"
|
||||||
"json"
|
);
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@ -286,21 +284,18 @@ ExpressionPreviewDialog.Widget.prototype._renderExpressionHistory = function(dat
|
|||||||
.addClass(entry.starred ? "data-table-star-on" : "data-table-star-off")
|
.addClass(entry.starred ? "data-table-star-on" : "data-table-star-off")
|
||||||
.appendTo(tr.insertCell(0))
|
.appendTo(tr.insertCell(0))
|
||||||
.click(function() {
|
.click(function() {
|
||||||
Refine.wrapCSRF(function(token) {
|
Refine.postCSRF(
|
||||||
$.post(
|
"command/core/toggle-starred-expression",
|
||||||
"command/core/toggle-starred-expression",
|
{
|
||||||
{
|
expression: entry.code
|
||||||
expression: entry.code,
|
},
|
||||||
csrf_token: token
|
function(data) {
|
||||||
},
|
entry.starred = !entry.starred;
|
||||||
function(data) {
|
renderEntry(self,tr,entry);
|
||||||
entry.starred = !entry.starred;
|
self._renderStarredExpressionsTab();
|
||||||
renderEntry(self,tr,entry);
|
},
|
||||||
self._renderStarredExpressionsTab();
|
"json"
|
||||||
},
|
);
|
||||||
"json"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('<a href="javascript:{}">'+$.i18n('core-dialogs/reuse')+'</a>').appendTo(tr.insertCell(1)).click(function() {
|
$('<a href="javascript:{}">'+$.i18n('core-dialogs/reuse')+'</a>').appendTo(tr.insertCell(1)).click(function() {
|
||||||
@ -355,17 +350,15 @@ ExpressionPreviewDialog.Widget.prototype._renderStarredExpressions = function(da
|
|||||||
var o = Scripting.parse(entry.code);
|
var o = Scripting.parse(entry.code);
|
||||||
|
|
||||||
$('<a href="javascript:{}">'+$.i18n('core-dialogs/remove')+'</a>').appendTo(tr.insertCell(0)).click(function() {
|
$('<a href="javascript:{}">'+$.i18n('core-dialogs/remove')+'</a>').appendTo(tr.insertCell(0)).click(function() {
|
||||||
Refine.wrapCSRF(function(token) {
|
Refine.postCSRF(
|
||||||
$.post(
|
"command/core/toggle-starred-expression",
|
||||||
"command/core/toggle-starred-expression",
|
{ expression: entry.code, returnList: true },
|
||||||
{ expression: entry.code, returnList: true, csrf_token: token },
|
function(data) {
|
||||||
function(data) {
|
self._renderStarredExpressions(data);
|
||||||
self._renderStarredExpressions(data);
|
self._renderExpressionHistoryTab();
|
||||||
self._renderExpressionHistoryTab();
|
},
|
||||||
},
|
"json"
|
||||||
"json"
|
);
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('<a href="javascript:{}">Reuse</a>').appendTo(tr.insertCell(1)).click(function() {
|
$('<a href="javascript:{}">Reuse</a>').appendTo(tr.insertCell(1)).click(function() {
|
||||||
|
@ -92,7 +92,8 @@ Refine.DefaultImportingController.prototype.startImportJob = function(form, prog
|
|||||||
.attr("action", "command/core/importing-controller?" + $.param({
|
.attr("action", "command/core/importing-controller?" + $.param({
|
||||||
"controller": "core/default-importing-controller",
|
"controller": "core/default-importing-controller",
|
||||||
"jobID": jobID,
|
"jobID": jobID,
|
||||||
"subCommand": "load-raw-data"
|
"subCommand": "load-raw-data",
|
||||||
|
"csrf_token": token
|
||||||
}));
|
}));
|
||||||
form[0].submit();
|
form[0].submit();
|
||||||
|
|
||||||
@ -182,27 +183,30 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
|
|||||||
if (!(format in this._parserOptions)) {
|
if (!(format in this._parserOptions)) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting'));
|
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting'));
|
||||||
$.post(
|
Refine.wrapCSRF(function(token) {
|
||||||
"command/core/importing-controller?" + $.param({
|
$.post(
|
||||||
"controller": "core/default-importing-controller",
|
"command/core/importing-controller?" + $.param({
|
||||||
"jobID": this._jobID,
|
"controller": "core/default-importing-controller",
|
||||||
"subCommand": "initialize-parser-ui",
|
"jobID": this._jobID,
|
||||||
"format": format
|
"subCommand": "initialize-parser-ui",
|
||||||
}),
|
"format": format,
|
||||||
null,
|
"csrf_token": token
|
||||||
function(data) {
|
}),
|
||||||
dismissBusy();
|
null,
|
||||||
|
function(data) {
|
||||||
|
dismissBusy();
|
||||||
|
|
||||||
if (data.options) {
|
if (data.options) {
|
||||||
self._parserOptions[format] = data.options;
|
self._parserOptions[format] = data.options;
|
||||||
onDone();
|
onDone();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"json"
|
"json"
|
||||||
)
|
)
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
dismissBusy();
|
dismissBusy();
|
||||||
alert($.i18n('core-views/check-format'));
|
alert($.i18n('core-views/check-format'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
onDone();
|
onDone();
|
||||||
@ -211,32 +215,35 @@ Refine.DefaultImportingController.prototype._ensureFormatParserUIHasInitializati
|
|||||||
|
|
||||||
Refine.DefaultImportingController.prototype.updateFormatAndOptions = function(options, callback, finallyCallBack) {
|
Refine.DefaultImportingController.prototype.updateFormatAndOptions = function(options, callback, finallyCallBack) {
|
||||||
var self = this;
|
var self = this;
|
||||||
$.post(
|
Refine.wrapCSRF(function(token) {
|
||||||
"command/core/importing-controller?" + $.param({
|
$.post(
|
||||||
"controller": "core/default-importing-controller",
|
"command/core/importing-controller?" + $.param({
|
||||||
"jobID": this._jobID,
|
"controller": "core/default-importing-controller",
|
||||||
"subCommand": "update-format-and-options"
|
"jobID": this._jobID,
|
||||||
}),
|
"subCommand": "update-format-and-options",
|
||||||
{
|
"csrf_token": token
|
||||||
"format" : this._format,
|
}),
|
||||||
"options" : JSON.stringify(options)
|
{
|
||||||
},
|
"format" : this._format,
|
||||||
function(o) {
|
"options" : JSON.stringify(options)
|
||||||
if (o.status == 'error') {
|
},
|
||||||
if (o.message) {
|
function(o) {
|
||||||
alert(o.message);
|
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 {
|
} else {
|
||||||
var messages = [];
|
callback(o);
|
||||||
$.each(o.errors, function() { messages.push(this.message); });
|
|
||||||
alert(messages.join('\n\n'));
|
|
||||||
}
|
}
|
||||||
finallyCallBack();
|
},
|
||||||
} else {
|
"json"
|
||||||
callback(o);
|
);
|
||||||
}
|
});
|
||||||
},
|
|
||||||
"json"
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Refine.DefaultImportingController.prototype.getPreviewData = function(callback, numRows) {
|
Refine.DefaultImportingController.prototype.getPreviewData = function(callback, numRows) {
|
||||||
@ -286,56 +293,59 @@ Refine.DefaultImportingController.prototype._createProject = function() {
|
|||||||
var options = this._formatParserUI.getOptions();
|
var options = this._formatParserUI.getOptions();
|
||||||
options.projectName = projectName;
|
options.projectName = projectName;
|
||||||
options.projectTags = projectTags;
|
options.projectTags = projectTags;
|
||||||
$.post(
|
Refine.wrapCSRF(function(token) {
|
||||||
"command/core/importing-controller?" + $.param({
|
$.post(
|
||||||
"controller": "core/default-importing-controller",
|
"command/core/importing-controller?" + $.param({
|
||||||
"jobID": this._jobID,
|
"controller": "core/default-importing-controller",
|
||||||
"subCommand": "create-project"
|
"jobID": this._jobID,
|
||||||
}),
|
"subCommand": "create-project",
|
||||||
{
|
"csrf_token": token
|
||||||
"format" : this._format,
|
}),
|
||||||
"options" : JSON.stringify(options)
|
{
|
||||||
},
|
"format" : this._format,
|
||||||
function(o) {
|
"options" : JSON.stringify(options)
|
||||||
if (o.status == 'error') {
|
},
|
||||||
alert(o.message);
|
function(o) {
|
||||||
return;
|
if (o.status == 'error') {
|
||||||
}
|
alert(o.message);
|
||||||
|
return;
|
||||||
var start = new Date();
|
}
|
||||||
var timerID = window.setInterval(
|
|
||||||
function() {
|
var start = new Date();
|
||||||
self._createProjectUI.pollImportJob(
|
var timerID = window.setInterval(
|
||||||
start,
|
function() {
|
||||||
self._jobID,
|
self._createProjectUI.pollImportJob(
|
||||||
timerID,
|
start,
|
||||||
function(job) {
|
self._jobID,
|
||||||
return "projectID" in job.config;
|
timerID,
|
||||||
},
|
function(job) {
|
||||||
function(jobID, job) {
|
return "projectID" in job.config;
|
||||||
Refine.CreateProjectUI.cancelImportingJob(jobID);
|
},
|
||||||
document.location = "project?project=" + job.config.projectID;
|
function(jobID, job) {
|
||||||
},
|
Refine.CreateProjectUI.cancelImportingJob(jobID);
|
||||||
function(job) {
|
document.location = "project?project=" + job.config.projectID;
|
||||||
alert($.i18n('core-index-import/errors')+'\n' + Refine.CreateProjectUI.composeErrorMessage(job));
|
},
|
||||||
self._onImportJobReady();
|
function(job) {
|
||||||
}
|
alert($.i18n('core-index-import/errors')+'\n' + Refine.CreateProjectUI.composeErrorMessage(job));
|
||||||
|
self._onImportJobReady();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
1000
|
||||||
);
|
);
|
||||||
},
|
self._createProjectUI.showImportProgressPanel($.i18n('core-index-import/creating-proj'), function() {
|
||||||
1000
|
// 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"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -328,30 +328,33 @@ Refine.DefaultImportingController.prototype._commitFileSelection = function() {
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting-files'));
|
var dismissBusy = DialogSystem.showBusy($.i18n('core-index-import/inspecting-files'));
|
||||||
$.post(
|
Refine.wrapCSRF(function(token) {
|
||||||
"command/core/importing-controller?" + $.param({
|
$.post(
|
||||||
"controller": "core/default-importing-controller",
|
"command/core/importing-controller?" + $.param({
|
||||||
"jobID": this._jobID,
|
"controller": "core/default-importing-controller",
|
||||||
"subCommand": "update-file-selection"
|
"jobID": this._jobID,
|
||||||
}),
|
"subCommand": "update-file-selection",
|
||||||
{
|
"csrf_token": token
|
||||||
"fileSelection" : JSON.stringify(this._job.config.fileSelection)
|
}),
|
||||||
},
|
{
|
||||||
function(data) {
|
"fileSelection" : JSON.stringify(this._job.config.fileSelection)
|
||||||
dismissBusy();
|
},
|
||||||
|
function(data) {
|
||||||
|
dismissBusy();
|
||||||
|
|
||||||
if (!(data)) {
|
if (!(data)) {
|
||||||
self._createProjectUI.showImportJobError($.i18n('core-index-import/unknown-err'));
|
self._createProjectUI.showImportJobError($.i18n('core-index-import/unknown-err'));
|
||||||
} else if (data.code == "error" || !("job" in data)) {
|
} 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'));
|
self._createProjectUI.showImportJobError((data.message) ? ($.i18n('core-index-import/error')+ ' ' + data.message) : $.i18n('core-index-import/unknown-err'));
|
||||||
} else {
|
} else {
|
||||||
// Different files might be selected. We start over again.
|
// Different files might be selected. We start over again.
|
||||||
delete this._parserOptions;
|
delete this._parserOptions;
|
||||||
|
|
||||||
self._job = data.job;
|
self._job = data.job;
|
||||||
self._showParsingPanel(true);
|
self._showParsingPanel(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"json"
|
"json"
|
||||||
);
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -388,18 +388,11 @@ Refine.postProcess = function(moduleName, command, params, body, updateOptions,
|
|||||||
|
|
||||||
Refine.setAjaxInProgress();
|
Refine.setAjaxInProgress();
|
||||||
|
|
||||||
Refine.wrapCSRF(
|
Refine.postCSRF(
|
||||||
function(token) {
|
"command/" + moduleName + "/" + command + "?" + $.param(params),
|
||||||
|
body,
|
||||||
// Add it to the body and submit it as a POST request
|
onDone,
|
||||||
body['csrf_token'] = token;
|
"json"
|
||||||
$.post(
|
|
||||||
"command/" + moduleName + "/" + command + "?" + $.param(params),
|
|
||||||
body,
|
|
||||||
onDone,
|
|
||||||
"json"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function() {
|
||||||
@ -422,6 +415,17 @@ Refine.wrapCSRF = function(onCSRF) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
Refine.wrapCSRF(function(token) {
|
||||||
|
var fullData = data || {};
|
||||||
|
data['csrf_token'] = token;
|
||||||
|
$.post(url, fulldata, success, dataType);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Refine.setAjaxInProgress = function() {
|
Refine.setAjaxInProgress = function() {
|
||||||
$(document.body).attr("ajax_in_progress", "true");
|
$(document.body).attr("ajax_in_progress", "true");
|
||||||
};
|
};
|
||||||
|
@ -124,17 +124,15 @@ ProcessPanel.prototype.undo = function() {
|
|||||||
|
|
||||||
ProcessPanel.prototype._cancelAll = function() {
|
ProcessPanel.prototype._cancelAll = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
Refine.wrapCSRF(function(token) {
|
Refine.postCSRF(
|
||||||
$.post(
|
"command/core/cancel-processes?" + $.param({ project: theProject.id }),
|
||||||
"command/core/cancel-processes?" + $.param({ project: theProject.id }),
|
{ },
|
||||||
{ csrf_token: token },
|
function(o) {
|
||||||
function(o) {
|
self._data = null;
|
||||||
self._data = null;
|
self._runOnDones();
|
||||||
self._runOnDones();
|
},
|
||||||
},
|
"json"
|
||||||
"json"
|
);
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ProcessPanel.prototype._render = function(newData) {
|
ProcessPanel.prototype._render = function(newData) {
|
||||||
|
Loading…
Reference in New Issue
Block a user