diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py
index bfc4371d08..f780e74aa5 100644
--- a/redash/handlers/query_results.py
+++ b/redash/handlers/query_results.py
@@ -328,7 +328,11 @@ def get(self, query_id=None, query_result_id=None, filetype="json"):
abort(404, message="No cached result found for this query.")
if query_result:
- require_access(query_result.data_source, self.current_user, view_only)
+ api_key = request.args.get("api_key")
+ if query and api_key in query.dashboard_api_keys:
+ pass # shared dashboard
+ else:
+ require_access(query_result.data_source, self.current_user, view_only)
if isinstance(self.current_user, models.ApiUser):
event = {
diff --git a/redash/serializers/query_result.py b/redash/serializers/query_result.py
index 1944d4cf8f..af1f05f3ed 100644
--- a/redash/serializers/query_result.py
+++ b/redash/serializers/query_result.py
@@ -74,7 +74,7 @@ def _get_column_lists(columns):
def serialize_query_result(query_result, is_api_user):
if is_api_user:
- publicly_needed_keys = ["data", "retrieved_at"]
+ publicly_needed_keys = ["data", "id", "retrieved_at"]
return project(query_result.to_dict(), publicly_needed_keys)
else:
return query_result.to_dict()
diff --git a/tests/handlers/test_query_results.py b/tests/handlers/test_query_results.py
index 1170d6941e..4e18d496ec 100644
--- a/tests/handlers/test_query_results.py
+++ b/tests/handlers/test_query_results.py
@@ -294,6 +294,36 @@ def test_access_with_query_api_key(self):
)
self.assertEqual(rv.status_code, 200)
+ def test_access_query_using_dashboard_api_key(self):
+ query = self.factory.create_query()
+ query_result = self.factory.create_query_result(query_text=query.query_text)
+
+ dashboard1 = self.factory.create_dashboard()
+ visualization1 = self.factory.create_visualization(query_rel=query)
+ self.factory.create_widget(visualization=visualization1, dashboard=dashboard1)
+ api_key = self.factory.create_api_key(object=dashboard1).api_key
+
+ rv = self.make_request(
+ "get",
+ "/api/queries/{}/results/{}.json?api_key={}".format(query.id, query_result.id, api_key),
+ user=False,
+ )
+ self.assertEqual(rv.status_code, 200)
+
+ def test_reject_query_using_unrelated_dashboard_api_key(self):
+ query = self.factory.create_query()
+ query_result = self.factory.create_query_result(query_text=query.query_text)
+
+ dashboard1 = self.factory.create_dashboard()
+ api_key = self.factory.create_api_key(object=dashboard1).api_key
+
+ rv = self.make_request(
+ "get",
+ "/api/queries/{}/results/{}.json?api_key={}".format(query.id, query_result.id, api_key),
+ user=False,
+ )
+ self.assertEqual(rv.status_code, 403)
+
def test_access_with_query_api_key_without_query_result_id(self):
ds = self.factory.create_data_source(group=self.factory.org.default_group, view_only=False)
query = self.factory.create_query()
diff --git a/tests/serializers/test_query_results.py b/tests/serializers/test_query_results.py
index b1a2443871..ad244a3629 100644
--- a/tests/serializers/test_query_results.py
+++ b/tests/serializers/test_query_results.py
@@ -32,7 +32,7 @@ def test_serializes_all_keys_for_authenticated_users(self):
def test_doesnt_serialize_sensitive_keys_for_unauthenticated_users(self):
query_result = self.factory.create_query_result(data={})
serialized = serialize_query_result(query_result, True)
- self.assertSetEqual(set(["data", "retrieved_at"]), set(serialized.keys()))
+ self.assertSetEqual(set(["data", "id", "retrieved_at"]), set(serialized.keys()))
class DsvSerializationTest(BaseTestCase):