Repository: getredash/redash

Issue:
An unauthorized (read only) user can create queries 
### Issue Summary

I created a user on the default group and set the data source capabilities to "Read only" on that group. Accessing as this brand new user, I really can't create a new query, but I can fork a existing one, and modify it as I want. I think this could be a security flaw.

### Steps to Reproduce

1. Installed with docker, connect to a data source (I created a MySQL one);
2. Create a few queries;
3. Create a user and assign him to the "default" group;
4. Set the permissions of the data source for the default group to read only;
5. Do login with the brand new user and click to list the queries;
6. Choice one of the queries and fork it.

I expect that a "read only" role should really be read only.

### Technical details:

* Redash Version: 1.0.3+b2850
* Browser/OS: Google Chrome 59/Fedora 25
* How did you install Redash: Docker with the suggested `docker-compose.production.yml`



Discussion:
Confirming that this issue exists on Redash Version: 1.0.3+b2866. Although the `Execute Query` and `Refresh Query` buttons are disabled, a read-only user can fork an existing query to create a new API key, and alter the refresh interval for the forked query. However, it does not appear that the altered query is executed, even when the refresh interval has elapsed.

Browser/OS: Google Chrome 59/Ubuntu 17.04
How did you install Redash: Docker with the suggested `docker-compose.production.yml`

Resolving patch:
diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py
--- a/redash/handlers/queries.py
+++ b/redash/handlers/queries.py
@@ -251,6 +251,7 @@ def post(self, query_id):
         Responds with created :ref:`query <query-response-label>` object.
         """
         query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+        require_access(query.data_source.groups, self.current_user, not_view_only)
         forked_query = query.fork(self.current_user)
         models.db.session.commit()
         return forked_query.to_dict(with_visualizations=True)


Test patch:
diff --git a/tests/handlers/test_queries.py b/tests/handlers/test_queries.py
--- a/tests/handlers/test_queries.py
+++ b/tests/handlers/test_queries.py
@@ -158,3 +158,21 @@ def test_refresh_query_you_dont_have_access_to(self):
         user = self.factory.create_user(group_ids=[group.id])
         response = self.make_request('post', self.path, user=user)
         self.assertEqual(403, response.status_code)
+
+
+class TestQueryForkResourcePost(BaseTestCase):
+    def test_forks_a_query(self):
+        ds = self.factory.create_data_source(group=self.factory.org.default_group, view_only=False)
+        query = self.factory.create_query(data_source=ds)
+
+        rv = self.make_request('post', '/api/queries/{}/fork'.format(query.id))
+
+        self.assertEqual(rv.status_code, 200)
+
+    def test_must_have_full_access_to_data_source(self):
+        ds = self.factory.create_data_source(group=self.factory.org.default_group, view_only=True)
+        query = self.factory.create_query(data_source=ds)
+
+        rv = self.make_request('post', '/api/queries/{}/fork'.format(query.id))
+
+        self.assertEqual(rv.status_code, 403)