prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Redash Permissions not working for some use cases
  ### Issue Summary
  
  Currently, when query owner grants permission to another user for a query, the user is still unable to perform the following tasks:
  
  * change data source
  * schedule the query
  * add and save new visualisation
  
  I believe the user should have the ability to do all the things that the owner could do once permission has been granted.
  
  ### Technical details:
  
  * Redash Version: 1.0.3
  * Browser/OS: Chrome
  * How did you install Redash: AWS using the AMI
  
  
  </issue>
  <code>
  [start of redash/handlers/visualizations.py]
  1 import json
  2 from flask import request
  3 
  4 from redash import models
  5 from redash.permissions import require_permission, require_admin_or_owner
  6 from redash.handlers.base import BaseResource, get_object_or_404
  7 
  8 
  9 class VisualizationListResource(BaseResource):
  10     @require_permission('edit_query')
  11     def post(self):
  12         kwargs = request.get_json(force=True)
  13 
  14         query = get_object_or_404(models.Query.get_by_id_and_org, kwargs.pop('query_id'), self.current_org)
  15         require_admin_or_owner(query.user_id)
  16 
  17         kwargs['options'] = json.dumps(kwargs['options'])
  18         kwargs['query_rel'] = query
  19 
  20         vis = models.Visualization(**kwargs)
  21         models.db.session.add(vis)
  22         models.db.session.commit()
  23         d = vis.to_dict(with_query=False)
  24         return d
  25 
  26 
  27 class VisualizationResource(BaseResource):
  28     @require_permission('edit_query')
  29     def post(self, visualization_id):
  30         vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)
  31         require_admin_or_owner(vis.query_rel.user_id)
  32 
  33         kwargs = request.get_json(force=True)
  34         if 'options' in kwargs:
  35             kwargs['options'] = json.dumps(kwargs['options'])
  36 
  37         kwargs.pop('id', None)
  38         kwargs.pop('query_id', None)
  39 
  40         self.update_model(vis, kwargs)
  41         d = vis.to_dict(with_query=False)
  42         models.db.session.commit()
  43         return d
  44 
  45     @require_permission('edit_query')
  46     def delete(self, visualization_id):
  47         vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)
  48         require_admin_or_owner(vis.query_rel.user_id)
  49         models.db.session.delete(vis)
  50         models.db.session.commit()
  51
  [end of redash/handlers/visualizations.py]
  [start of redash/permissions.py]
  1 from flask_login import current_user
  2 from flask_restful import abort
  3 import functools
  4 from funcy import flatten
  5 
  6 view_only = True
  7 not_view_only = False
  8 
  9 ACCESS_TYPE_VIEW = 'view'
  10 ACCESS_TYPE_MODIFY = 'modify'
  11 ACCESS_TYPE_DELETE = 'delete'
  12 
  13 ACCESS_TYPES = (ACCESS_TYPE_VIEW, ACCESS_TYPE_MODIFY, ACCESS_TYPE_DELETE)
  14 
  15 
  16 def has_access(object_groups, user, need_view_only):
  17     if 'admin' in user.permissions:
  18         return True
  19 
  20     matching_groups = set(object_groups.keys()).intersection(user.group_ids)
  21 
  22     if not matching_groups:
  23         return False
  24 
  25     required_level = 1 if need_view_only else 2
  26 
  27     group_level = 1 if all(flatten([object_groups[group] for group in matching_groups])) else 2
  28 
  29     return required_level <= group_level
  30 
  31 
  32 def require_access(object_groups, user, need_view_only):
  33     if not has_access(object_groups, user, need_view_only):
  34         abort(403)
  35 
  36 
  37 class require_permissions(object):
  38     def __init__(self, permissions):
  39         self.permissions = permissions
  40 
  41     def __call__(self, fn):
  42         @functools.wraps(fn)
  43         def decorated(*args, **kwargs):
  44             has_permissions = current_user.has_permissions(self.permissions)
  45 
  46             if has_permissions:
  47                 return fn(*args, **kwargs)
  48             else:
  49                 abort(403)
  50 
  51         return decorated
  52 
  53 
  54 def require_permission(permission):
  55     return require_permissions((permission,))
  56 
  57 
  58 def require_admin(fn):
  59     return require_permission('admin')(fn)
  60 
  61 
  62 def require_super_admin(fn):
  63     return require_permission('super_admin')(fn)
  64 
  65 
  66 def has_permission_or_owner(permission, object_owner_id):
  67     return int(object_owner_id) == current_user.id or current_user.has_permission(permission)
  68 
  69 
  70 def is_admin_or_owner(object_owner_id):
  71     return has_permission_or_owner('admin', object_owner_id)
  72 
  73 
  74 def require_permission_or_owner(permission, object_owner_id):
  75     if not has_permission_or_owner(permission, object_owner_id):
  76         abort(403)
  77 
  78 
  79 def require_admin_or_owner(object_owner_id):
  80     if not is_admin_or_owner(object_owner_id):
  81         abort(403, message="You don't have permission to edit this resource.")
  82 
  83 
  84 def can_modify(obj, user):
  85     return is_admin_or_owner(obj.user_id) or user.has_access(obj, ACCESS_TYPE_MODIFY)
  86 
  87 
  88 def require_object_modify_permission(obj, user):
  89     if not can_modify(obj, user):
  90         abort(403)
  91
  [end of redash/permissions.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/redash/handlers/visualizations.py b/redash/handlers/visualizations.py
  --- a/redash/handlers/visualizations.py
  +++ b/redash/handlers/visualizations.py
  @@ -1,9 +1,12 @@
   import json
  +
   from flask import request
   
   from redash import models
  -from redash.permissions import require_permission, require_admin_or_owner
   from redash.handlers.base import BaseResource, get_object_or_404
  +from redash.permissions import (require_admin_or_owner,
  +                                require_object_modify_permission,
  +                                require_permission)
   
   
   class VisualizationListResource(BaseResource):
  @@ -12,7 +15,7 @@
           kwargs = request.get_json(force=True)
   
           query = get_object_or_404(models.Query.get_by_id_and_org, kwargs.pop('query_id'), self.current_org)
  -        require_admin_or_owner(query.user_id)
  +        require_object_modify_permission(query, self.current_user)
   
           kwargs['options'] = json.dumps(kwargs['options'])
           kwargs['query_rel'] = query
  @@ -28,7 +31,7 @@
       @require_permission('edit_query')
       def post(self, visualization_id):
           vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)
  -        require_admin_or_owner(vis.query_rel.user_id)
  +        require_object_modify_permission(vis.query_rel, self.current_user)
   
           kwargs = request.get_json(force=True)
           if 'options' in kwargs:
  @@ -45,6 +48,6 @@
       @require_permission('edit_query')
       def delete(self, visualization_id):
           vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)
  -        require_admin_or_owner(vis.query_rel.user_id)
  +        require_object_modify_permission(vis.query_rel, self.current_user)
           models.db.session.delete(vis)
           models.db.session.commit()
  diff --git a/redash/permissions.py b/redash/permissions.py
  --- a/redash/permissions.py
  +++ b/redash/permissions.py
  @@ -1,6 +1,7 @@
  +import functools
  +
   from flask_login import current_user
   from flask_restful import abort
  -import functools
   from funcy import flatten
   
   view_only = True
  
