CSRF protection for gdata extension

This commit is contained in:
Antonin Delpeuch 2019-10-17 09:54:53 +01:00
parent b52c009491
commit 24feda600a
7 changed files with 192 additions and 105 deletions

View File

@ -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) {

View File

@ -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();
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();
if (data2.status == 'ok') {
self._doc = doc;
self._jobID = data.jobID;
self._options = data2.options;
if (data2.status == 'ok') {
self._doc = doc;
self._jobID = data.jobID;
self._options = data2.options;
self._showParsingPanel();
} else {
alert(data2.message);
}
},
"json"
);
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": this._jobID,
"subCommand": "parse-preview",
"csrf_token": token
}),
{
"options" : JSON.stringify(this.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": this._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"
);
});
};

View File

@ -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,

View File

@ -66,7 +66,9 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<skip>true</skip>
<suiteXmlFiles>
<suiteXmlFile>tests/conf/tests.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
<plugin>
@ -162,6 +164,12 @@
<version>6.9.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.23.4</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -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) {

View File

@ -0,0 +1,14 @@
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="GData extension">
<test name="tests">
<groups>
<run>
<exclude name="broken" />
</run>
</groups>
<packages>
<package name="com.google.refine.extension.gdata.*" />
</packages>
</test>
</suite>

View File

@ -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));
}
}