Skip to content

Commit cb103d0

Browse files
ref: handle AnonymousUser in some views (#91884)
this is needed to upgrade django-stubs. I think in every place here these are superfluous but are helpful to teach the type checker what is going on <!-- Describe your PR here. -->
1 parent bba930f commit cb103d0

29 files changed

+117
-12
lines changed

src/sentry/api/endpoints/accept_project_transfer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from sentry.models.organization import Organization
2020
from sentry.models.project import Project
2121
from sentry.signals import project_transferred
22+
from sentry.users.models.user import User
2223
from sentry.utils import metrics
2324
from sentry.utils.signing import unsign
2425

@@ -36,7 +37,7 @@ class AcceptProjectTransferEndpoint(Endpoint):
3637
authentication_classes = (SessionAuthentication,)
3738
permission_classes = (SentryIsAuthenticated,)
3839

39-
def get_validated_data(self, data, user):
40+
def get_validated_data(self, data, user: User):
4041
try:
4142
data = unsign(force_str(data), salt=SALT)
4243
except SignatureExpired:
@@ -64,6 +65,9 @@ def get_validated_data(self, data, user):
6465

6566
@sudo_required
6667
def get(self, request: Request) -> Response:
68+
if not request.user.is_authenticated:
69+
return Response(status=400)
70+
6771
try:
6872
data = request.GET["data"]
6973
except KeyError:
@@ -92,6 +96,9 @@ def get(self, request: Request) -> Response:
9296

9397
@sudo_required
9498
def post(self, request: Request) -> Response:
99+
if not request.user.is_authenticated:
100+
return Response(status=400)
101+
95102
try:
96103
data = request.data["data"]
97104
except KeyError:

src/sentry/api/endpoints/api_authorizations.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class ApiAuthorizationsEndpoint(Endpoint):
2727
permission_classes = (SentryIsAuthenticated,)
2828

2929
def get(self, request: Request) -> Response:
30+
if not request.user.is_authenticated:
31+
return Response(status=400)
32+
3033
queryset = ApiAuthorization.objects.filter(
3134
user_id=request.user.id, application__status=ApiApplicationStatus.active
3235
).select_related("application")
@@ -40,6 +43,9 @@ def get(self, request: Request) -> Response:
4043
)
4144

4245
def delete(self, request: Request) -> Response:
46+
if not request.user.is_authenticated:
47+
return Response(status=400)
48+
4349
authorization = request.data.get("authorization")
4450
if not authorization:
4551
return Response({"authorization": ""}, status=400)

src/sentry/api/endpoints/api_tokens.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ def get_appropriate_user_id(request: Request) -> int:
3939
For GET endpoints, the GET dict is used.
4040
For all others, the DATA dict is used.
4141
"""
42+
assert request.user.is_authenticated
43+
4244
# Get the user id for the user that made the current request as a baseline default
4345
user_id = request.user.id
4446
if has_elevated_mode(request):

src/sentry/api/endpoints/assistant.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ class AssistantEndpoint(Endpoint):
6565

6666
def get(self, request: Request) -> Response:
6767
"""Return all the guides with a 'seen' attribute if it has been 'viewed' or 'dismissed'."""
68+
if not request.user.is_authenticated:
69+
return Response(status=400)
70+
6871
seen_ids = set(
6972
AssistantActivity.objects.filter(user_id=request.user.id).values_list(
7073
"guide_id", flat=True
@@ -85,6 +88,9 @@ def put(self, request: Request):
8588
'useful' (optional): true / false,
8689
}
8790
"""
91+
if not request.user.is_authenticated:
92+
return Response(status=400)
93+
8894
serializer = AssistantSerializer(data=request.data)
8995

9096
if not serializer.is_valid():

src/sentry/api/endpoints/broadcast_details.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ def get(self, request: Request, broadcast_id) -> Response:
6060
return self._serialize_response(request, broadcast)
6161

