Skip to content

Commit a02170e

Browse files
committed
move validation to validate_* methods & return correct HTTP status codes
1 parent cef6b26 commit a02170e

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
@@ -453,6 +455,26 @@ def validate_samplingMode(self, value):
453455

454456
return value
455457

458+
def validate_hasGranularReplayPermissions(self, value):
459+
self._validate_granular_replay_permissions()
460+
return value
461+
462+
def validate_replayAccessMembers(self, value):
463+
self._validate_granular_replay_permissions()
464+
return value
465+
466+
def _validate_granular_replay_permissions(self):
467+
organization = self.context["organization"]
468+
request = self.context["request"]
469+
470+
if not features.has("organizations:granular-replay-permissions", organization):
471+
raise NotFound("This feature is not enabled for your organization.")
472+
473+
if not request.access.has_scope("org:admin"):
474+
raise PermissionDenied(
475+
"You do not have permission to modify granular replay permissions."
476+
)
477+
456478
def validate(self, attrs):
457479
attrs = super().validate(attrs)
458480
if attrs.get("avatarType") == "upload":
@@ -567,85 +589,65 @@ def save(self, **kwargs):
567589
if trusted_relay_info is not None:
568590
self.save_trusted_relays(trusted_relay_info, changed_data, org)
569591

570-
if "hasGranularReplayPermissions" in data or "replayAccessMembers" in data:
571-
if not features.has("organizations:granular-replay-permissions", org):
572-
raise serializers.ValidationError(
573-
{
574-
"hasGranularReplayPermissions": "This feature is not enabled for your organization."
575-
}
576-
)
577-
578-
if not self.context["request"].access.has_scope("org:admin"):
579-
raise serializers.ValidationError(
580-
{
581-
"hasGranularReplayPermissions": "You do not have permission to modify granular replay permissions."
582-
}
583-
)
584-
585-
if "hasGranularReplayPermissions" in data:
586-
option_key = "sentry:granular-replay-permissions"
587-
new_value = data["hasGranularReplayPermissions"]
588-
589-
option_inst, created = OrganizationOption.objects.update_or_create(
590-
organization=org, key=option_key, defaults={"value": new_value}
592+
if "hasGranularReplayPermissions" in data:
593+
option_key = "sentry:granular-replay-permissions"
594+
new_value = data["hasGranularReplayPermissions"]
595+
option_inst, created = OrganizationOption.objects.update_or_create(
596+
organization=org, key=option_key, defaults={"value": new_value}
597+
)
598+
changed_data["hasGranularReplayPermissions"] = f"to {new_value}"
599+
600+
if "replayAccessMembers" in data:
601+
member_ids = data["replayAccessMembers"]
602+
if member_ids is None:
603+
member_ids = []
604+
current_member_ids = set(
605+
OrganizationMemberReplayAccess.objects.filter(organization=org).values_list(
606+
"organizationmember_id", flat=True
591607
)
592-
593-
changed_data["hasGranularReplayPermissions"] = f"to {new_value}"
594-
595-
if "replayAccessMembers" in data:
596-
member_ids = data["replayAccessMembers"]
597-
598-
if member_ids is None:
599-
member_ids = []
600-
601-
current_member_ids = set(
602-
OrganizationMemberReplayAccess.objects.filter(organization=org).values_list(
603-
"organizationmember_id", flat=True
608+
)
609+
new_member_ids = set(member_ids)
610+
611+
to_add = new_member_ids - current_member_ids
612+
to_remove = current_member_ids - new_member_ids
613+
if to_add:
614+
valid_member_ids = set(
615+
OrganizationMember.objects.filter(organization=org, id__in=to_add).values_list(
616+
"id", flat=True
604617
)
605618
)
606-
new_member_ids = set(member_ids)
607-
608-
to_add = new_member_ids - current_member_ids
609-
to_remove = current_member_ids - new_member_ids
610-
611-
if to_add:
612-
valid_member_ids = set(
613-
OrganizationMember.objects.filter(
614-
organization=org, id__in=to_add
615-
).values_list("id", flat=True)
619+
invalid_member_ids = to_add - valid_member_ids
620+
if invalid_member_ids:
621+
raise serializers.ValidationError(
622+
{
623+
"replayAccessMembers": f"Invalid organization member IDs: {sorted(invalid_member_ids)}"
624+
}
616625
)
617-
invalid_member_ids = to_add - valid_member_ids
618-
if invalid_member_ids:
619-
raise serializers.ValidationError(
620-
{
621-
"replayAccessMembers": f"Invalid organization member IDs: {sorted(invalid_member_ids)}"
622-
}
626+
627+
OrganizationMemberReplayAccess.objects.bulk_create(
628+
[
629+
OrganizationMemberReplayAccess(
630+
organization=org, organizationmember_id=member_id
623631
)
632+
for member_id in to_add
633+
],
634+
ignore_conflicts=True,
635+
)
624636

625-
OrganizationMemberReplayAccess.objects.bulk_create(
626-
[
627-
OrganizationMemberReplayAccess(
628-
organization=org, organizationmember_id=member_id
629-
)
630-
for member_id in to_add
631-
],
632-
ignore_conflicts=True,
633-
)
637+
if to_remove:
638+
OrganizationMemberReplayAccess.objects.filter(
639+
organization=org, organizationmember_id__in=to_remove
640+
).delete()
634641

642+
if to_add or to_remove:
643+
changes = []
644+
if to_add:
645+
changes.append(f"added {len(to_add)} member(s)")
635646
if to_remove:
636-
OrganizationMemberReplayAccess.objects.filter(
637-
organization=org, organizationmember_id__in=to_remove
638-
).delete()
639-
640-
if to_add or to_remove:
641-
changes = []
642-
if to_add:
643-
changes.append(f"added {len(to_add)} member(s)")
644-
if to_remove:
645-
changes.append(f"removed {len(to_remove)} member(s)")
646-
changed_data["replayAccessMembers"] = (
647-
f"{' and '.join(changes)} (total: {len(new_member_ids)} member(s) with access)"
648-
)
647+
changes.append(f"removed {len(to_remove)} member(s)")
648+
changed_data["replayAccessMembers"] = (
649+
f"{' and '.join(changes)} (total: {len(new_member_ids)} member(s) with access)"
650+
)
649651

650652
if "openMembership" in data:
651653
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)