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
14 changes: 12 additions & 2 deletions src/sentry/management/commands/generate_controlsilo_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.urls import URLPattern, URLResolver

from sentry.silo.base import SiloMode
from sentry.web.frontend.base import BaseView

# There are a handful of catchall routes
# that we don't want to handle in controlsilo
Expand Down Expand Up @@ -95,7 +96,7 @@ def handle(self, **options):
func = info.callable
if isinstance(func, functools.partial):
func = func.func
if hasattr(func, "view_class"):
elif hasattr(func, "view_class"):
func = func.view_class

if hasattr(func, "__name__"):
Expand All @@ -114,7 +115,16 @@ def handle(self, **options):
except ImportError as err:
raise CommandError(f"Could not load view in {module}: {err}")

if not hasattr(view_func, "silo_limit"):
# If a view/endpoint doesn't have a silo_limit it is likely a basic django view
# We have tests in tests/sentry/silo/test_base.py that ensure all views/endpoints
# have silo annotations on them. BaseView subclasses generally render HTML,
# and we don't need them in the controlsilo URL inventory as the UI doesn't
# fetch data from them.
if (
not hasattr(view_func, "silo_limit")
or isinstance(view_func, type)
and issubclass(view_func, BaseView)
):
continue

silo_limit = view_func.silo_limit
Expand Down
5 changes: 3 additions & 2 deletions src/sentry/testutils/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,8 +719,9 @@ def path_2fa(self):
return reverse("sentry-account-settings-security")

def enable_org_2fa(self, organization):
organization.flags.require_2fa = True
organization.save()
with assume_test_silo_mode(SiloMode.REGION):
organization.flags.require_2fa = True
organization.save()

