You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by gr...@apache.org on 2019/07/29 23:23:07 UTC

[incubator-superset] branch master updated: [feature flag] Enforce csrf protection on explore_json endpoint (#7935)

This is an automated email from the ASF dual-hosted git repository.

graceguo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 06d547f  [feature flag] Enforce csrf protection on explore_json endpoint (#7935)
06d547f is described below

commit 06d547fbace346f79dc3d8beb1fa2bfcd9125890
Author: Grace Guo <gr...@airbnb.com>
AuthorDate: Mon Jul 29 16:22:47 2019 -0700

    [feature flag] Enforce csrf protection on explore_json endpoint (#7935)
    
    also added a section for featured flags in http://superset.incubator.apache.org/installation.html
---
 docs/installation.rst  | 24 +++++++++++++++++++++++-
 superset/config.py     |  3 ++-
 superset/views/base.py |  8 ++++++++
 superset/views/core.py | 11 +++++++++--
 4 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/docs/installation.rst b/docs/installation.rst
index d82f0f7..68bc5ce 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -667,7 +667,7 @@ DOMAIN SHARDING
 
 Chrome allows up to 6 open connections per domain at a time. When there are more
 than 6 slices in dashboard, a lot of time fetch requests are queued up and wait for
-next available socket. PR (`#5039 <https://github.com/apache/incubator-superset/pull/5039>`) adds domain sharding to Superset,
+next available socket. `PR 5039 <https://github.com/apache/incubator-superset/pull/5039>`_ adds domain sharding to Superset,
 and this feature will be enabled by configuration only (by default Superset
 doesn't allow cross-domain request).
 
@@ -1161,3 +1161,25 @@ Then we can add this two lines to ``superset_config.py``:
 
   from custom_sso_security_manager import CustomSsoSecurityManager
   CUSTOM_SECURITY_MANAGER = CustomSsoSecurityManager
+
+Feature Flags
+---------------------------
+
+Because of a wide variety of users, Superset has some features that are not enabled by default. For example, some users have stronger security restrictions, while some others may not. So Superset allow users to enable or disable some features by config. For feature owners, you can add optional functionalities in Superset, but will be only affected by a subset of users.
+
+You can enable or disable features with flag from ``superset_config.py``:
+
+.. code-block:: python
+
+     DEFAULT_FEATURE_FLAGS = {
+         'CLIENT_CACHE': False,
+         'ENABLE_EXPLORE_JSON_CSRF_PROTECTION': False
+     }
+
+Here is a list of flags and descriptions:
+
+* ENABLE_EXPLORE_JSON_CSRF_PROTECTION
+
+  * For some security concerns, you may need to enforce CSRF protection on all query request to explore_json endpoint. In Superset, we use `flask-csrf <https://sjl.bitbucket.io/flask-csrf/>`_ add csrf protection for all POST requests, but this protection doesn't apply to GET method.
+
+  * When ENABLE_EXPLORE_JSON_CSRF_PROTECTION is set to true, your users cannot make GET request to explore_json. The default value for this feature False (current behavior), explore_json accepts both GET and POST request. See `PR 7935 <https://github.com/apache/incubator-superset/pull/7935>`_ for more details.
diff --git a/superset/config.py b/superset/config.py
index b676e51..01bc16a 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -204,7 +204,8 @@ LANGUAGES = {
 # will result in combined feature flags of { 'FOO': True, 'BAR': True, 'BAZ': True }
 DEFAULT_FEATURE_FLAGS = {
     # Experimental feature introducing a client (browser) cache
-    "CLIENT_CACHE": False
+    "CLIENT_CACHE": False,
+    "ENABLE_EXPLORE_JSON_CSRF_PROTECTION": False,
 }
 
 # A function that receives a dict of all feature flags
diff --git a/superset/views/base.py b/superset/views/base.py
index c82db76..37ba999 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -32,6 +32,7 @@ from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
 from flask_wtf.form import FlaskForm
 import simplejson as json
+from werkzeug.exceptions import HTTPException
 from wtforms.fields.core import Field, UnboundField
 import yaml
 
@@ -134,6 +135,13 @@ def handle_api_exception(f):
                 stacktrace=utils.get_stacktrace(),
                 status=e.status,
             )
+        except HTTPException as e:
+            logging.exception(e)
+            return json_error_response(
+                utils.error_msg_from_exception(e),
+                stacktrace=traceback.format_exc(),
+                status=e.code,
+            )
         except Exception as e:
             logging.exception(e)
             return json_error_response(
diff --git a/superset/views/core.py b/superset/views/core.py
index 558a30c..eaf7891 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -53,6 +53,7 @@ from superset import (
     db,
     event_logger,
     get_feature_flags,
+    is_feature_enabled,
     results_backend,
     security_manager,
     sql_lab,
@@ -1047,12 +1048,18 @@ class Superset(BaseSupersetView):
         payload = viz_obj.get_payload()
         return data_payload_response(*viz_obj.payload_json_and_has_error(payload))
 
+    EXPLORE_JSON_METHODS = ["POST"]
+    if not is_feature_enabled("ENABLE_EXPLORE_JSON_CSRF_PROTECTION"):
+        EXPLORE_JSON_METHODS.append("GET")
+
     @event_logger.log_this
     @api
     @has_access_api
     @handle_api_exception
-    @expose("/explore_json/<datasource_type>/<datasource_id>/", methods=["GET", "POST"])
-    @expose("/explore_json/", methods=["GET", "POST"])
+    @expose(
+        "/explore_json/<datasource_type>/<datasource_id>/", methods=EXPLORE_JSON_METHODS
+    )
+    @expose("/explore_json/", methods=EXPLORE_JSON_METHODS)
     @etag_cache(CACHE_DEFAULT_TIMEOUT, check_perms=check_datasource_perms)
     def explore_json(self, datasource_type=None, datasource_id=None):
         """Serves all request that GET or POST form_data