We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Member roles cannot delete own saved search
Users with their role as `Member` cannot delete/remove their own Saved Search: 

- Members can save a search query and it will only be shown to the Member that created it.
- Members cannot delete their own saved search.
- Other users cannot see another Member's saved search, not even Admins, Managers, or Owners. Since no one else can see another Member's saved search, Admins and above cannot delete them either.
- If a Member is updated to a different role, they cannot see their own saved searches unless they're back as a Member.

cc @getsentry/workflow (fyi @getsentry/cops) 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/sentry/api/endpoints/project_search_details.py`
Content:
```
1 from __future__ import absolute_import
2 
3 from rest_framework import serializers
4 from rest_framework.response import Response
5 
6 from sentry.api.bases.project import ProjectEndpoint, RelaxedSearchPermission
7 from sentry.api.exceptions import ResourceDoesNotExist
8 from sentry.api.serializers import serialize
9 from sentry.models import SavedSearch, SavedSearchUserDefault
10 
11 
12 class LimitedSavedSearchSerializer(serializers.Serializer):
13     isUserDefault = serializers.BooleanField(required=False)
14 
15 
16 class SavedSearchSerializer(serializers.Serializer):
17     name = serializers.CharField(max_length=128, required=True)
18     query = serializers.CharField(required=True)
19     isDefault = serializers.BooleanField(required=False)
20     isUserDefault = serializers.BooleanField(required=False)
21 
22 
23 class ProjectSearchDetailsEndpoint(ProjectEndpoint):
24     permission_classes = (RelaxedSearchPermission, )
25 
26     def get(self, request, project, search_id):
27         """
28         Retrieve a saved search
29 
30         Return details on an individual saved search.
31 
32             {method} {path}
33 
34         """
35         try:
36             search = SavedSearch.objects.get(
37                 project=project,
38                 id=search_id,
39             )
40         except SavedSearch.DoesNotExist:
41             raise ResourceDoesNotExist
42 
43         return Response(serialize(search, request.user))
44 
45     def put(self, request, project, search_id):
46         """
47         Update a saved search
48 
49         Update a saved search.
50 
51             {method} {path}
52             {{
53                 "name: "Unresolved",
54                 "query": "is:unresolved",
55                 "dateSavedSearchd": "2015-05-11T02:23:10Z"
56             }}
57 
58         """
59         try:
60             search = SavedSearch.objects.get(
61                 project=project,
62                 id=search_id,
63             )
64         except SavedSearch.DoesNotExist:
65             raise ResourceDoesNotExist
66 
67         has_team_scope = any(
68             request.access.has_team_scope(team, 'project:write') for team in project.teams.all()
69         )
70         if has_team_scope:
71             serializer = SavedSearchSerializer(data=request.DATA, partial=True)
72         else:
73             serializer = LimitedSavedSearchSerializer(data=request.DATA, partial=True)
74 
75         if not serializer.is_valid():
76             return Response(serializer.errors, status=400)
77 
78         result = serializer.object
79 
80         kwargs = {}
81         if result.get('name'):
82             kwargs['name'] = result['name']
83         if result.get('query'):
84             kwargs['query'] = result['query']
85         if result.get('isDefault'):
86             kwargs['is_default'] = result['isDefault']
87 
88         if kwargs:
89             search.update(**kwargs)
90 
91         if result.get('isDefault'):
92             SavedSearch.objects.filter(
93                 project=project,
94             ).exclude(id=search_id).update(is_default=False)
95 
96         if result.get('isUserDefault'):
97             SavedSearchUserDefault.objects.create_or_update(
98                 user=request.user, project=project, values={
99                     'savedsearch': search,
100                 }
101             )
102 
103         return Response(serialize(search, request.user))
104 
105     def delete(self, request, project, search_id):
106         """
107         Delete a saved search
108 
109         Permanently remove a saved search.
110 
111             {method} {path}
112 
113         """
114         try:
115             search = SavedSearch.objects.get(
116                 project=project,
117                 id=search_id,
118             )
119         except SavedSearch.DoesNotExist:
120             raise ResourceDoesNotExist
121 
122         search.delete()
123 
124         return Response(status=204)
125
```

Path: `src/sentry/api/bases/project.py`
Content:
```
1 from __future__ import absolute_import
2 
3 from rest_framework.response import Response
4 
5 from sentry import roles
6 from sentry.api.base import Endpoint
7 from sentry.api.exceptions import ResourceDoesNotExist, ProjectMoved
8 from sentry.app import raven
9 from sentry.auth.superuser import is_active_superuser
10 from sentry.models import OrganizationMember, Project, ProjectStatus, ProjectRedirect
11 
12 from .organization import OrganizationPermission
13 from .team import has_team_permission
14 
15 
16 class ProjectPermission(OrganizationPermission):
17     scope_map = {
18         'GET': ['project:read', 'project:write', 'project:admin'],
19         'POST': ['project:write', 'project:admin'],
20         'PUT': ['project:write', 'project:admin'],
21         'DELETE': ['project:admin'],
22     }
23 
24     def has_object_permission(self, request, view, project):
25         result = super(ProjectPermission,
26                        self).has_object_permission(request, view, project.organization)
27 
28         if not result:
29             return result
30 
31         if project.teams.exists():
32             return any(
33                 has_team_permission(request, team, self.scope_map) for team in project.teams.all()
34             )
35         elif request.user.is_authenticated():
36             # this is only for team-less projects
37             if is_active_superuser(request):
38                 return True
39             try:
40                 role = OrganizationMember.objects.filter(
41                     organization=project.organization,
42                     user=request.user,
43                 ).values_list('role', flat=True).get()
44             except OrganizationMember.DoesNotExist:
45                 # this should probably never happen?
46                 return False
47 
48             return roles.get(role).is_global
49 
50         return False
51 
52 
53 class StrictProjectPermission(ProjectPermission):
54     scope_map = {
55         'GET': ['project:write', 'project:admin'],
56         'POST': ['project:write', 'project:admin'],
57         'PUT': ['project:write', 'project:admin'],
58         'DELETE': ['project:admin'],
59     }
60 
61 
62 class ProjectReleasePermission(ProjectPermission):
63     scope_map = {
64         'GET': ['project:read', 'project:write', 'project:admin', 'project:releases'],
65         'POST': ['project:write', 'project:admin', 'project:releases'],
66         'PUT': ['project:write', 'project:admin', 'project:releases'],
67         'DELETE': ['project:admin', 'project:releases'],
68     }
69 
70 
71 class ProjectEventPermission(ProjectPermission):
72     scope_map = {
73         'GET': ['event:read', 'event:write', 'event:admin'],
74         'POST': ['event:write', 'event:admin'],
75         'PUT': ['event:write', 'event:admin'],
76         'DELETE': ['event:admin'],
77     }
78 
79 
80 class ProjectSettingPermission(ProjectPermission):
81     scope_map = {
82         'GET': ['project:read', 'project:write', 'project:admin'],
83         'POST': ['project:write', 'project:admin'],
84         'PUT': ['project:write', 'project:admin'],
85         'DELETE': ['project:write', 'project:admin'],
86     }
87 
88 
89 class RelaxedSearchPermission(ProjectPermission):
90     scope_map = {
91         'GET': ['project:read', 'project:write', 'project:admin'],
92         # members can do writes
93         'POST': ['project:write', 'project:admin', 'project:read'],
94         'PUT': ['project:write', 'project:admin', 'project:read'],
95         'DELETE': ['project:admin'],
96     }
97 
98 
99 class ProjectEndpoint(Endpoint):
100     permission_classes = (ProjectPermission, )
101 
102     def convert_args(self, request, organization_slug, project_slug, *args, **kwargs):
103         try:
104             project = Project.objects.filter(
105                 organization__slug=organization_slug,
106                 slug=project_slug,
107             ).select_related('organization').prefetch_related('teams').get()
108         except Project.DoesNotExist:
109             try:
110                 # Project may have been renamed
111                 redirect = ProjectRedirect.objects.select_related('project')
112                 redirect = redirect.get(
113                     organization__slug=organization_slug,
114                     redirect_slug=project_slug
115                 )
116 
117                 # get full path so that we keep query strings
118                 requested_url = request.get_full_path()
119                 new_url = requested_url.replace(
120                     'projects/%s/%s/' %
121                     (organization_slug, project_slug), 'projects/%s/%s/' %
122                     (organization_slug, redirect.project.slug))
123 
124                 # Resource was moved/renamed if the requested url is different than the new url
125                 if requested_url != new_url:
126                     raise ProjectMoved(new_url, redirect.project.slug)
127 
128                 # otherwise project doesn't exist
129                 raise ResourceDoesNotExist
130             except ProjectRedirect.DoesNotExist:
131                 raise ResourceDoesNotExist
132 
133         if project.status != ProjectStatus.VISIBLE:
134             raise ResourceDoesNotExist
135 
136         self.check_object_permissions(request, project)
137 
138         raven.tags_context({
139             'project': project.id,
140             'organization': project.organization_id,
141         })
142 
143         request._request.organization = project.organization
144 
145         kwargs['project'] = project
146         return (args, kwargs)
147 
148     def handle_exception(self, request, exc):
149         if isinstance(exc, ProjectMoved):
150             response = Response({
151                 'slug': exc.detail['extra']['slug'],
152                 'detail': exc.detail
153             }, status=exc.status_code)
154             response['Location'] = exc.detail['extra']['url']
155             return response
156         return super(ProjectEndpoint, self).handle_exception(request, exc)
157
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/sentry/api/bases/project.py b/src/sentry/api/bases/project.py
--- a/src/sentry/api/bases/project.py
+++ b/src/sentry/api/bases/project.py
@@ -92,7 +92,8 @@
         # members can do writes
         'POST': ['project:write', 'project:admin', 'project:read'],
         'PUT': ['project:write', 'project:admin', 'project:read'],
