Skip to content

Commit b32f30c

Browse files
committed
move validation to validate_* methods & return correct HTTP status codes
1 parent 44fb982 commit b32f30c

File tree

2 files changed

+75
-75
lines changed

2 files changed

+75
-75
lines changed

src/sentry/core/endpoints/organization_details.py

Lines changed: 73 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
from datetime import datetime, timedelta, timezone
66
from typing import TypedDict
77

8+
from django.core.exceptions import PermissionDenied
89
from django.db import models, router, transaction
910
from django.db.models.query_utils import DeferredAttribute
1011
from django.urls import reverse
1112
from django.utils import timezone as django_timezone
1213
from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_serializer
1314
from rest_framework import serializers, status
15+
from rest_framework.exceptions import NotFound
1416
from sentry_sdk import capture_exception
1517

1618
from bitfield.types import BitHandler
@@ -484,6 +486,26 @@ def validate_samplingMode(self, value):
484486

485487
return value
486488

489+
def validate_hasGranularReplayPermissions(self, value):
490+
self._validate_granular_replay_permissions()
491+
return value
492+
493+
def validate_replayAccessMembers(self, value):
494+
self._validate_granular_replay_permissions()
495+
return value
496+
497+
def _validate_granular_replay_permissions(self):
498+
organization = self.context["organization"]
499+
request = self.context["request"]
500+
501+
if not features.has("organizations:granular-replay-permissions", organization):
502+
raise NotFound("This feature is not enabled for your organization.")
503+
504+
if not request.access.has_scope("org:admin"):
505+
raise PermissionDenied(
506+
"You do not have permission to modify granular replay permissions."
507+
)
508+
487509
def validate(self, attrs):
488510
attrs = super().validate(attrs)
489511
if attrs.get("avatarType") == "upload":
@@ -598,85 +620,65 @@ def save(self, **kwargs):
598620
if trusted_relay_info is not None:
599621
self.save_trusted_relays(trusted_relay_info, changed_data, org)
600622

