From f8e2bc9eca596fc909614ee15ad45af9fada1b22 Mon Sep 17 00:00:00 2001 From: Eran Sandler Date: Sun, 12 Jul 2015 23:00:50 +0300 Subject: [PATCH 1/3] Added configurable CORS support for the Query Result API (to export data as JSON or CSV). Configuration is via an environment variable that is comma separated to include one or more domains (REDASH_QUERIES_RESULT_CORS) --- redash/controllers.py | 27 +++++++++++++++++++++++---- redash/settings.py | 2 ++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/redash/controllers.py b/redash/controllers.py index 7c845445ef..62ef29073d 100644 --- a/redash/controllers.py +++ b/redash/controllers.py @@ -488,6 +488,22 @@ def csv_response(query_result): headers.update(cache_headers) return make_response(s.getvalue(), 200, headers) + @staticmethod + def add_access_control_allow_origin_header(headers): + if 'Origin' in request.headers: + origin = request.headers['Origin'] + + if origin in settings.QUERIES_RESULT_CORS: + headers['Access-Control-Allow-Origin'] = origin + headers['Access-Control-Allow-Credentials'] = 'true' + if request.method == 'OPTIONS': + headers['Access-Control-Request-Method'] = 'GET, POST, PUT' + headers['Access-Control-Allow-Headers'] = 'Content-Type' + + @require_permission('view_query') + def options(self, query_id=None, query_result_id=None, filetype='json'): + self.add_access_control_allow_origin_header(request.headers) + @require_permission('view_query') def get(self, query_id=None, query_result_id=None, filetype='json'): if query_result_id is None and query_id is not None: @@ -517,9 +533,15 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): record_event.delay(event) + headers = request.headers + + if len(settings.QUERIES_RESULT_CORS) > 0: + self.add_access_control_allow_origin_header(headers) + if filetype == 'json': data = json.dumps({'query_result': query_result.to_dict()}, cls=utils.JSONEncoder) - return make_response(data, 200, cache_headers) + headers.update(cache_headers) + return make_response(data, 200, headers) else: return self.csv_response(query_result) @@ -559,6 +581,3 @@ def send_static(filename): if __name__ == '__main__': app.run(debug=True) - - - diff --git a/redash/settings.py b/redash/settings.py index 2bef79cda1..0da76761fe 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -81,6 +81,8 @@ def parse_boolean(str): CLIENT_SIDE_METRICS = parse_boolean(os.environ.get("REDASH_CLIENT_SIDE_METRICS", "false")) ANALYTICS = os.environ.get("REDASH_ANALYTICS", "") +QUERIES_RESULT_CORS = set(os.environ.get("REDASH_QUERIES_RESULT_CORS", "").split(",")) + # Query Runners QUERY_RUNNERS = array_from_string(os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join([ 'redash.query_runner.big_query', From 421470666ad810eed4d6db9ec85fe5b158ec200e Mon Sep 17 00:00:00 2001 From: Eran Sandler Date: Sun, 12 Jul 2015 23:06:00 +0300 Subject: [PATCH 2/3] use set_from_string. --- redash/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redash/settings.py b/redash/settings.py index 0da76761fe..1f3cf8c485 100644 --- a/redash/settings.py +++ b/redash/settings.py @@ -81,7 +81,7 @@ def parse_boolean(str): CLIENT_SIDE_METRICS = parse_boolean(os.environ.get("REDASH_CLIENT_SIDE_METRICS", "false")) ANALYTICS = os.environ.get("REDASH_ANALYTICS", "") -QUERIES_RESULT_CORS = set(os.environ.get("REDASH_QUERIES_RESULT_CORS", "").split(",")) +QUERIES_RESULT_CORS = set_from_string(os.environ.get("REDASH_QUERIES_RESULT_CORS", "")) # Query Runners QUERY_RUNNERS = array_from_string(os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join([ From 66084b1a3b6a08e4678e624d0f9da3a1c46e8c37 Mon Sep 17 00:00:00 2001 From: Eran Sandler Date: Sun, 12 Jul 2015 23:07:06 +0300 Subject: [PATCH 3/3] minor fixes --- redash/controllers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/redash/controllers.py b/redash/controllers.py index 62ef29073d..73fc066cfe 100644 --- a/redash/controllers.py +++ b/redash/controllers.py @@ -343,7 +343,7 @@ class QueryAPI(BaseResource): @require_permission('edit_query') def post(self, query_id): query = models.Query.get_by_id(query_id) - + query_def = request.get_json(force=True) for field in ['id', 'created_at', 'api_key', 'visualizations', 'latest_query_data', 'user', 'last_modified_by']: query_def.pop(field, None) @@ -395,7 +395,7 @@ def post(self): kwargs = request.get_json(force=True) kwargs['options'] = json.dumps(kwargs['options']) kwargs['query'] = kwargs.pop('query_id') - + vis = models.Visualization(**kwargs) vis.save() @@ -533,7 +533,7 @@ def get(self, query_id=None, query_result_id=None, filetype='json'): record_event.delay(event) - headers = request.headers + headers = {} if len(settings.QUERIES_RESULT_CORS) > 0: self.add_access_control_allow_origin_header(headers)