Fixing CSRF issues (#2569)

* 0.17.4

* Fixing CSRF issues

Since turning CSRF across the site with Flask-WTF, a few POST request
have been failing. This PR addresses these issues.
diff --git a/superset/assets/javascripts/SqlLab/components/App.jsx b/superset/assets/javascripts/SqlLab/components/App.jsx
index 81736ac..2e96912 100644
--- a/superset/assets/javascripts/SqlLab/components/App.jsx
+++ b/superset/assets/javascripts/SqlLab/components/App.jsx
@@ -5,7 +5,7 @@
 import TabbedSqlEditors from './TabbedSqlEditors';
 import QueryAutoRefresh from './QueryAutoRefresh';
 import QuerySearch from './QuerySearch';
-import AlertsWrapper from './AlertsWrapper';
+import AlertsWrapper from '../../components/AlertsWrapper';
 
 import { bindActionCreators } from 'redux';
 import { connect } from 'react-redux';
diff --git a/superset/assets/javascripts/SqlLab/components/AlertsWrapper.jsx b/superset/assets/javascripts/components/AlertsWrapper.jsx
similarity index 100%
rename from superset/assets/javascripts/SqlLab/components/AlertsWrapper.jsx
rename to superset/assets/javascripts/components/AlertsWrapper.jsx
diff --git a/superset/assets/javascripts/explorev2/index.jsx b/superset/assets/javascripts/explorev2/index.jsx
index cd7f52f..f2757eb 100644
--- a/superset/assets/javascripts/explorev2/index.jsx
+++ b/superset/assets/javascripts/explorev2/index.jsx
@@ -7,7 +7,9 @@
 import thunk from 'redux-thunk';
 import { now } from '../modules/dates';
 import { initEnhancer } from '../reduxUtils';
+import AlertsWrapper from '../components/AlertsWrapper';
 import { getControlsState, getFormDataFromControls } from './stores/store';
+import { initJQueryAjaxCSRF } from '../modules/utils';
 
 
 // jquery and bootstrap required to make bootstrap dropdown menu's work
@@ -15,6 +17,7 @@
 const jQuery = window.jQuery = require('jquery'); // eslint-disable-line
 require('bootstrap');
 require('./main.css');
+initJQueryAjaxCSRF();
 
 const exploreViewContainer = document.getElementById('js-explore-view-container');
 const bootstrapData = JSON.parse(exploreViewContainer.getAttribute('data-bootstrap'));
@@ -47,7 +50,10 @@
 
 ReactDOM.render(
   <Provider store={store}>
-    <ExploreViewContainer />
+    <div>
+      <ExploreViewContainer />
+      <AlertsWrapper />
+    </div>
   </Provider>,
   exploreViewContainer
 );
diff --git a/superset/assets/package.json b/superset/assets/package.json
index 0c66b34..c3ec785 100644
--- a/superset/assets/package.json
+++ b/superset/assets/package.json
@@ -1,6 +1,6 @@
 {
   "name": "superset",
-  "version": "0.17.4rc5",
+  "version": "0.17.4",
   "description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
   "license": "Apache-2.0",
   "directories": {
diff --git a/superset/assets/spec/javascripts/sqllab/AlertsWrapper_spec.jsx b/superset/assets/spec/javascripts/sqllab/AlertsWrapper_spec.jsx
index 064fe8c..7e1f2e9 100644
--- a/superset/assets/spec/javascripts/sqllab/AlertsWrapper_spec.jsx
+++ b/superset/assets/spec/javascripts/sqllab/AlertsWrapper_spec.jsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import AlertsWrapper from '../../../javascripts/SqlLab/components/AlertsWrapper';
+import AlertsWrapper from '../../../javascripts/components/AlertsWrapper';
 import { describe, it } from 'mocha';
 import { expect } from 'chai';
 
diff --git a/superset/assets/utils/common.js b/superset/assets/utils/common.js
index dd94c83..592f708 100644
--- a/superset/assets/utils/common.js
+++ b/superset/assets/utils/common.js
@@ -1,3 +1,4 @@
+/* global notify */
 /* eslint global-require: 0 */
 import $ from 'jquery';
 const d3 = window.d3 || require('d3');
@@ -78,12 +79,8 @@
     success: (data) => {
       callback(data);
     },
-    error: (error) => {
-      /* eslint no-console: 0 */
-      if (console && console.warn) {
-        console.warn('Something went wrong...');
-        console.warn(error);
-      }
+    error: () => {
+      notify.error('Error getting the short URL');
       callback(longUrl);
     },
   });
diff --git a/superset/templates/superset/basic.html b/superset/templates/superset/basic.html
index a2e9200..e146c51 100644
--- a/superset/templates/superset/basic.html
+++ b/superset/templates/superset/basic.html
@@ -22,6 +22,12 @@
         {% include "superset/partials/_script_tag.html" %}
       {% endwith %}
     {% endblock %}
+    <input
+      type="hidden"
+      name="csrf_token"
+      id="csrf_token"
+      value="{{ csrf_token() if csrf_token else '' }}"
+    >
   </head>
 
   <body>
@@ -38,12 +44,6 @@
       <div id="app" data-bootstrap="{{ bootstrap_data }}" >
         <img src="/static/assets/images/loading.gif" style="width: 50px; margin: 10px;">
       </div>
-      <input
-        type="hidden"
-        name="csrf_token"
-        id="csrf_token"
-        value="{{ csrf_token() if csrf_token else '' }}"
-      >
     {% endblock %}
 
     <!-- Modal for misc messages / alerts  -->
diff --git a/superset/templates/superset/dashboard.html b/superset/templates/superset/dashboard.html
index ae203ba..f899d6f 100644
--- a/superset/templates/superset/dashboard.html
+++ b/superset/templates/superset/dashboard.html
@@ -22,10 +22,4 @@
   <div id="grid-container" class="slice-grid gridster"></div>
 
 </div>
-<input
-  type="hidden"
-  name="csrf_token"
-  id="csrf_token"
-  value="{{ csrf_token() if csrf_token else '' }}"
->
 {% endblock %}
diff --git a/superset/templates/superset/models/database/macros.html b/superset/templates/superset/models/database/macros.html
index db5422f..0854357 100644
--- a/superset/templates/superset/models/database/macros.html
+++ b/superset/templates/superset/models/database/macros.html
@@ -5,13 +5,22 @@
     $("#testconn").click(function(e) {
       e.preventDefault();
       var url = "/superset/testconn";
+      var csrf_token = "{{ csrf_token() }}";
+
+      $.ajaxSetup({
+        beforeSend: function(xhr, settings) {
+          if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
+            xhr.setRequestHeader("X-CSRFToken", csrf_token);
+          }
+        }
+      });
 
       var data = {};
       try{
         data = JSON.stringify({
           uri: $("#sqlalchemy_uri").val(),
           name: $('#database_name').val(),
-          extras: JSON.parse($("#extra").val())
+          extras: JSON.parse($("#extra").val()),
         })
       } catch(parse_error){
         alert("Malformed JSON in the extras field: " + parse_error);