Skip to content

Commit

Permalink
feat(permissions): Allow team admins to call delete debug file endpoi…
Browse files Browse the repository at this point in the history
…nt (#56148)

Frontend Changes: #56123

Allow team admins to delete debug files through the endpoint

Fixes #53919
  • Loading branch information
schew2381 authored Sep 13, 2023
1 parent 86b9b59 commit d9b917e
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 3 deletions.
13 changes: 10 additions & 3 deletions src/sentry/api/endpoints/debug_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.paginator import OffsetPaginator
from sentry.api.serializers import serialize
from sentry.auth.access import Access
from sentry.auth.superuser import is_active_superuser
from sentry.auth.system import is_system_auth
from sentry.constants import DEBUG_FILES_ROLE_DEFAULT, KNOWN_DIF_FORMATS
Expand All @@ -35,6 +36,7 @@
create_files_from_dif_zip,
)
from sentry.models.debugfile import ProguardArtifactRelease
from sentry.models.project import Project
from sentry.models.release import get_artifact_counts
from sentry.tasks.assemble import (
AssembleTask,
Expand Down Expand Up @@ -87,6 +89,12 @@ def has_download_permission(request, project):
return roles.get(current_role).priority >= roles.get(required_role).priority


def _has_delete_permission(access: Access, project: Project) -> bool:
if access.has_scope("project:write"):
return True
return access.has_project_scope(project, "project:write")


@region_silo_endpoint
class ProguardArtifactReleasesEndpoint(ProjectEndpoint):
publish_status = {
Expand Down Expand Up @@ -296,7 +304,7 @@ def on_results(difs: Sequence[ProjectDebugFile]):
on_results=on_results,
)

def delete(self, request: Request, project) -> Response:
def delete(self, request: Request, project: Project) -> Response:
"""
Delete a specific Project's Debug Information File
```````````````````````````````````````````````````
Expand All @@ -310,8 +318,7 @@ def delete(self, request: Request, project) -> Response:
:qparam string id: The id of the DIF to delete.
:auth: required
"""

if request.GET.get("id") and (request.access.has_scope("project:write")):
if request.GET.get("id") and _has_delete_permission(request.access, project):
with atomic_transaction(using=router.db_for_write(File)):
debug_file = (
ProjectDebugFile.objects.filter(id=request.GET.get("id"), project_id=project.id)
Expand Down
71 changes: 71 additions & 0 deletions tests/sentry/api/endpoints/test_debug_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,77 @@ def test_dsyms_search(self):
dsyms = response.data
assert len(dsyms) == 20

def test_dsyms_delete_as_team_admin(self):
project = self.create_project(name="foo")
self.login_as(user=self.user)

url = reverse(
"sentry-api-0-dsym-files",
kwargs={"organization_slug": project.organization.slug, "project_slug": project.slug},
)
response = self._upload_proguard(url, PROGUARD_UUID)

assert response.status_code == 201
assert len(response.data) == 1

url = reverse(
"sentry-api-0-associate-dsym-files",
kwargs={"organization_slug": project.organization.slug, "project_slug": project.slug},
)
response = self.client.post(
url,
{
"checksums": ["e6d3c5185dac63eddfdc1a5edfffa32d46103b44"],
"platform": "android",
"name": "MyApp",
"appId": "com.example.myapp",
"version": "1.0",
"build": "1",
},
format="json",
)

url = reverse(
"sentry-api-0-dsym-files",
kwargs={"organization_slug": project.organization.slug, "project_slug": project.slug},
)
response = self.client.get(url)
download_id = response.data[0]["id"]

assert response.status_code == 200

team_admin = self.create_user()
team_admin_without_access = self.create_user()

self.create_member(
user=team_admin,
organization=project.organization,
role="member",
)
self.create_member(
user=team_admin_without_access,
organization=project.organization,
role="member",
)
self.create_team_membership(user=team_admin, team=self.team, role="admin")
self.create_team_membership(
user=team_admin_without_access, team=self.create_team(), role="admin"
)

# Team admin without project access can't delete
self.login_as(team_admin_without_access)
response = self.client.delete(url + "?id=" + download_id)

assert response.status_code == 404, response.content
assert ProjectDebugFile.objects.count() == 1

# Team admin with project access can delete
self.login_as(team_admin)
response = self.client.delete(url + "?id=" + download_id)

assert response.status_code == 204, response.content
assert ProjectDebugFile.objects.count() == 0

def test_source_maps(self):
project = self.create_project(name="foo")

Expand Down

0 comments on commit d9b917e

Please sign in to comment.