Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/sentry/data_export/endpoints/data_export_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from sentry.apidocs.constants import RESPONSE_FORBIDDEN, RESPONSE_NOT_FOUND, RESPONSE_UNAUTHORIZED
from sentry.apidocs.parameters import GlobalParams
from sentry.apidocs.utils import inline_sentry_response_serializer
from sentry.auth.principal import owned_by_acting_user
from sentry.data_export.models import ExportedData
from sentry.models.organization import Organization
from sentry.models.project import Project
Expand Down Expand Up @@ -76,7 +77,7 @@ def get(
"""
try:
data_export = ExportedData.objects.get(
id=data_export_id, organization=organization, user_id=request.user.id
owned_by_acting_user(request.user), id=data_export_id, organization=organization
)
except ExportedData.DoesNotExist:
if ExportedData.objects.filter(id=data_export_id, organization=organization).exists():
Expand Down
10 changes: 8 additions & 2 deletions src/sentry/data_secrecy/api/waive_data_secrecy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import control_silo_endpoint
from sentry.api.bases.organization import ControlSiloOrganizationEndpoint, OrganizationPermission
from sentry.auth.principal import acting_user_id
from sentry.data_secrecy.cache import effective_grant_status_cache
from sentry.data_secrecy.logic import cache_effective_grant_status, data_access_grant_exists
from sentry.data_secrecy.models.data_access_grant import (
Expand Down Expand Up @@ -98,7 +99,10 @@ def post(
result = validator.validated_data

DataAccessGrant.create_data_access_grant(
organization.id, request.user.id, DataAccessGrant.GrantType.MANUAL, result["access_end"]
organization.id,
acting_user_id(request.user),
DataAccessGrant.GrantType.MANUAL,
result["access_end"],
)

cache_effective_grant_status(organization_id=organization.id)
Expand All @@ -123,7 +127,9 @@ def delete(
Revoke all active data secrecy waivers for an organization.
"""
DataAccessGrant.revoke_active_data_access_grants(
organization.id, request.user.id, DataAccessGrant.RevocationReason.MANUAL_REVOCATION
organization.id,
acting_user_id(request.user),
DataAccessGrant.RevocationReason.MANUAL_REVOCATION,
)

return Response(status=status.HTTP_204_NO_CONTENT)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sentry.api.base import cell_silo_endpoint
from sentry.api.bases.organization import OrganizationEndpoint, OrganizationUserReportsPermission
from sentry.api.utils import get_date_range_from_stats_period
from sentry.auth.principal import acting_user_id
from sentry.exceptions import InvalidParams
from sentry.feedback.lib.label_query import (
query_label_group_counts,
Expand Down Expand Up @@ -108,7 +109,9 @@ def get(self, request: Request, organization: Organization) -> Response:
{"detail": "AI categorization is not available for this organization."}, status=403
)

viewer_context = SeerViewerContext(organization_id=organization.id, user_id=request.user.id)
viewer_context = SeerViewerContext(
organization_id=organization.id, user_id=acting_user_id(request.user)
)

try:
start, end = get_date_range_from_stats_period(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from sentry.api.base import cell_silo_endpoint
from sentry.api.bases.organization import OrganizationEndpoint, OrganizationUserReportsPermission
from sentry.api.utils import get_date_range_from_stats_period
from sentry.auth.principal import acting_user_id
from sentry.exceptions import InvalidParams
from sentry.feedback.lib.seer_api import SummarizeFeedbacksRequest, make_summarize_feedbacks_request
from sentry.grouping.utils import hash_from_values
Expand Down Expand Up @@ -95,7 +96,9 @@ def get(self, request: Request, organization: Organization) -> Response:
{"detail": "AI summaries are not available for this organization."}, status=403
)

viewer_context = SeerViewerContext(organization_id=organization.id, user_id=request.user.id)
viewer_context = SeerViewerContext(
organization_id=organization.id, user_id=acting_user_id(request.user)
)

try:
start, end = get_date_range_from_stats_period(
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/flags/endpoints/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from sentry.api.paginator import OffsetPaginator
from sentry.api.serializers import Serializer, register, serialize
from sentry.auth.principal import acting_user_id, is_acting_user
from sentry.flags.models import FlagWebHookSigningSecretModel
from sentry.models.organization import Organization

Expand Down Expand Up @@ -95,7 +96,7 @@ def post(self, request: Request, organization: Organization) -> Response:
organization_id=organization.id
).get(provider=validator.validated_data["provider"])
# allow update access if the user created the secret
is_creator = request.user.id == secret.created_by
is_creator = is_acting_user(request.user, secret.created_by)
except FlagWebHookSigningSecretModel.DoesNotExist:
# anyone can post a new secret
is_creator = True
Expand All @@ -105,7 +106,7 @@ def post(self, request: Request, organization: Organization) -> Response:
organization=organization,
provider=validator.validated_data["provider"],
defaults={
"created_by": request.user.id,
"created_by": acting_user_id(request.user),
"date_added": datetime.now(tz=timezone.utc),
"provider": validator.validated_data["provider"],
"secret": validator.validated_data["secret"],
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/insights/endpoints/starred_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from sentry.apidocs.parameters import GlobalParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.auth.principal import require_user
from sentry.insights.models import InsightsStarredSegment
from sentry.models.organization import Organization
from sentry.utils.db import atomic_transaction
Expand Down Expand Up @@ -97,7 +98,7 @@ def post(
_, created = InsightsStarredSegment.objects.get_or_create(
organization=organization,
project_id=project.id,
user_id=request.user.id,
user_id=require_user(request).id,
segment_name=service_span_name,
)

Expand Down Expand Up @@ -162,7 +163,7 @@ def delete(

InsightsStarredSegment.objects.filter(
organization=organization,
user_id=request.user.id,
user_id=require_user(request).id,
project_id=project.id,
segment_name=service_span_name,
).delete()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sentry.apidocs import constants
from sentry.apidocs.parameters import GlobalParams, InvestigationParams
from sentry.apidocs.utils import inline_sentry_response_serializer
from sentry.auth.principal import acting_user_id
from sentry.investigations.endpoints.serializers import (
InvestigationDetailsSerializer,
InvestigationDetailsSerializerResponse,
Expand Down Expand Up @@ -138,7 +139,7 @@ def put(
archived = archive_investigation_with_orchestration(
investigation=investigation,
expected_version=expected_version,
actor_id=request.user.id,
actor_id=acting_user_id(request.user),
)
except Exception as error:
response = service_error(error)
Expand Down Expand Up @@ -204,7 +205,7 @@ def delete(
archive_investigation_with_orchestration(
investigation=investigation,
expected_version=validator.validated_data["investigation_version"],
actor_id=request.user.id,
actor_id=acting_user_id(request.user),
)
except Exception as error:
response = service_error(error)
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/middleware/superuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework.request import Request
from rest_framework.response import Response

from sentry.auth.principal import acting_user_id
from sentry.auth.superuser import Superuser, logger


Expand All @@ -27,7 +28,7 @@ def process_request(self, request: Request) -> None:
"url": request.build_absolute_uri(),
"method": request.method,
"ip_address": request.META["REMOTE_ADDR"],
"user_id": request.user.id,
"user_id": acting_user_id(request.user),
"user_email": self._extract_email(request),
},
)
Expand All @@ -47,7 +48,7 @@ def process_response(self, request: Request, response: Response) -> Response:
"superuser.superuser_access",
extra={
"superuser_token_id": su.token,
"user_id": request.user.id,
"user_id": acting_user_id(request.user),
"user_email": self._extract_email(request),
"su_org_accessed": org_slug,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
OrganizationEndpoint,
OrganizationReleasePermission,
)
from sentry.auth.principal import acting_user_id, owned_by_acting_user
from sentry.auth.staff import is_active_staff
from sentry.models.organization import Organization
from sentry.preprod.analytics import PreprodStatusCheckApprovalCreatedEvent
Expand Down Expand Up @@ -75,9 +76,9 @@ def post(self, request: Request, organization: Organization, artifact_id: str) -
# exists()+create() instead of get_or_create — no unique constraint on this model
# (see snapshots/tasks.py for rationale)
already_approved = PreprodComparisonApproval.objects.filter(
owned_by_acting_user(request.user, "approved_by_id"),
preprod_artifact=artifact,
preprod_feature_type=feature_type,
approved_by_id=request.user.id,
approval_status=PreprodComparisonApproval.ApprovalStatus.APPROVED,
).exists()

Expand All @@ -88,7 +89,7 @@ def post(self, request: Request, organization: Organization, artifact_id: str) -
PreprodComparisonApproval.objects.create(
preprod_artifact=artifact,
preprod_feature_type=feature_type,
approved_by_id=request.user.id,
approved_by_id=acting_user_id(request.user),
approval_status=PreprodComparisonApproval.ApprovalStatus.APPROVED,
approved_at=datetime.now(timezone.utc),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
as_validation_errors,
)
from sentry.apidocs.utils import inline_sentry_response_serializer
from sentry.auth.principal import acting_user_id
from sentry.constants import ALL_ACCESS_PROJECT_ID, ALL_ACCESS_PROJECTS_SLUG
from sentry.issues.action_log import (
action_context_scope,
Expand Down Expand Up @@ -586,7 +587,7 @@ def put(
)
fetch_commits = not commit_list
try:
release.set_refs(refs, request.user.id, fetch=fetch_commits)
release.set_refs(refs, acting_user_id(request.user), fetch=fetch_commits)
except InvalidRepository as e:
scope.set_tag("failure_reason", "InvalidRepository")
scope.set_attribute("failure_reason", "InvalidRepository")
Expand Down
4 changes: 1 addition & 3 deletions src/sentry/releases/endpoints/project_releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,7 @@ def post(

analytics.record(
ReleaseCreatedEvent(
user_id=acting_user_id(request.user)
if request.user and request.user.id
else None,
user_id=acting_user_id(request.user),
organization_id=project.organization_id,
project_ids=[project.id],
user_agent=request.META.get("HTTP_USER_AGENT", "")[:256],
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/replays/endpoints/project_replay_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from sentry.api.base import cell_silo_endpoint
from sentry.api.bases.project import ProjectPermission
from sentry.api.utils import default_start_end_dates, handle_query_errors
from sentry.auth.principal import acting_user_id
from sentry.models.project import Project
from sentry.replays.endpoints.project_replay_endpoint import ProjectReplayEndpoint
from sentry.replays.lib.seer_api import (
Expand Down Expand Up @@ -197,7 +198,7 @@ def get(self, request: Request, project: Project, replay_id: str) -> Response:
# Both the frontend and summary generation are gated by the same permissions.

viewer_context = SeerViewerContext(
organization_id=project.organization_id, user_id=request.user.id
organization_id=project.organization_id, user_id=acting_user_id(request.user)
)

# Request Seer for the state of the summary task.
Expand Down Expand Up @@ -321,7 +322,7 @@ def validate_iso_timestamp(timestamp: str | None) -> str | None:
start_request["temperature"] = temperature

viewer_context = SeerViewerContext(
organization_id=project.organization_id, user_id=request.user.id
organization_id=project.organization_id, user_id=acting_user_id(request.user)
)

return self._make_seer_start_request(start_request, viewer_context=viewer_context)
7 changes: 4 additions & 3 deletions src/sentry/replays/endpoints/project_replay_viewed_by.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from sentry.apidocs.examples.replay_examples import ReplayExamples
from sentry.apidocs.parameters import GlobalParams, ReplayParams
from sentry.apidocs.utils import inline_sentry_response_serializer
from sentry.auth.principal import acting_user_id
from sentry.auth.principal import acting_user_id, require_user
from sentry.models.project import Project
from sentry.replays.endpoints.project_replay_endpoint import ProjectReplayEndpoint
from sentry.replays.lib.kafka import publish_replay_event
Expand Down Expand Up @@ -127,7 +127,8 @@ def post(self, request: Request, project: Project, replay_id: str) -> Response:

replay_id = str(uuid.UUID(replay_id))

user_orgs = user_service.get_organizations(user_id=request.user.id)
user = require_user(request)
user_orgs = user_service.get_organizations(user_id=user.id)
if project.organization.id not in [org.id for org in user_orgs]:
# If the user is not in the same organization as the replay, we don't need to do anything.
return Response(status=204)
Expand Down Expand Up @@ -158,7 +159,7 @@ def post(self, request: Request, project: Project, replay_id: str) -> Response:
message = viewed_event(
project.id,
replay_id,
request.user.id,
user.id,
finished_at_ts,
)
publish_replay_event(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
)
from sentry.apidocs.parameters import GlobalParams
from sentry.apidocs.utils import inline_sentry_response_serializer
from sentry.auth.principal import acting_user_id
from sentry.hybridcloud.models.outbox import outbox_context
from sentry.organizations.services.organization import (
RpcOrganization,
Expand Down Expand Up @@ -324,7 +325,7 @@ def post(
scope_list=data["scopes"],
is_active=data.get("isActive", True),
all_projects=all_projects,
created_by_id=request.user.id,
created_by_id=acting_user_id(request.user),
)
ServiceAccountProject.objects.bulk_create(
ServiceAccountProject(service_account=service_account, project_id=project_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from sentry.api.serializers.rest_framework import CamelSnakeSerializer
from sentry.apidocs.constants import RESPONSE_NOT_FOUND, RESPONSE_UNAUTHORIZED
from sentry.apidocs.parameters import GlobalParams
from sentry.auth.principal import require_user
from sentry.constants import ObjectStatus
from sentry.issues.action_log import (
action_context_scope,
Expand Down Expand Up @@ -104,7 +105,7 @@ def post(self, request: Request, organization: Organization) -> Response:

qs = Project.objects.filter(
organization=organization,
teams__organizationmember__user_id=request.user.id,
teams__organizationmember__user_id=require_user(request).id,
status=ObjectStatus.ACTIVE,
).order_by("slug")

Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/auth/test_principal.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def _is_request_user_type_check(node: ast.AST) -> bool:


# Lower these as callers move off. Exact, so the baseline can't drift back up.
REQUEST_USER_ID_BASELINE = 202
REQUEST_USER_ID_BASELINE = 179
REQUEST_USER_TYPE_CHECK_BASELINE = 31


Expand Down
21 changes: 21 additions & 0 deletions tests/sentry/data_export/endpoints/test_data_export_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
from io import BytesIO
from unittest.mock import patch

from django.test import override_settings
from django.urls import reverse
from django.utils import timezone

from sentry.data_export.base import ExportQueryType, ExportStatus
from sentry.data_export.models import ExportedData
from sentry.models.files.file import File
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.features import with_feature
from sentry.viewer_context import Actor, ActorType, ViewerContext, encode_viewer_context


class DataExportDetailsTest(APITestCase):
Expand Down Expand Up @@ -131,6 +134,24 @@ def test_cannot_download_another_members_export(self) -> None:
response = self.client.get(f"{url}?download")
assert response.status_code == 404

@override_settings(SEER_API_SHARED_SECRET="service-account-secret")
@with_feature("organizations:service-accounts")
def test_service_account_cannot_access_members_export(self) -> None:
service_account = self.create_service_account(
organization=self.organization, scope_list=["event:read"]
)
viewer_context = encode_viewer_context(
ViewerContext(
actor=Actor(type=ActorType.SERVICE_ACCOUNT, actor_id=service_account.id),
organization_id=self.organization.id,
),
key="service-account-secret",
)

url = reverse(self.endpoint, args=[self.organization.slug, self.data_export.id])
response = self.client.get(url, HTTP_X_VIEWER_CONTEXT=viewer_context)
assert response.status_code == 404, response.content

def test_content_errors(self) -> None:
self.data_export = ExportedData.objects.create(
user_id=self.user.id,
Expand Down
Loading
Loading