def api_enable_org_2fa(self, organization, user):
self.login_as(user)
Expand Down
11 changes: 3 additions & 8 deletions src/sentry/web/frontend/react_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@
)
from sentry.utils.http import is_using_customer_domain, query_string
from sentry.web.client_config import get_client_config
from sentry.web.frontend.base import (
BaseView,
ControlSiloOrganizationView,
all_silo_view,
control_silo_view,
)
from sentry.web.frontend.base import BaseView, ControlSiloOrganizationView, control_silo_view
from sentry.web.helpers import render_to_response

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -206,7 +201,7 @@ def handle_react(

# While most of our HTML views are rendered in control silo,
# our tests frequently redirect to HTML views
@all_silo_view
@control_silo_view
class ReactPageView(ControlSiloOrganizationView, ReactMixin):
def handle_auth_required(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
# If user is a superuser (but not active, because otherwise this method would never be called)
Expand All @@ -221,7 +216,7 @@ def handle(self, request: HttpRequest, organization, **kwargs) -> HttpResponse:
return self.handle_react(request, organization=organization)


@all_silo_view
@control_silo_view
class GenericReactPageView(BaseView, ReactMixin):
def handle(self, request: HttpRequest, **kwargs) -> HttpResponse:
return self.handle_react(request, **kwargs)
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/web/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,7 @@
re_path(
r"^(?P<organization_slug>[^/]+)/projects/(?P<project_id_or_slug>[^/]+)/events/(?P<client_event_id>[^/]+)/$",
ProjectEventRedirect.as_view(),
name="sentry-project-event-redirect",
name="sentry-organization-project-event-redirect",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had two routes with this name 🫠

),
re_path(
r"^(?P<organization_slug>[^/]+)/api-keys/$",
Expand Down
44 changes: 0 additions & 44 deletions static/app/data/controlsiloUrlPatterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ const patterns: RegExp[] = [
new RegExp('^remote/heroku/resources(?:/[^/]+)?$'),
new RegExp('^remote/heroku/sso-login/?$'),
new RegExp('^remote/beacon/$'),
new RegExp('^remote/newsletter/unsubscribe/$'),
new RegExp('^remote/github-copilot/oauth/$'),
new RegExp('^remote/github-secret-scanning/$'),
new RegExp('^remote/github/marketplace/purchase/$'),
new RegExp('^remote/channel-provision/account/$'),
new RegExp('^remote/vercel/v1/installations/new/plans/$'),
new RegExp('^remote/vercel/v1/installations/[^/]+/plans/$'),
Expand All @@ -16,11 +13,7 @@ const patterns: RegExp[] = [
new RegExp('^remote/vercel/v1/installations/[^/]+/$'),
new RegExp('^remote/vercel/v1/installations/[^/]+/resources$'),
new RegExp('^remote/vercel/v1/installations/[^/]+/resources/[^/]+$'),
new RegExp('^remote/vercel/oauth$'),
new RegExp('^support/$'),
new RegExp('^identity/login/[^/]+/$'),
new RegExp('^orgredirect/try-business/$'),
new RegExp('^orgredirect/'),
new RegExp('^associate/complete/[^/]+/$'),
new RegExp('^associate/[^/]+/$'),
new RegExp('^api/0/staff-auth/$'),
Expand Down Expand Up @@ -54,7 +47,6 @@ const patterns: RegExp[] = [
new RegExp('^api/0/sponsored_account_request/$'),
new RegExp('^api/0/migrate_to_hosted/$'),
new RegExp('^api/0/sponsored_education_account/$'),
new RegExp('^organizations/[^/]+/documents/[^/]+/?$'),
new RegExp('^api/0/organizations/[^/]+/broadcasts/$'),
new RegExp('^api/0/billing-config/$'),
new RegExp('^api/0/auth-details/$'),
Expand Down Expand Up @@ -154,48 +146,24 @@ const patterns: RegExp[] = [
new RegExp('^api/0/internal/beacon/$'),
new RegExp('^api/0/internal/integration-proxy/$'),
new RegExp('^api/0/internal/notifications/registered-templates/$'),
new RegExp('^api/0/uptime-ips/$'),
new RegExp('^api/0/tempest-ips/$'),
new RegExp('^api/0/secret-scanning/github/$'),
new RegExp('^api/hooks/mailgun/inbound/'),
new RegExp('^auth-v2/'),
new RegExp('^oauth/authorize/$'),
new RegExp('^oauth/token/$'),
new RegExp('^oauth/userinfo/$'),
new RegExp('^saml/acs/[^/]+/$'),
new RegExp('^saml/sls/[^/]+/$'),
new RegExp('^saml/metadata/[^/]+/$'),
new RegExp('^auth/login/$'),
new RegExp('^auth/login/[^/]+/$'),
new RegExp('^auth/channel/[^/]+/[^/]+/$'),
new RegExp('^auth/link/[^/]+/$'),
new RegExp('^auth/2fa/$'),
new RegExp('^auth/2fa/u2fappid\\.json$'),
new RegExp('^auth/sso/$'),
new RegExp('^auth/logout/$'),
new RegExp('^auth/reactivate/$'),
new RegExp('^auth/register/$'),
new RegExp('^auth/close/$'),
new RegExp('^login-redirect/$'),
new RegExp('^account/sudo/$'),
new RegExp('^account/confirm-email/$'),
new RegExp('^account/confirm-email/[^/]+/[^/]+/$'),
new RegExp('^account/confirm-signed-email/[^/]+/$'),
new RegExp('^account/user-confirm/[^/]+/$'),
new RegExp('^account/recover/$'),
new RegExp('^account/recover/confirm/[^/]+/[^/]+/$'),
new RegExp('^account/relocation/reclaim/[^/]+/$'),
new RegExp('^account/settings/identities/associate/[^/]+/[^/]+/[^/]+/$'),
new RegExp('^account/settings/wizard/[^/]+/$'),
new RegExp('^account/settings/social/associate/complete/[^/]+/$'),
new RegExp('^account/settings/social/associate/[^/]+/$'),
new RegExp('^auth/sso/account/settings/social/associate/complete/[^/]+/$'),
new RegExp('^out/$'),
new RegExp('^settings/organization/auth/configure/$'),
new RegExp('^disabled-member/'),
new RegExp('^organizations/[^/]+/auth/configure/$'),
new RegExp('^organizations/[^/]+/integrations/[^/]+/setup/$'),
new RegExp('^organizations/[^/]+/disabled-member/$'),
new RegExp('^avatar/[^/]+/$'),
new RegExp('^sentry-app-avatar/[^/]+/$'),
new RegExp('^doc-integration-avatar/[^/]+/$'),
Expand All @@ -204,30 +172,18 @@ const patterns: RegExp[] = [
new RegExp('^extensions/jira/installed/$'),
new RegExp('^extensions/jira/uninstalled/$'),
new RegExp('^extensions/jira/search/[^/]+/[^/]+/$'),
new RegExp('^extensions/jira/configure/$'),
new RegExp('^extensions/jira/issue-details/[^/]+/$'),
new RegExp('^extensions/jira-server/search/[^/]+/[^/]+/$'),
new RegExp('^extensions/slack/link-identity/[^/]+/$'),
new RegExp('^extensions/slack/unlink-identity/[^/]+/$'),
new RegExp('^extensions/github/installation/[^/]+/$'),
new RegExp('^extensions/github/search/[^/]+/[^/]+/$'),
new RegExp('^extensions/gitlab/search/[^/]+/[^/]+/$'),
new RegExp('^extensions/vsts/search/[^/]+/[^/]+/$'),
new RegExp('^extensions/vsts/configure/$'),
new RegExp('^extensions/bitbucket/descriptor/$'),
new RegExp('^extensions/bitbucket/installed/$'),
new RegExp('^extensions/bitbucket/uninstalled/$'),
new RegExp('^extensions/bitbucket/search/[^/]+/[^/]+/$'),
new RegExp('^extensions/vercel/configure/$'),
new RegExp('^extensions/vercel/delete/$'),
new RegExp('^extensions/vercel/webhook/$'),
new RegExp('^extensions/msteams/configure/$'),
new RegExp('^extensions/msteams/link-identity/[^/]+/$'),
new RegExp('^extensions/msteams/unlink-identity/[^/]+/$'),
new RegExp('^extensions/discord/configure/$'),
new RegExp('^extensions/discord/link-identity/[^/]+/$'),
new RegExp('^extensions/discord/unlink-identity/[^/]+/$'),
new RegExp('^share/(?:group|issue)/[^/]+/$'),
];

export default patterns;
19 changes: 14 additions & 5 deletions tests/sentry/core/endpoints/test_organization_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
from sentry.silo.base import SiloMode
from sentry.testutils.cases import APITestCase, TwoFactorAPITestCase
from sentry.testutils.hybrid_cloud import HybridCloudTestMixin
from sentry.testutils.silo import assume_test_silo_mode, create_test_regions, region_silo_test
from sentry.testutils.silo import (
assume_test_silo_mode,
control_silo_test,
create_test_regions,
region_silo_test,
)
from sentry.users.models.authenticator import Authenticator
from sentry.utils.slug import ORG_SLUG_PATTERN

Expand Down Expand Up @@ -328,7 +333,7 @@ def test_streamline_only_is_true(self) -> None:


@region_silo_test(regions=create_test_regions("de", "us"))
class OrganizationsCreateInRegionTest(OrganizationIndexTest, HybridCloudTestMixin):
class OrganizationsCreateInRegionTest(OrganizationIndexTest):
method = "post"

@override_settings(SENTRY_MONOLITH_REGION="us", SENTRY_REGION="de")
Expand All @@ -348,7 +353,9 @@ def test_success(self) -> None:
assert mapping.region_name == "de"


@control_silo_test
class OrganizationIndex2faTest(TwoFactorAPITestCase):
# This is the HTML view, not an API endpoint
endpoint = "sentry-organization-home"

def setUp(self) -> None:
Expand All @@ -365,8 +372,7 @@ def test_preexisting_members_must_enable_2fa(self) -> None:
self.login_as(self.no_2fa_user)
self.assert_redirected_to_2fa()

with assume_test_silo_mode(SiloMode.CONTROL):
TotpInterface().enroll(self.no_2fa_user)
TotpInterface().enroll(self.no_2fa_user)
self.get_success_response(self.org_2fa.slug)

def test_new_member_must_enable_2fa(self) -> None:
Expand Down Expand Up @@ -396,7 +402,9 @@ def test_superuser_can_access_org_home(self) -> None:
self.get_success_response(self.org_2fa.slug)


@control_silo_test
class OrganizationIndexMemberLimitTest(APITestCase):
# This is a react view, not an API endpoint
endpoint = "sentry-organization-index"

def setup_user(self, is_superuser=False):
Expand All @@ -417,4 +425,5 @@ def test_member_limit_redirect(self) -> None:

def test_member_limit_superuser_no_redirect(self) -> None:
self.setup_user(is_superuser=True)
self.get_success_response(self.organization.slug, status_code=200)
response = self.get_success_response(self.organization.slug, status_code=200)
assert response.headers["Content-Type"] == "text/html"
19 changes: 13 additions & 6 deletions tests/sentry/middleware/test_customer_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@

from sentry.api.base import Endpoint
from sentry.middleware.customer_domain import CustomerDomainMiddleware
from sentry.silo.base import SiloMode
from sentry.testutils.cases import APITestCase, TestCase
from sentry.testutils.helpers import with_feature
from sentry.testutils.silo import all_silo_test, create_test_regions, no_silo_test
from sentry.testutils.silo import (
all_silo_test,
assume_test_silo_mode,
create_test_regions,
no_silo_test,
)
from sentry.web.frontend.auth_logout import AuthLogoutView


Expand Down Expand Up @@ -162,11 +168,12 @@ def test_billing_route(self) -> None:
self.login_as(user=non_staff_user)
self.create_organization(name="albertos-apples", owner=non_staff_user)

response = self.client.get(
"/settings/billing/checkout/",
data={"querystring": "value"},
follow=True,
)
with assume_test_silo_mode(SiloMode.CONTROL):
response = self.client.get(
"/settings/billing/checkout/",
data={"querystring": "value"},
follow=True,
)
assert response.status_code == 200
assert response.redirect_chain == [
("http://albertos-apples.testserver/settings/billing/checkout/?querystring=value", 302)
Expand Down
3 changes: 2 additions & 1 deletion tests/sentry/web/frontend/test_project_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def test_redirect_to_event(self) -> None:
self.assertRedirects(
resp,
f"http://testserver/organizations/{self.org.slug}/issues/{self.event.group_id}/events/{self.event.event_id}/",
fetch_redirect_response=False,
)

def test_event_not_found(self) -> None:
Expand Down Expand Up @@ -79,7 +80,7 @@ def test_redirect_to_event_customer_domain(self) -> None:
self.org.refresh_from_db()
resp = self.client.get(
reverse(
"sentry-project-event-redirect",
"sentry-organization-project-event-redirect",
args=[self.org.slug, self.project.slug, self.event.event_id],
)
)
Expand Down
Loading