Skip to content

Commit 1316eb4

Browse files
authored
feat(relay): API organization option for controlling ingestion through trusted relays only (#92632)
API for controling the organization option: `organizations:ingest-through-trusted-relays-only` which will allow ingestion of data coming from trusted relays only. UI part will be done as a separate pull request. Setting a value in project config will be done as a separate PR too. Part of: [TET-541: UI Toggle to enable ingestion through trusted relays only](https://linear.app/getsentry/issue/TET-541/ui-toggle-to-enable-ingestion-through-trusted-relays-only)
1 parent cef3c21 commit 1316eb4

File tree

4 files changed

+53
-1
lines changed

4 files changed

+53
-1
lines changed

src/sentry/api/endpoints/organization_details.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
GITHUB_COMMENT_BOT_DEFAULT,
5151
GITLAB_COMMENT_BOT_DEFAULT,
5252
HIDE_AI_FEATURES_DEFAULT,
53+
INGEST_THROUGH_TRUSTED_RELAYS_ONLY_DEFAULT,
5354
ISSUE_ALERTS_THREAD_DEFAULT,
5455
JOIN_REQUESTS_DEFAULT,
5556
LEGACY_RATE_LIMIT_OPTIONS,
@@ -108,7 +109,6 @@
108109
ERR_SSO_ENABLED = "Cannot require two-factor authentication with SSO enabled"
109110
ERR_3RD_PARTY_PUBLISHED_APP = "Cannot delete an organization that owns a published integration. Contact support if you need assistance."
110111
ERR_PLAN_REQUIRED = "A paid plan is required to enable this feature."
111-
112112
ORG_OPTIONS = (
113113
# serializer field name, option key name, type, default value
114114
(
@@ -235,6 +235,12 @@
235235
str,
236236
DEFAULT_AUTOFIX_AUTOMATION_TUNING_DEFAULT,
237237
),
238+
(
239+
"ingestThroughTrustedRelaysOnly",
240+
"sentry:ingest-through-trusted-relays-only",
241+
bool,
242+
INGEST_THROUGH_TRUSTED_RELAYS_ONLY_DEFAULT,
243+
),
238244
)
239245

240246
DELETION_STATUSES = frozenset(
@@ -302,6 +308,7 @@ class OrganizationSerializer(BaseOrganizationSerializer):
302308
required=False,
303309
help_text="The default automation tuning setting for new projects.",
304310
)
311+
ingestThroughTrustedRelaysOnly = serializers.BooleanField(required=False)
305312

306313
@cached_property
307314
def _has_legacy_rate_limits(self):
@@ -377,6 +384,18 @@ def validate_trustedRelays(self, value):
377384

378385
return value
379386

387+
def validate_ingestThroughTrustedRelaysOnly(self, value):
388+
organization = self.context["organization"]
389+
request = self.context["request"]
390+
if not features.has(
391+
"organizations:ingest-through-trusted-relays-only", organization, actor=request.user
392+
):
393+
# NOTE (vgrozdanic): For now allow access to this setting only to orgs with the feature flag enabled
394+
raise serializers.ValidationError(
395+
"Organization does not have the ingest through trusted relays only feature enabled."
396+
)
397+
return value
398+
380399
def validate_accountRateLimit(self, value):
381400
if not self._has_legacy_rate_limits:
382401
raise serializers.ValidationError(
@@ -659,6 +678,7 @@ def post_org_pending_deletion(
659678
"apdexThreshold",
660679
"genAIConsent",
661680
"defaultAutofixAutomationTuning",
681+
"ingestThroughTrustedRelaysOnly",
662682
]
663683
)
664684
class OrganizationDetailsPutSerializer(serializers.Serializer):

src/sentry/api/serializers/models/organization.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
GITHUB_COMMENT_BOT_DEFAULT,
4040
GITLAB_COMMENT_BOT_DEFAULT,
4141
HIDE_AI_FEATURES_DEFAULT,
42+
INGEST_THROUGH_TRUSTED_RELAYS_ONLY_DEFAULT,
4243
ISSUE_ALERTS_THREAD_DEFAULT,
4344
JOIN_REQUESTS_DEFAULT,
4445
METRIC_ALERTS_THREAD_DEFAULT,
@@ -513,6 +514,7 @@ class _DetailedOrganizationSerializerResponseOptional(OrganizationSerializerResp
513514
effectiveSampleRate: float
514515
planSampleRate: float
515516
desiredSampleRate: float
517+
ingestThroughTrustedRelaysOnly: bool
516518

517519

518520
@extend_schema_serializer(exclude_fields=["availableRoles"])
@@ -732,6 +734,12 @@ def serialize( # type: ignore[explicit-override, override]
732734
obj.get_option("sentry:sampling_mode", SAMPLING_MODE_DEFAULT)
733735
)
734736

737+
if features.has("organizations:ingest-through-trusted-relays-only", obj):
738+
context["ingestThroughTrustedRelaysOnly"] = obj.get_option(
739+
"sentry:ingest-through-trusted-relays-only",
740+
INGEST_THROUGH_TRUSTED_RELAYS_ONLY_DEFAULT,
741+
)
742+
735743
if access.role is not None:
736744
context["role"] = access.role # Deprecated
737745
context["orgRole"] = access.role
@@ -765,6 +773,7 @@ def serialize( # type: ignore[explicit-override, override]
765773
"quota",
766774
"rollbackEnabled",
767775
"streamlineOnly",
776+
"ingestThroughTrustedRelaysOnly",
768777
]
769778
)
770779
class DetailedOrganizationSerializerWithProjectsAndTeamsResponse(

src/sentry/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,7 @@ class InsightModules(Enum):
722722
SAMPLING_MODE_DEFAULT = "organization"
723723
ROLLBACK_ENABLED_DEFAULT = True
724724
DEFAULT_AUTOFIX_AUTOMATION_TUNING_DEFAULT = "off"
725+
INGEST_THROUGH_TRUSTED_RELAYS_ONLY_DEFAULT = False
725726

726727
# `sentry:events_member_admin` - controls whether the 'member' role gets the event:admin scope
727728
EVENTS_MEMBER_ADMIN_DEFAULT = True

tests/sentry/api/endpoints/test_organization_details.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,28 @@ def test_target_sample_rate_feature(self):
12751275
data = {"targetSampleRate": 0.1}
12761276
self.get_error_response(self.organization.slug, status_code=400, **data)
12771277

1278+
def test_ingest_through_trusted_relays_only_option(self):
1279+
# by default option is not set
1280+
assert not self.organization.get_option("sentry:ingest_through_trusted_relays_only")
1281+
1282+
with self.feature("organizations:ingest-through-trusted-relays-only"):
1283+
data = {"ingestThroughTrustedRelaysOnly": True}
1284+
self.get_success_response(self.organization.slug, **data)
1285+
assert self.organization.get_option("sentry:ingest-through-trusted-relays-only")
1286+
1287+
with self.feature({"organizations:ingest-through-trusted-relays-only": False}):
1288+
data = {"ingestThroughTrustedRelaysOnly": True}
1289+
self.get_error_response(self.organization.slug, status_code=400, **data)
1290+
1291+
@with_feature("organizations:ingest-through-trusted-relays-only")
1292+
def test_get_ingest_through_trusted_relays_only_option(self):
1293+
response = self.get_success_response(self.organization.slug)
1294+
assert response.data["ingestThroughTrustedRelaysOnly"] is False
1295+
1296+
def test_get_ingest_through_trusted_relays_only_option_without_feature(self):
1297+
response = self.get_success_response(self.organization.slug)
1298+
assert "ingestThroughTrustedRelaysOnly" not in response.data
1299+
12781300
@with_feature("organizations:dynamic-sampling-custom")
12791301
def test_target_sample_rate_range(self):
12801302
# low, within and high

0 commit comments

Comments
 (0)