CSRF protection for gdata extension
This commit is contained in:
parent
b52c009491
commit
24feda600a
@ -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) {
|
||||
|
@ -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": 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"
|
||||
);
|
||||
});
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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) {
|
||||
|
14
extensions/gdata/tests/conf/tests.xml
Normal file
14
extensions/gdata/tests/conf/tests.xml
Normal 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>
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user