Browse Source

ref(hc): Query changes in anticipation of removing cross silo fks (#46717)

Query side of https://github.com/getsentry/sentry/pull/45528/files

Goal is to deploy this, follow up with fixes, then land the above actual
breaking keys.

Front end changes are safe -- they are only type narrowing and can be
deployed separately safely, but putting them in this PR is convenient.
Zach Collins 1 year ago
parent
commit
e719ef61e3

+ 17 - 5
src/sentry/api/bases/sentryapps.py

@@ -178,14 +178,17 @@ class SentryAppPermission(SentryPermission):
         if not hasattr(request, "user") or not request.user:
             return False
 
-        self.determine_access(request, sentry_app.owner)
+        owner_app = organization_service.get_organization_by_id(
+            id=sentry_app.owner_id, user_id=request.user.id
+        )
+        self.determine_access(request, owner_app)
 
         if is_active_superuser(request):
             return True
 
         # if app is unpublished, user must be in the Org who owns the app.
         if not sentry_app.is_published:
-            if sentry_app.owner not in request.user.get_orgs():
+            if sentry_app.owner_id not in {o.id for o in request.user.get_orgs()}:
                 raise Http404
 
         # TODO(meredith): make a better way to allow for public
@@ -348,7 +351,10 @@ class SentryAppAuthorizationsPermission(SentryPermission):
         if not hasattr(request, "user") or not request.user:
             return False
 
-        self.determine_access(request, installation.organization)
+        installation_org_context = organization_service.get_organization_by_id(
+            id=installation.organization_id, user_id=request.user.id
+        )
+        self.determine_access(request, installation_org_context)
 
         if not request.user.is_sentry_app:
             return False
@@ -374,7 +380,10 @@ class SentryInternalAppTokenPermission(SentryPermission):
         if not hasattr(request, "user") or not request.user:
             return False
 
-        self.determine_access(request, sentry_app.owner)
+        owner_app = organization_service.get_organization_by_id(
+            id=sentry_app.owner_id, user_id=request.user.id
+        )
+        self.determine_access(request, owner_app)
 
         if is_active_superuser(request):
             return True
@@ -394,7 +403,10 @@ class SentryAppStatsPermission(SentryPermission):
         if not hasattr(request, "user") or not request.user:
             return False
 
-        self.determine_access(request, sentry_app.owner)
+        owner_app = organization_service.get_organization_by_id(
+            id=sentry_app.owner_id, user_id=request.user.id
+        )
+        self.determine_access(request, owner_app)
 
         if is_active_superuser(request):
             return True

+ 13 - 3
src/sentry/api/endpoints/integrations/sentry_apps/details.py

@@ -14,6 +14,7 @@ from sentry.api.serializers.rest_framework import SentryAppSerializer
 from sentry.constants import SentryAppStatus
 from sentry.mediators import InstallationNotifier
 from sentry.sentry_apps.apps import SentryAppUpdater
+from sentry.services.hybrid_cloud.organization import organization_service
 from sentry.utils import json
 from sentry.utils.audit import create_audit_entry
 
@@ -27,8 +28,17 @@ class SentryAppDetailsEndpoint(SentryAppBaseEndpoint):
 
     @catch_raised_errors
     def put(self, request: Request, sentry_app) -> Response:
-        if self._has_hook_events(request) and not features.has(
-            "organizations:integrations-event-hooks", sentry_app.owner, actor=request.user
+        owner_context = organization_service.get_organization_by_id(
+            id=sentry_app.owner_id, user_id=None
+        )
+        if (
+            owner_context
+            and self._has_hook_events(request)
+            and not features.has(
+                "organizations:integrations-event-hooks",
+                owner_context.organization,
+                actor=request.user,
+            )
         ):
 
             return Response(
@@ -76,7 +86,7 @@ class SentryAppDetailsEndpoint(SentryAppBaseEndpoint):
                     "user_id": request.user.id,
                     "sentry_app_id": sentry_app.id,
                     "sentry_app_name": sentry_app.name,
-                    "organization_id": sentry_app.owner.id,
+                    "organization_id": sentry_app.owner_id,
                     "error_message": error_message,
                 }
                 logger.info(name, extra=log_info)

+ 2 - 2
src/sentry/api/endpoints/integrations/sentry_apps/index.py

@@ -30,11 +30,11 @@ class SentryAppsEndpoint(SentryAppsBaseEndpoint):
         elif status == "unpublished":
             queryset = SentryApp.objects.filter(status=SentryAppStatus.UNPUBLISHED)
             if not is_active_superuser(request):
-                queryset = queryset.filter(owner__in=request.user.get_orgs())
+                queryset = queryset.filter(owner_id__in=[o.id for o in request.user.get_orgs()])
         elif status == "internal":
             queryset = SentryApp.objects.filter(status=SentryAppStatus.INTERNAL)
             if not is_active_superuser(request):
-                queryset = queryset.filter(owner__in=request.user.get_orgs())
+                queryset = queryset.filter(owner_id__in=[o.id for o in request.user.get_orgs()])
         else:
             if is_active_superuser(request):
                 queryset = SentryApp.objects.all()

+ 1 - 1
src/sentry/api/endpoints/integrations/sentry_apps/installation/index.py

@@ -53,7 +53,7 @@ class SentryAppInstallationsEndpoint(SentryAppInstallationsBaseEndpoint):
         slug = serializer.validated_data.get("slug")
         try:
             install = SentryAppInstallation.objects.get(
-                sentry_app__slug=slug, organization=organization
+                sentry_app__slug=slug, organization_id=organization.id
             )
         except SentryAppInstallation.DoesNotExist:
             install = SentryAppInstallationCreator(

+ 2 - 2
src/sentry/api/endpoints/integrations/sentry_apps/interaction.py

@@ -32,7 +32,7 @@ class SentryAppInteractionEndpoint(SentryAppBaseEndpoint, StatsMixin):
             model=tsdb.models.sentry_app_viewed,
             keys=[sentry_app.id],
             **self._parse_args(request),
-            tenant_ids={"organization_id": sentry_app.owner.id},
+            tenant_ids={"organization_id": sentry_app.owner_id},
         )[sentry_app.id]
 
         component_interactions = tsdb.get_range(
@@ -42,7 +42,7 @@ class SentryAppInteractionEndpoint(SentryAppBaseEndpoint, StatsMixin):
                 for component in sentry_app.components.all()
             ],
             **self._parse_args(request),
-            tenant_ids={"organization_id": sentry_app.owner.id},
+            tenant_ids={"organization_id": sentry_app.owner_id},
         )
 
         return Response(

+ 1 - 1
src/sentry/api/endpoints/integrations/sentry_apps/organization_sentry_apps.py

@@ -13,7 +13,7 @@ from sentry.models import SentryApp
 class OrganizationSentryAppsEndpoint(OrganizationEndpoint):
     @add_integration_platform_metric_tag
     def get(self, request: Request, organization) -> Response:
-        queryset = SentryApp.objects.filter(owner=organization, application__isnull=False)
+        queryset = SentryApp.objects.filter(owner_id=organization.id, application__isnull=False)
 
         if SentryAppStatus.as_int(request.GET.get("status")) is not None:
             queryset = queryset.filter(status=SentryAppStatus.as_int(request.GET.get("status")))

+ 7 - 2
src/sentry/api/endpoints/integrations/sentry_apps/publish_request.py

@@ -8,6 +8,7 @@ from sentry.constants import SentryAppStatus
 from sentry.models import SentryAppAvatar
 from sentry.models.avatars.sentry_app_avatar import SentryAppAvatarTypes
 from sentry.sentry_apps.apps import SentryAppUpdater
+from sentry.services.hybrid_cloud.organization import organization_service
 from sentry.utils import email
 
 
@@ -52,12 +53,16 @@ class SentryAppPublishRequestEndpoint(SentryAppBaseEndpoint):
             status=SentryAppStatus.PUBLISH_REQUEST_INPROGRESS_STR,
         ).run(user=request.user)
 
-        message = f"User {request.user.email} of organization {sentry_app.owner.slug} wants to publish {sentry_app.slug}\n"
+        org_context = organization_service.get_organization_by_id(
+            id=sentry_app.owner_id, user_id=None
+        )
+        org_slug = "<unknown>" if org_context is None else org_context.organization.slug
+        message = f"User {request.user.email} of organization {org_slug} wants to publish {sentry_app.slug}\n"
 
         for question_pair in request.data.get("questionnaire"):
             message += "\n\n>{}\n{}".format(question_pair["question"], question_pair["answer"])
 
-        subject = "Sentry Integration Publication Request from %s" % sentry_app.owner.slug
+        subject = "Sentry Integration Publication Request from %s" % org_slug
 
         email.send_mail(
             subject,

+ 0 - 3
src/sentry/api/endpoints/organization_auth_provider_details.py

@@ -28,7 +28,4 @@ class OrganizationAuthProviderDetailsEndpoint(OrganizationEndpoint):
             # configured, make sure we respond with a 20x
             return Response(status=status.HTTP_204_NO_CONTENT)
 
-        # cache organization so that we don't need to query for org when serializing
-        auth_provider.set_cached_field_value("organization", organization)
-
         return Response(serialize(auth_provider, request.user))

+ 0 - 5
src/sentry/api/endpoints/organization_member/details.py

@@ -20,7 +20,6 @@ from sentry.apidocs.constants import (
 from sentry.apidocs.parameters import GLOBAL_PARAMS
 from sentry.auth.superuser import is_active_superuser
 from sentry.models import (
-    AuthIdentity,
     AuthProvider,
     InviteStatus,
     Organization,
@@ -355,10 +354,6 @@ class OrganizationMemberDetailsEndpoint(OrganizationMemberEndpoint):
         audit_data = member.get_audit_log_data()
 
         with transaction.atomic():
-            AuthIdentity.objects.filter(
-                user=member.user, auth_provider__organization=organization
-            ).delete()
-
             # Delete instances of `UserOption` that are scoped to the projects within the
             # organization when corresponding member is removed from org
             proj_list = Project.objects.filter(organization=organization).values_list(

+ 2 - 2
src/sentry/api/endpoints/organization_member/team_details.py

@@ -108,9 +108,9 @@ class OrganizationMemberTeamDetailsEndpoint(OrganizationMemberEndpoint):
         if not created:
             return
 
-        requester = request.user if request.user != member.user else None
+        requester = request.user.id if request.user.id != member.user_id else None
         if requester:
-            omt.update(requester=requester)
+            omt.update(requester_id=requester)
 
         omt.send_request_email()
 

Some files were not shown because too many files changed in this diff