Skip to content

Commit 788f96a

Browse files
authored
fix(api): Skip issue mutations without project access (#123108)
If an org doesn't have open team membership on (or generally has no projects), `get_projects` will return an empty list, and error when we try to reference projects inside of it. We should just return 204 in those situations instead. Fixes SENTRY-5P2G
1 parent 690b66a commit 788f96a

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

src/sentry/issues/endpoints/organization_group_index.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,8 +522,12 @@ def get(
522522
examples=IssueExamples.ORGANIZATION_GROUP_INDEX_PUT,
523523
)
524524
@track_slo_response("workflow")
525-
def put(self, request: Request, organization: Organization) -> Response[MutateIssueResponse]:
525+
def put(
526+
self, request: Request, organization: Organization
527+
) -> Response[MutateIssueResponse] | Response[None]:
526528
projects = self.get_projects(request, organization)
529+
if not projects:
530+
return Response(status=204)
527531

528532
search_fn = functools.partial(
529533
self._search,
@@ -568,6 +572,8 @@ def delete(
568572
self, request: Request, organization: Organization
569573
) -> Response[None] | Response[DetailResponse] | Response[ValidationErrorResponse]:
570574
projects = self.get_projects(request, organization)
575+
if not projects:
576+
return Response(status=204)
571577

572578
search_fn = functools.partial(
573579
self._search,

tests/sentry/issues/endpoints/test_organization_group_index.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3225,6 +3225,17 @@ def get_response(self, *args: Any, **kwargs: Any) -> Response:
32253225
def assertNoResolution(self, group: Group) -> None:
32263226
assert not GroupResolution.objects.filter(group=group).exists()
32273227

3228+
def test_no_accessible_projects(self) -> None:
3229+
organization = self.create_organization()
3230+
self.create_project(organization=organization)
3231+
user = self.create_user()
3232+
self.create_member(organization=organization, user=user, has_global_access=False)
3233+
self.login_as(user=user)
3234+
3235+
response = self.get_response(organization.slug, status="resolved")
3236+
3237+
assert response.status_code == 204
3238+
32283239
def test_global_resolve(self) -> None:
32293240
group1 = self.create_group(status=GroupStatus.RESOLVED)
32303241
group2 = self.create_group(status=GroupStatus.UNRESOLVED)
@@ -4654,6 +4665,17 @@ def assert_deleted_groups(self, groups: Sequence[Group]) -> None:
46544665
assert not Group.objects.filter(id=group.id).exists()
46554666
assert not GroupHash.objects.filter(group_id=group.id).exists()
46564667

4668+
def test_no_accessible_projects(self) -> None:
4669+
organization = self.create_organization()
4670+
self.create_project(organization=organization)
4671+
user = self.create_user()
4672+
self.create_member(organization=organization, user=user, has_global_access=False)
4673+
self.login_as(user=user)
4674+
4675+
response = self.get_response(organization.slug)
4676+
4677+
assert response.status_code == 204
4678+
46574679
@patch("sentry.eventstream.snuba.SnubaEventStream._send")
46584680
@patch("sentry.eventstream.snuba.datetime")
46594681
def test_delete_by_id(self, mock_datetime: MagicMock, mock_send: MagicMock) -> None:

0 commit comments

Comments
 (0)