6262
def put(self, request: Request, broadcast_id) -> Response:
63+
if not request.user.is_authenticated:
64+
return Response(status=400)
65+
6366
broadcast = self._get_broadcast(request, broadcast_id)
6467
validator = self._get_validator(request)(data=request.data, partial=True)
6568
if not validator.is_valid():

src/sentry/api/endpoints/broadcast_index.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ def get(
132132
paginator_cls=paginator_cls,
133133
)
134134

135-
def put(self, request: Request):
135+
def put(self, request: Request) -> Response:
136+
if not request.user.is_authenticated:
137+
return Response(status=401)
138+
136139
validator = BroadcastValidator(data=request.data, partial=True)
137140
if not validator.is_valid():
138141
return self.respond(validator.errors, status=400)
@@ -146,9 +149,6 @@ def put(self, request: Request):
146149
queryset = queryset.filter(id__in=ids)
147150

148151
if result.get("hasSeen"):
149-
if not request.user.is_authenticated:
150-
return self.respond(status=401)
151-
152152
if ids:
153153
unseen_queryset = queryset
154154
else:
@@ -166,6 +166,8 @@ def put(self, request: Request):
166166
return self.respond(result)
167167

168168
def post(self, request: Request) -> Response:
169+
if not request.user.is_authenticated:
170+
return Response(status=400)
169171
if not request.access.has_permission("broadcasts.admin"):
170172
return self.respond(status=401)
171173

src/sentry/api/endpoints/group_autofix_setup_check.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ def get(self, request: Request, group: Group) -> Response:
9999
"""
100100
Checks if we are able to run Autofix on the given group.
101101
"""
102+
if not request.user.is_authenticated:
103+
return Response(status=400)
104+
102105
org: Organization = request.organization
103106

104107
integration_check = None

src/sentry/api/endpoints/organization_dashboards.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ def get(self, request: Request, organization) -> Response:
9696
"""
9797
Retrieve a list of custom dashboards that are associated with the given organization.
9898
"""
99+
if not request.user.is_authenticated:
100+
return Response(status=400)
101+
99102
if not features.has("organizations:dashboards-basic", organization, actor=request.user):
100103
return Response(status=404)
101104

src/sentry/api/endpoints/organization_events_trace.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,9 @@ def record_analytics(
10581058
set_span_data("trace_view.projects", len_projects)
10591059

10601060
def get(self, request: Request, organization: Organization, trace_id: str) -> HttpResponse:
1061+
if not request.user.is_authenticated:
1062+
return Response(status=400)
1063+
10611064
if not self.has_feature(organization, request):
10621065
return Response(status=404)
10631066

@@ -1118,7 +1121,7 @@ def get(self, request: Request, organization: Organization, trace_id: str) -> Ht
11181121
False,
11191122
query_source=query_source,
11201123
)
1121-
self.record_analytics(transactions, trace_id, self.request.user.id, organization.id)
1124+
self.record_analytics(transactions, trace_id, request.user.id, organization.id)
11221125

11231126
warning_extra: dict[str, str] = {"trace": trace_id, "organization": organization.slug}
11241127

src/sentry/api/endpoints/organization_fork.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def post(self, request: Request, organization_id_or_slug) -> Response:
7070

7171
logger.info("relocations.fork.post.start", extra={"caller": request.user.id})
7272