-        'DELETE': ['project:admin'],
+        # members can delete their own searches
+        'DELETE': ['project:read', 'project:write', 'project:admin'],
     }
 
 
diff --git a/src/sentry/api/endpoints/project_search_details.py b/src/sentry/api/endpoints/project_search_details.py
--- a/src/sentry/api/endpoints/project_search_details.py
+++ b/src/sentry/api/endpoints/project_search_details.py
@@ -119,6 +119,14 @@
         except SavedSearch.DoesNotExist:
             raise ResourceDoesNotExist
 
-        search.delete()
+        is_search_owner = request.user and request.user == search.owner
 
-        return Response(status=204)
+        if request.access.has_scope('project:write'):
+            if not search.owner or is_search_owner:
+                search.delete()
+                return Response(status=204)
+        elif is_search_owner:
+            search.delete()
+            return Response(status=204)
+
+        return Response(status=403)

{"golden_diff": "diff --git a/src/sentry/api/bases/project.py b/src/sentry/api/bases/project.py\n--- a/src/sentry/api/bases/project.py\n+++ b/src/sentry/api/bases/project.py\n@@ -92,7 +92,8 @@\n         # members can do writes\n         'POST': ['project:write', 'project:admin', 'project:read'],\n         'PUT': ['project:write', 'project:admin', 'project:read'],\n-        'DELETE': ['project:admin'],\n+        # members can delete their own searches\n+        'DELETE': ['project:read', 'project:write', 'project:admin'],\n     }\n \n \ndiff --git a/src/sentry/api/endpoints/project_search_details.py b/src/sentry/api/endpoints/project_search_details.py\n--- a/src/sentry/api/endpoints/project_search_details.py\n+++ b/src/sentry/api/endpoints/project_search_details.py\n@@ -119,6 +119,14 @@\n         except SavedSearch.DoesNotExist:\n             raise ResourceDoesNotExist\n \n-        search.delete()\n+        is_search_owner = request.user and request.user == search.owner\n \n-        return Response(status=204)\n+        if request.access.has_scope('project:write'):\n+            if not search.owner or is_search_owner:\n+                search.delete()\n+                return Response(status=204)\n+        elif is_search_owner:\n+            search.delete()\n+            return Response(status=204)\n+\n+        return Response(status=403)\n", "issue": "Member roles cannot delete own saved search\nUsers with their role as `Member` cannot delete/remove their own Saved Search: \r\n\r\n- Members can save a search query and it will only be shown to the Member that created it.\r\n- Members cannot delete their own saved search.\r\n- Other users cannot see another Member's saved search, not even Admins, Managers, or Owners. Since no one else can see another Member's saved search, Admins and above cannot delete them either.\r\n- If a Member is updated to a different role, they cannot see their own saved searches unless they're back as a Member.\r\n\r\ncc @getsentry/workflow (fyi @getsentry/cops) \n", "before_files": [{"content": "from __future__ import absolute_import\n\nfrom rest_framework import serializers\nfrom rest_framework.response import Response\n\nfrom sentry.api.bases.project import ProjectEndpoint, RelaxedSearchPermission\nfrom sentry.api.exceptions import ResourceDoesNotExist\nfrom sentry.api.serializers import serialize\nfrom sentry.models import SavedSearch, SavedSearchUserDefault\n\n\nclass LimitedSavedSearchSerializer(serializers.Serializer):\n    isUserDefault = serializers.BooleanField(required=False)\n\n\nclass SavedSearchSerializer(serializers.Serializer):\n    name = serializers.CharField(max_length=128, required=True)\n    query = serializers.CharField(required=True)\n    isDefault = serializers.BooleanField(required=False)\n    isUserDefault = serializers.BooleanField(required=False)\n\n\nclass ProjectSearchDetailsEndpoint(ProjectEndpoint):\n    permission_classes = (RelaxedSearchPermission, )\n\n    def get(self, request, project, search_id):\n        \"\"\"\n        Retrieve a saved search\n\n        Return details on an individual saved search.\n\n            {method} {path}\n\n        \"\"\"\n        try:\n            search = SavedSearch.objects.get(\n                project=project,\n                id=search_id,\n            )\n        except SavedSearch.DoesNotExist:\n            raise ResourceDoesNotExist\n\n        return Response(serialize(search, request.user))\n\n    def put(self, request, project, search_id):\n        \"\"\"\n        Update a saved search\n\n        Update a saved search.\n\n            {method} {path}\n            {{\n                \"name: \"Unresolved\",\n                \"query\": \"is:unresolved\",\n                \"dateSavedSearchd\": \"2015-05-11T02:23:10Z\"\n            }}\n\n        \"\"\"\n        try:\n            search = SavedSearch.objects.get(\n                project=project,\n                id=search_id,\n            )\n        except SavedSearch.DoesNotExist:\n            raise ResourceDoesNotExist\n\n        has_team_scope = any(\n            request.access.has_team_scope(team, 'project:write') for team in project.teams.all()\n        )\n        if has_team_scope:\n            serializer = SavedSearchSerializer(data=request.DATA, partial=True)\n        else:\n            serializer = LimitedSavedSearchSerializer(data=request.DATA, partial=True)\n\n        if not serializer.is_valid():\n            return Response(serializer.errors, status=400)\n\n        result = serializer.object\n\n        kwargs = {}\n        if result.get('name'):\n            kwargs['name'] = result['name']\n        if result.get('query'):\n            kwargs['query'] = result['query']\n        if result.get('isDefault'):\n            kwargs['is_default'] = result['isDefault']\n\n        if kwargs:\n            search.update(**kwargs)\n\n        if result.get('isDefault'):\n            SavedSearch.objects.filter(\n                project=project,\n            ).exclude(id=search_id).update(is_default=False)\n\n        if result.get('isUserDefault'):\n            SavedSearchUserDefault.objects.create_or_update(\n                user=request.user, project=project, values={\n                    'savedsearch': search,\n                }\n            )\n\n        return Response(serialize(search, request.user))\n\n    def delete(self, request, project, search_id):\n        \"\"\"\n        Delete a saved search\n\n        Permanently remove a saved search.\n\n            {method} {path}\n\n        \"\"\"\n        try:\n            search = SavedSearch.objects.get(\n                project=project,\n                id=search_id,\n            )\n        except SavedSearch.DoesNotExist:\n            raise ResourceDoesNotExist\n\n        search.delete()\n\n        return Response(status=204)\n", "path": "src/sentry/api/endpoints/project_search_details.py"}, {"content": "from __future__ import absolute_import\n\nfrom rest_framework.response import Response\n\nfrom sentry import roles\nfrom sentry.api.base import Endpoint\nfrom sentry.api.exceptions import ResourceDoesNotExist, ProjectMoved\nfrom sentry.app import raven\nfrom sentry.auth.superuser import is_active_superuser\nfrom sentry.models import OrganizationMember, Project, ProjectStatus, ProjectRedirect\n\nfrom .organization import OrganizationPermission\nfrom .team import has_team_permission\n\n\nclass ProjectPermission(OrganizationPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin'],\n        'POST': ['project:write', 'project:admin'],\n        'PUT': ['project:write', 'project:admin'],\n        'DELETE': ['project:admin'],\n    }\n\n    def has_object_permission(self, request, view, project):\n        result = super(ProjectPermission,\n                       self).has_object_permission(request, view, project.organization)\n\n        if not result:\n            return result\n\n        if project.teams.exists():\n            return any(\n                has_team_permission(request, team, self.scope_map) for team in project.teams.all()\n            )\n        elif request.user.is_authenticated():\n            # this is only for team-less projects\n            if is_active_superuser(request):\n                return True\n            try:\n                role = OrganizationMember.objects.filter(\n                    organization=project.organization,\n                    user=request.user,\n                ).values_list('role', flat=True).get()\n            except OrganizationMember.DoesNotExist:\n                # this should probably never happen?\n                return False\n\n            return roles.get(role).is_global\n\n        return False\n\n\nclass StrictProjectPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:write', 'project:admin'],\n        'POST': ['project:write', 'project:admin'],\n        'PUT': ['project:write', 'project:admin'],\n        'DELETE': ['project:admin'],\n    }\n\n\nclass ProjectReleasePermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin', 'project:releases'],\n        'POST': ['project:write', 'project:admin', 'project:releases'],\n        'PUT': ['project:write', 'project:admin', 'project:releases'],\n        'DELETE': ['project:admin', 'project:releases'],\n    }\n\n\nclass ProjectEventPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['event:read', 'event:write', 'event:admin'],\n        'POST': ['event:write', 'event:admin'],\n        'PUT': ['event:write', 'event:admin'],\n        'DELETE': ['event:admin'],\n    }\n\n\nclass ProjectSettingPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin'],\n        'POST': ['project:write', 'project:admin'],\n        'PUT': ['project:write', 'project:admin'],\n        'DELETE': ['project:write', 'project:admin'],\n    }\n\n\nclass RelaxedSearchPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin'],\n        # members can do writes\n        'POST': ['project:write', 'project:admin', 'project:read'],\n        'PUT': ['project:write', 'project:admin', 'project:read'],\n        'DELETE': ['project:admin'],\n    }\n\n\nclass ProjectEndpoint(Endpoint):\n    permission_classes = (ProjectPermission, )\n\n    def convert_args(self, request, organization_slug, project_slug, *args, **kwargs):\n        try:\n            project = Project.objects.filter(\n                organization__slug=organization_slug,\n                slug=project_slug,\n            ).select_related('organization').prefetch_related('teams').get()\n        except Project.DoesNotExist:\n            try:\n                # Project may have been renamed\n                redirect = ProjectRedirect.objects.select_related('project')\n                redirect = redirect.get(\n                    organization__slug=organization_slug,\n                    redirect_slug=project_slug\n                )\n\n                # get full path so that we keep query strings\n                requested_url = request.get_full_path()\n                new_url = requested_url.replace(\n                    'projects/%s/%s/' %\n                    (organization_slug, project_slug), 'projects/%s/%s/' %\n                    (organization_slug, redirect.project.slug))\n\n                # Resource was moved/renamed if the requested url is different than the new url\n                if requested_url != new_url:\n                    raise ProjectMoved(new_url, redirect.project.slug)\n\n                # otherwise project doesn't exist\n                raise ResourceDoesNotExist\n            except ProjectRedirect.DoesNotExist:\n                raise ResourceDoesNotExist\n\n        if project.status != ProjectStatus.VISIBLE:\n            raise ResourceDoesNotExist\n\n        self.check_object_permissions(request, project)\n\n        raven.tags_context({\n            'project': project.id,\n            'organization': project.organization_id,\n        })\n\n        request._request.organization = project.organization\n\n        kwargs['project'] = project\n        return (args, kwargs)\n\n    def handle_exception(self, request, exc):\n        if isinstance(exc, ProjectMoved):\n            response = Response({\n                'slug': exc.detail['extra']['slug'],\n                'detail': exc.detail\n            }, status=exc.status_code)\n            response['Location'] = exc.detail['extra']['url']\n            return response\n        return super(ProjectEndpoint, self).handle_exception(request, exc)\n", "path": "src/sentry/api/bases/project.py"}], "after_files": [{"content": "from __future__ import absolute_import\n\nfrom rest_framework import serializers\nfrom rest_framework.response import Response\n\nfrom sentry.api.bases.project import ProjectEndpoint, RelaxedSearchPermission\nfrom sentry.api.exceptions import ResourceDoesNotExist\nfrom sentry.api.serializers import serialize\nfrom sentry.models import SavedSearch, SavedSearchUserDefault\n\n\nclass LimitedSavedSearchSerializer(serializers.Serializer):\n    isUserDefault = serializers.BooleanField(required=False)\n\n\nclass SavedSearchSerializer(serializers.Serializer):\n    name = serializers.CharField(max_length=128, required=True)\n    query = serializers.CharField(required=True)\n    isDefault = serializers.BooleanField(required=False)\n    isUserDefault = serializers.BooleanField(required=False)\n\n\nclass ProjectSearchDetailsEndpoint(ProjectEndpoint):\n    permission_classes = (RelaxedSearchPermission, )\n\n    def get(self, request, project, search_id):\n        \"\"\"\n        Retrieve a saved search\n\n        Return details on an individual saved search.\n\n            {method} {path}\n\n        \"\"\"\n        try:\n            search = SavedSearch.objects.get(\n                project=project,\n                id=search_id,\n            )\n        except SavedSearch.DoesNotExist:\n            raise ResourceDoesNotExist\n\n        return Response(serialize(search, request.user))\n\n    def put(self, request, project, search_id):\n        \"\"\"\n        Update a saved search\n\n        Update a saved search.\n\n            {method} {path}\n            {{\n                \"name: \"Unresolved\",\n                \"query\": \"is:unresolved\",\n                \"dateSavedSearchd\": \"2015-05-11T02:23:10Z\"\n            }}\n\n        \"\"\"\n        try:\n            search = SavedSearch.objects.get(\n                project=project,\n                id=search_id,\n            )\n        except SavedSearch.DoesNotExist:\n            raise ResourceDoesNotExist\n\n        has_team_scope = any(\n            request.access.has_team_scope(team, 'project:write') for team in project.teams.all()\n        )\n        if has_team_scope:\n            serializer = SavedSearchSerializer(data=request.DATA, partial=True)\n        else:\n            serializer = LimitedSavedSearchSerializer(data=request.DATA, partial=True)\n\n        if not serializer.is_valid():\n            return Response(serializer.errors, status=400)\n\n        result = serializer.object\n\n        kwargs = {}\n        if result.get('name'):\n            kwargs['name'] = result['name']\n        if result.get('query'):\n            kwargs['query'] = result['query']\n        if result.get('isDefault'):\n            kwargs['is_default'] = result['isDefault']\n\n        if kwargs:\n            search.update(**kwargs)\n\n        if result.get('isDefault'):\n            SavedSearch.objects.filter(\n                project=project,\n            ).exclude(id=search_id).update(is_default=False)\n\n        if result.get('isUserDefault'):\n            SavedSearchUserDefault.objects.create_or_update(\n                user=request.user, project=project, values={\n                    'savedsearch': search,\n                }\n            )\n\n        return Response(serialize(search, request.user))\n\n    def delete(self, request, project, search_id):\n        \"\"\"\n        Delete a saved search\n\n        Permanently remove a saved search.\n\n            {method} {path}\n\n        \"\"\"\n        try:\n            search = SavedSearch.objects.get(\n                project=project,\n                id=search_id,\n            )\n        except SavedSearch.DoesNotExist:\n            raise ResourceDoesNotExist\n\n        is_search_owner = request.user and request.user == search.owner\n\n        if request.access.has_scope('project:write'):\n            if not search.owner or is_search_owner:\n                search.delete()\n                return Response(status=204)\n        elif is_search_owner:\n            search.delete()\n            return Response(status=204)\n\n        return Response(status=403)\n", "path": "src/sentry/api/endpoints/project_search_details.py"}, {"content": "from __future__ import absolute_import\n\nfrom rest_framework.response import Response\n\nfrom sentry import roles\nfrom sentry.api.base import Endpoint\nfrom sentry.api.exceptions import ResourceDoesNotExist, ProjectMoved\nfrom sentry.app import raven\nfrom sentry.auth.superuser import is_active_superuser\nfrom sentry.models import OrganizationMember, Project, ProjectStatus, ProjectRedirect\n\nfrom .organization import OrganizationPermission\nfrom .team import has_team_permission\n\n\nclass ProjectPermission(OrganizationPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin'],\n        'POST': ['project:write', 'project:admin'],\n        'PUT': ['project:write', 'project:admin'],\n        'DELETE': ['project:admin'],\n    }\n\n    def has_object_permission(self, request, view, project):\n        result = super(ProjectPermission,\n                       self).has_object_permission(request, view, project.organization)\n\n        if not result:\n            return result\n\n        if project.teams.exists():\n            return any(\n                has_team_permission(request, team, self.scope_map) for team in project.teams.all()\n            )\n        elif request.user.is_authenticated():\n            # this is only for team-less projects\n            if is_active_superuser(request):\n                return True\n            try:\n                role = OrganizationMember.objects.filter(\n                    organization=project.organization,\n                    user=request.user,\n                ).values_list('role', flat=True).get()\n            except OrganizationMember.DoesNotExist:\n                # this should probably never happen?\n                return False\n\n            return roles.get(role).is_global\n\n        return False\n\n\nclass StrictProjectPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:write', 'project:admin'],\n        'POST': ['project:write', 'project:admin'],\n        'PUT': ['project:write', 'project:admin'],\n        'DELETE': ['project:admin'],\n    }\n\n\nclass ProjectReleasePermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin', 'project:releases'],\n        'POST': ['project:write', 'project:admin', 'project:releases'],\n        'PUT': ['project:write', 'project:admin', 'project:releases'],\n        'DELETE': ['project:admin', 'project:releases'],\n    }\n\n\nclass ProjectEventPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['event:read', 'event:write', 'event:admin'],\n        'POST': ['event:write', 'event:admin'],\n        'PUT': ['event:write', 'event:admin'],\n        'DELETE': ['event:admin'],\n    }\n\n\nclass ProjectSettingPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin'],\n        'POST': ['project:write', 'project:admin'],\n        'PUT': ['project:write', 'project:admin'],\n        'DELETE': ['project:write', 'project:admin'],\n    }\n\n\nclass RelaxedSearchPermission(ProjectPermission):\n    scope_map = {\n        'GET': ['project:read', 'project:write', 'project:admin'],\n        # members can do writes\n        'POST': ['project:write', 'project:admin', 'project:read'],\n        'PUT': ['project:write', 'project:admin', 'project:read'],\n        # members can delete their own searches\n        'DELETE': ['project:read', 'project:write', 'project:admin'],\n    }\n\n\nclass ProjectEndpoint(Endpoint):\n    permission_classes = (ProjectPermission, )\n\n    def convert_args(self, request, organization_slug, project_slug, *args, **kwargs):\n        try:\n            project = Project.objects.filter(\n                organization__slug=organization_slug,\n                slug=project_slug,\n            ).select_related('organization').prefetch_related('teams').get()\n        except Project.DoesNotExist:\n            try:\n                # Project may have been renamed\n                redirect = ProjectRedirect.objects.select_related('project')\n                redirect = redirect.get(\n                    organization__slug=organization_slug,\n                    redirect_slug=project_slug\n                )\n\n                # get full path so that we keep query strings\n                requested_url = request.get_full_path()\n                new_url = requested_url.replace(\n                    'projects/%s/%s/' %\n                    (organization_slug, project_slug), 'projects/%s/%s/' %\n                    (organization_slug, redirect.project.slug))\n\n                # Resource was moved/renamed if the requested url is different than the new url\n                if requested_url != new_url:\n                    raise ProjectMoved(new_url, redirect.project.slug)\n\n                # otherwise project doesn't exist\n                raise ResourceDoesNotExist\n            except ProjectRedirect.DoesNotExist:\n                raise ResourceDoesNotExist\n\n        if project.status != ProjectStatus.VISIBLE:\n            raise ResourceDoesNotExist\n\n        self.check_object_permissions(request, project)\n\n        raven.tags_context({\n            'project': project.id,\n            'organization': project.organization_id,\n        })\n\n        request._request.organization = project.organization\n\n        kwargs['project'] = project\n        return (args, kwargs)\n\n    def handle_exception(self, request, exc):\n        if isinstance(exc, ProjectMoved):\n            response = Response({\n                'slug': exc.detail['extra']['slug'],\n                'detail': exc.detail\n            }, status=exc.status_code)\n            response['Location'] = exc.detail['extra']['url']\n            return response\n        return super(ProjectEndpoint, self).handle_exception(request, exc)\n", "path": "src/sentry/api/bases/project.py"}]}