verification_info: {"golden_diff": "diff --git a/redash/handlers/visualizations.py b/redash/handlers/visualizations.py\n--- a/redash/handlers/visualizations.py\n+++ b/redash/handlers/visualizations.py\n@@ -1,9 +1,12 @@\n import json\n+\n from flask import request\n \n from redash import models\n-from redash.permissions import require_permission, require_admin_or_owner\n from redash.handlers.base import BaseResource, get_object_or_404\n+from redash.permissions import (require_admin_or_owner,\n+                                require_object_modify_permission,\n+                                require_permission)\n \n \n class VisualizationListResource(BaseResource):\n@@ -12,7 +15,7 @@\n         kwargs = request.get_json(force=True)\n \n         query = get_object_or_404(models.Query.get_by_id_and_org, kwargs.pop('query_id'), self.current_org)\n-        require_admin_or_owner(query.user_id)\n+        require_object_modify_permission(query, self.current_user)\n \n         kwargs['options'] = json.dumps(kwargs['options'])\n         kwargs['query_rel'] = query\n@@ -28,7 +31,7 @@\n     @require_permission('edit_query')\n     def post(self, visualization_id):\n         vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)\n-        require_admin_or_owner(vis.query_rel.user_id)\n+        require_object_modify_permission(vis.query_rel, self.current_user)\n \n         kwargs = request.get_json(force=True)\n         if 'options' in kwargs:\n@@ -45,6 +48,6 @@\n     @require_permission('edit_query')\n     def delete(self, visualization_id):\n         vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)\n-        require_admin_or_owner(vis.query_rel.user_id)\n+        require_object_modify_permission(vis.query_rel, self.current_user)\n         models.db.session.delete(vis)\n         models.db.session.commit()\ndiff --git a/redash/permissions.py b/redash/permissions.py\n--- a/redash/permissions.py\n+++ b/redash/permissions.py\n@@ -1,6 +1,7 @@\n+import functools\n+\n from flask_login import current_user\n from flask_restful import abort\n-import functools\n from funcy import flatten\n \n view_only = True\n", "issue": "Redash Permissions not working for some use cases\n### Issue Summary\r\n\r\nCurrently, when query owner grants permission to another user for a query, the user is still unable to perform the following tasks:\r\n\r\n* change data source\r\n* schedule the query\r\n* add and save new visualisation\r\n\r\nI believe the user should have the ability to do all the things that the owner could do once permission has been granted.\r\n\r\n### Technical details:\r\n\r\n* Redash Version: 1.0.3\r\n* Browser/OS: Chrome\r\n* How did you install Redash: AWS using the AMI\r\n\n", "before_files": [{"content": "import json\nfrom flask import request\n\nfrom redash import models\nfrom redash.permissions import require_permission, require_admin_or_owner\nfrom redash.handlers.base import BaseResource, get_object_or_404\n\n\nclass VisualizationListResource(BaseResource):\n    @require_permission('edit_query')\n    def post(self):\n        kwargs = request.get_json(force=True)\n\n        query = get_object_or_404(models.Query.get_by_id_and_org, kwargs.pop('query_id'), self.current_org)\n        require_admin_or_owner(query.user_id)\n\n        kwargs['options'] = json.dumps(kwargs['options'])\n        kwargs['query_rel'] = query\n\n        vis = models.Visualization(**kwargs)\n        models.db.session.add(vis)\n        models.db.session.commit()\n        d = vis.to_dict(with_query=False)\n        return d\n\n\nclass VisualizationResource(BaseResource):\n    @require_permission('edit_query')\n    def post(self, visualization_id):\n        vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)\n        require_admin_or_owner(vis.query_rel.user_id)\n\n        kwargs = request.get_json(force=True)\n        if 'options' in kwargs:\n            kwargs['options'] = json.dumps(kwargs['options'])\n\n        kwargs.pop('id', None)\n        kwargs.pop('query_id', None)\n\n        self.update_model(vis, kwargs)\n        d = vis.to_dict(with_query=False)\n        models.db.session.commit()\n        return d\n\n    @require_permission('edit_query')\n    def delete(self, visualization_id):\n        vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)\n        require_admin_or_owner(vis.query_rel.user_id)\n        models.db.session.delete(vis)\n        models.db.session.commit()\n", "path": "redash/handlers/visualizations.py"}, {"content": "from flask_login import current_user\nfrom flask_restful import abort\nimport functools\nfrom funcy import flatten\n\nview_only = True\nnot_view_only = False\n\nACCESS_TYPE_VIEW = 'view'\nACCESS_TYPE_MODIFY = 'modify'\nACCESS_TYPE_DELETE = 'delete'\n\nACCESS_TYPES = (ACCESS_TYPE_VIEW, ACCESS_TYPE_MODIFY, ACCESS_TYPE_DELETE)\n\n\ndef has_access(object_groups, user, need_view_only):\n    if 'admin' in user.permissions:\n        return True\n\n    matching_groups = set(object_groups.keys()).intersection(user.group_ids)\n\n    if not matching_groups:\n        return False\n\n    required_level = 1 if need_view_only else 2\n\n    group_level = 1 if all(flatten([object_groups[group] for group in matching_groups])) else 2\n\n    return required_level <= group_level\n\n\ndef require_access(object_groups, user, need_view_only):\n    if not has_access(object_groups, user, need_view_only):\n        abort(403)\n\n\nclass require_permissions(object):\n    def __init__(self, permissions):\n        self.permissions = permissions\n\n    def __call__(self, fn):\n        @functools.wraps(fn)\n        def decorated(*args, **kwargs):\n            has_permissions = current_user.has_permissions(self.permissions)\n\n            if has_permissions:\n                return fn(*args, **kwargs)\n            else:\n                abort(403)\n\n        return decorated\n\n\ndef require_permission(permission):\n    return require_permissions((permission,))\n\n\ndef require_admin(fn):\n    return require_permission('admin')(fn)\n\n\ndef require_super_admin(fn):\n    return require_permission('super_admin')(fn)\n\n\ndef has_permission_or_owner(permission, object_owner_id):\n    return int(object_owner_id) == current_user.id or current_user.has_permission(permission)\n\n\ndef is_admin_or_owner(object_owner_id):\n    return has_permission_or_owner('admin', object_owner_id)\n\n\ndef require_permission_or_owner(permission, object_owner_id):\n    if not has_permission_or_owner(permission, object_owner_id):\n        abort(403)\n\n\ndef require_admin_or_owner(object_owner_id):\n    if not is_admin_or_owner(object_owner_id):\n        abort(403, message=\"You don't have permission to edit this resource.\")\n\n\ndef can_modify(obj, user):\n    return is_admin_or_owner(obj.user_id) or user.has_access(obj, ACCESS_TYPE_MODIFY)\n\n\ndef require_object_modify_permission(obj, user):\n    if not can_modify(obj, user):\n        abort(403)\n", "path": "redash/permissions.py"}]}