73+
if not request.user.is_authenticated:
74+
return Response(status=status.HTTP_400_BAD_REQUEST)
75+
7376
org_mapping = (
7477
organization_mapping_service.get(organization_id=organization_id_or_slug)
7578
if str(organization_id_or_slug).isdecimal()
@@ -147,7 +150,7 @@ def post(self, request: Request, organization_id_or_slug) -> Response:
147150
# duplicate from the foreign region.
148151
provenance = Relocation.Provenance.SAAS_TO_SAAS
149152
with atomic_transaction(using=(router.db_for_write(Relocation))):
150-
new_relocation: Relocation = Relocation.objects.create(
153+
new_relocation = Relocation.objects.create(
151154
creator_id=request.user.id,
152155
owner_id=owner.id,
153156
step=Relocation.Step.UPLOADING.value,

src/sentry/api/endpoints/organization_pinned_searches.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class OrganizationPinnedSearchEndpoint(OrganizationEndpoint):
4040
permission_classes = (OrganizationPinnedSearchPermission,)
4141

4242
def put(self, request: Request, organization) -> Response:
43+
if not request.user.is_authenticated:
44+
return Response(status=400)
45+
4346
serializer = OrganizationSearchSerializer(data=request.data)
4447

4548
if not serializer.is_valid():
@@ -91,6 +94,9 @@ def put(self, request: Request, organization) -> Response:
9194
return Response(serialize(pinned_search, request.user), status=201)
9295

9396
def delete(self, request: Request, organization) -> Response:
97+
if not request.user.is_authenticated:
98+
return Response(status=400)
99+
94100
try:
95101
search_type = SearchType(int(request.data.get("type", 0)))
96102
except ValueError as e:

src/sentry/api/endpoints/project_details.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,8 @@ def put(self, request: Request, project) -> Response:
569569
Note that solely having the **`project:read`** scope restricts updatable settings to
570570
`isBookmarked`.
571571
"""
572+
if not request.user.is_authenticated:
573+
return Response(status=status.HTTP_400_BAD_REQUEST)
572574

573575
old_data = serialize(project, request.user, DetailedProjectSerializer())
574576
has_elevated_scopes = request.access and (

src/sentry/api/endpoints/prompts_activity.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class PromptsActivityEndpoint(Endpoint):
4646
def get(self, request: Request, **kwargs) -> Response:
4747
"""Return feature prompt status if dismissed or in snoozed period"""
4848

49+
if not request.user.is_authenticated:
50+
return Response(status=400)
51+
4952
features = request.GET.getlist("feature")
5053
if len(features) == 0:
5154
return Response({"details": "No feature specified"}, status=400)

src/sentry/api/endpoints/trace_explorer_ai_query.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ def post(request: Request, organization: Organization) -> Response:
6565
"""
6666
Checks if we are able to run Autofix on the given group.
6767
"""
68+
if not request.user.is_authenticated:
69+
return Response(status=status.HTTP_400_BAD_REQUEST)
70+
6871
project_ids = [int(x) for x in request.data.get("project_ids", [])]
6972
natural_language_query = request.data.get("natural_language_query")
7073

src/sentry/api/endpoints/trace_explorer_ai_setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def post(request: Request, organization: Organization) -> Response:
6868
"""
6969
Checks if we are able to run Autofix on the given group.
7070
"""
71+
if not request.user.is_authenticated:
72+
return Response(status=status.HTTP_400_BAD_REQUEST)
73+
7174
project_ids = [int(x) for x in request.data.get("project_ids", [])]
7275

7376
if organization.get_option("sentry:hide_ai_features", False):

src/sentry/explore/endpoints/explore_saved_queries.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ def get(self, request: Request, organization) -> Response:
302302
Retrieve a list of saved queries that are associated with the given organization.
303303
"""
304304

305+
if not request.user.is_authenticated:
306+
return Response(status=400)
307+
305308
if not self.has_feature(organization, request):
306309
return self.respond(status=404)
307310

@@ -464,6 +467,9 @@ def post(self, request: Request, organization) -> Response:
464467
"""
465468
Create a new trace explorersaved query for the given organization.
466469
"""
470+
if not request.user.is_authenticated:
471+
return Response(status=400)
472+
467473
if not self.has_feature(organization, request):
468474
return self.respond(status=404)
469475

src/sentry/explore/endpoints/explore_saved_query_starred.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ def post(self, request: Request, organization: Organization, id: int) -> Respons
4444
"""
4545
Update the starred status of a saved Explore query for the current organization member.
4646
"""
47+
if not request.user.is_authenticated:
48+
return Response(status=status.HTTP_400_BAD_REQUEST)
49+
4750
if not self.has_feature(organization, request):
4851
return self.respond(status=404)
4952

src/sentry/explore/endpoints/explore_saved_query_starred_order.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def has_feature(self, organization, request):
4141
)
4242

4343
def put(self, request: Request, organization: Organization) -> Response:
44+
if not request.user.is_authenticated:
45+
return Response(status=status.HTTP_400_BAD_REQUEST)
46+
4447
if not self.has_feature(organization, request):
4548
return self.respond(status=404)
4649

src/sentry/insights/endpoints/starred_segments.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ def delete(self, request: Request, organization: Organization) -> Response:
6969
"""
7070
Delete a starred segment for the current organization member.
7171
"""
72+
if not request.user.is_authenticated:
73+
return Response(status=status.HTTP_400_BAD_REQUEST)
74+
7275
if not self.has_feature(organization, request):
7376
return self.respond(status=404)
7477

src/sentry/integrations/messaging/linkage.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,11 @@ def persist_identity(
280280
if idp is None:
281281
raise ValueError('idp is required for linking (params must include "integration_id")')
282282

283-
user = request.user
284-
if isinstance(user, AnonymousUser):
283+
if isinstance(request.user, AnonymousUser):
285284
raise TypeError("Cannot link identity without a logged-in user")
286285

287286
try:
288-
Identity.objects.link_identity(user=user, idp=idp, external_id=external_id)
287+
Identity.objects.link_identity(user=request.user, idp=idp, external_id=external_id)
289288
except IntegrityError:
290289
event = self.capture_metric("failure.integrity_error")
291290
logger.exception(event)
@@ -314,6 +313,8 @@ def metrics_operation_key(self) -> str:
314313
def persist_identity(
315314
self, idp: IdentityProvider | None, external_id: str, request: HttpRequest
316315
) -> HttpResponse | None:
316+
if isinstance(request.user, AnonymousUser):
317+
raise TypeError("Cannot link identity without a logged-in user")
317318
try:
318319
identities = Identity.objects.filter(external_id=external_id)
319320
if idp is not None:

src/sentry/issues/endpoints/organization_group_search_view_details_starred.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def post(self, request: Request, organization: Organization, view_id: int) -> Re
4040
"""
4141
Update the starred status of a group search view for the current organization member.
4242
"""
43+
if not request.user.is_authenticated:
44+
return Response(status=status.HTTP_400_BAD_REQUEST)
45+
4346
if not features.has(
4447
"organizations:issue-stream-custom-views", organization, actor=request.user
4548
):

src/sentry/issues/endpoints/organization_group_search_view_starred_order.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class OrganizationGroupSearchViewStarredOrderEndpoint(OrganizationEndpoint):
3535
permission_classes = (MemberPermission,)
3636

3737
def put(self, request: Request, organization: Organization) -> Response:
38+
if not request.user.is_authenticated:
39+
return Response(status=status.HTTP_400_BAD_REQUEST)
40+
3841
if not features.has(
3942
"organizations:issue-stream-custom-views", organization, actor=request.user
4043
):

src/sentry/issues/endpoints/organization_group_search_views.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ def get(self, request: Request, organization: Organization) -> Response:
7373
7474
Retrieve a list of custom views for the current organization member.
7575
"""
76+
if not request.user.is_authenticated:
77+
return Response(status=status.HTTP_400_BAD_REQUEST)
78+
7679
if not features.has(
7780
"organizations:issue-stream-custom-views", organization, actor=request.user
7881
):
@@ -178,6 +181,9 @@ def post(self, request: Request, organization: Organization) -> Response:
178181
"""
179182
Create a new custom view for the current organization member.
180183
"""
184+
if not request.user.is_authenticated:
185+
return Response(status=status.HTTP_400_BAD_REQUEST)
186+
181187
if not features.has(
182188
"organizations:issue-stream-custom-views", organization, actor=request.user
183189
):

0 commit comments

Comments
 (0)