601-
if "hasGranularReplayPermissions" in data or "replayAccessMembers" in data:
602-
if not features.has("organizations:granular-replay-permissions", org):
603-
raise serializers.ValidationError(
604-
{
605-
"hasGranularReplayPermissions": "This feature is not enabled for your organization."
606-
}
607-
)
608-
609-
if not self.context["request"].access.has_scope("org:admin"):
610-
raise serializers.ValidationError(
611-
{
612-
"hasGranularReplayPermissions": "You do not have permission to modify granular replay permissions."
613-
}
614-
)
615-
616-
if "hasGranularReplayPermissions" in data:
617-
option_key = "sentry:granular-replay-permissions"
618-
new_value = data["hasGranularReplayPermissions"]
619-
620-
option_inst, created = OrganizationOption.objects.update_or_create(
621-
organization=org, key=option_key, defaults={"value": new_value}
623+
if "hasGranularReplayPermissions" in data:
624+
option_key = "sentry:granular-replay-permissions"
625+
new_value = data["hasGranularReplayPermissions"]
626+
option_inst, created = OrganizationOption.objects.update_or_create(
627+
organization=org, key=option_key, defaults={"value": new_value}
628+
)
629+
changed_data["hasGranularReplayPermissions"] = f"to {new_value}"
630+
631+
if "replayAccessMembers" in data:
632+
member_ids = data["replayAccessMembers"]
633+
if member_ids is None:
634+
member_ids = []
635+
current_member_ids = set(
636+
OrganizationMemberReplayAccess.objects.filter(organization=org).values_list(
637+
"organizationmember_id", flat=True
622638
)
623-
624-
changed_data["hasGranularReplayPermissions"] = f"to {new_value}"
625-
626-
if "replayAccessMembers" in data:
627-
member_ids = data["replayAccessMembers"]
628-
629-
if member_ids is None:
630-
member_ids = []
631-
632-
current_member_ids = set(
633-
OrganizationMemberReplayAccess.objects.filter(organization=org).values_list(
634-
"organizationmember_id", flat=True
639+
)
640+
new_member_ids = set(member_ids)
641+
642+
to_add = new_member_ids - current_member_ids
643+
to_remove = current_member_ids - new_member_ids
644+
if to_add:
645+
valid_member_ids = set(
646+
OrganizationMember.objects.filter(organization=org, id__in=to_add).values_list(
647+
"id", flat=True
635648
)
636649
)
637-
new_member_ids = set(member_ids)
638-
639-
to_add = new_member_ids - current_member_ids
640-
to_remove = current_member_ids - new_member_ids
641-
642-
if to_add:
643-
valid_member_ids = set(
644-
OrganizationMember.objects.filter(
645-
organization=org, id__in=to_add
646-
).values_list("id", flat=True)
650+
invalid_member_ids = to_add - valid_member_ids
651+
if invalid_member_ids:
652+
raise serializers.ValidationError(
653+
{
654+
"replayAccessMembers": f"Invalid organization member IDs: {sorted(invalid_member_ids)}"
655+
}
647656
)
648-
invalid_member_ids = to_add - valid_member_ids
649-
if invalid_member_ids:
650-
raise serializers.ValidationError(
651-
{
652-
"replayAccessMembers": f"Invalid organization member IDs: {sorted(invalid_member_ids)}"
653-
}
657+
658+
OrganizationMemberReplayAccess.objects.bulk_create(
659+
[
660+
OrganizationMemberReplayAccess(
661+
organization=org, organizationmember_id=member_id
654662
)
663+
for member_id in to_add
664+
],
665+
ignore_conflicts=True,
666+
)
655667

656-
OrganizationMemberReplayAccess.objects.bulk_create(
657-
[
658-
OrganizationMemberReplayAccess(
659-
organization=org, organizationmember_id=member_id
660-
)
661-
for member_id in to_add
662-
],
663-
ignore_conflicts=True,
664-
)
668+
if to_remove:
669+
OrganizationMemberReplayAccess.objects.filter(
670+
organization=org, organizationmember_id__in=to_remove
671+
).delete()
665672

673+
if to_add or to_remove:
674+
changes = []
675+
if to_add:
676+
changes.append(f"added {len(to_add)} member(s)")
666677
if to_remove:
667-
OrganizationMemberReplayAccess.objects.filter(
668-
organization=org, organizationmember_id__in=to_remove
669-
).delete()
670-
671-
if to_add or to_remove:
672-
changes = []
673-
if to_add:
674-
changes.append(f"added {len(to_add)} member(s)")
675-
if to_remove:
676-
changes.append(f"removed {len(to_remove)} member(s)")
677-
changed_data["replayAccessMembers"] = (
678-
f"{' and '.join(changes)} (total: {len(new_member_ids)} member(s) with access)"
679-
)
678+
changes.append(f"removed {len(to_remove)} member(s)")
679+
changed_data["replayAccessMembers"] = (
680+
f"{' and '.join(changes)} (total: {len(new_member_ids)} member(s) with access)"
681+
)
680682

681683
if "openMembership" in data:
682684
org.flags.allow_joinleave = data["openMembership"]

tests/sentry/core/endpoints/test_organization_details.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,8 +1517,7 @@ def test_granular_replay_permissions_flag_unset(self) -> None:
15171517

15181518
def test_granular_replay_permissions_flag_requires_feature(self) -> None:
15191519
data = {"hasGranularReplayPermissions": True}
1520-
response = self.get_error_response(self.organization.slug, **data, status_code=400)
1521-
assert "hasGranularReplayPermissions" in response.data
1520+
self.get_error_response(self.organization.slug, **data, status_code=404)
15221521

15231522
@with_feature("organizations:granular-replay-permissions")
15241523
def test_granular_replay_permissions_flag_requires_admin_scope(self) -> None:
@@ -1656,8 +1655,7 @@ def test_replay_access_members_requires_feature(self) -> None:
16561655
organization=self.organization, user=self.create_user(), role="member"
16571656
)
16581657
data = {"replayAccessMembers": [member1.id]}
1659-
response = self.get_error_response(self.organization.slug, **data, status_code=400)
1660-
assert "hasGranularReplayPermissions" in response.data
1658+
self.get_error_response(self.organization.slug, **data, status_code=404)
16611659

16621660
@with_feature("organizations:granular-replay-permissions")
16631661
def test_replay_access_members_requires_admin_scope(self) -> None:

0 commit comments

Comments
 (0)