Skip to content
Merged
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: 14 additions & 0 deletions src/sentry/seer/endpoints/organization_seer_explorer_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ def get(
return Response(
{"detail": "Seer has not been acknowledged by the organization."}, status=403
)
if not organization.flags.allow_joinleave:
return Response(
{
"detail": "Organization does not have open team membership enabled. Seer requires this to aggregate context across all projects and allow members to ask questions freely."
},
status=403,
)
Comment on lines +155 to +161
Copy link
Contributor

Choose a reason for hiding this comment

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

Potential bug: Insufficient access control logic denies access to users with global roles.
  • Description: The access control logic incorrectly denies access to users with high-level permissions (e.g., org:admin, org:write). The code checks only the organization.flags.allow_joinleave flag, but it should use the request.access.has_global_access property, which correctly grants access to users with global roles regardless of the allow_joinleave setting. This results in a functional bug where legitimate users are blocked from the feature.

  • Suggested fix: Replace the check if not organization.flags.allow_joinleave: with if not request.access.has_global_access: to correctly account for users who have global project access through their organizational role.
    severity: 3.0, confidence: 5.0

Did we get this right? 👍 / 👎 to inform future reviews.


if not run_id:
return Response({"session": None}, status=404)
Expand Down Expand Up @@ -186,6 +193,13 @@ def post(
return Response(
{"detail": "Seer has not been acknowledged by the organization."}, status=403
)
if not organization.flags.allow_joinleave:
return Response(
{
"detail": "Organization does not have open team membership enabled. Seer requires this to aggregate context across all projects and allow members to ask questions freely."
},
status=403,
)

serializer = SeerExplorerChatSerializer(data=request.data)
if not serializer.is_valid():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,39 @@ def test_post_without_acknowledgement_returns_403(
assert response.data == {"detail": "Seer has not been acknowledged by the organization."}
mock_get_seer_org_acknowledgement.assert_called_once_with(self.organization.id)

def test_post_without_open_team_membership_returns_403(self) -> None:
self.organization.flags.allow_joinleave = False
self.organization.save()

with patch(
"sentry.seer.endpoints.organization_seer_explorer_chat.get_seer_org_acknowledgement",
return_value=True,
):
data = {"query": "Test query"}
response = self.client.post(self.url, data, format="json")

assert response.status_code == 403
assert (
response.data["detail"]
== "Organization does not have open team membership enabled. Seer requires this to aggregate context across all projects and allow members to ask questions freely."
)

def test_get_without_open_team_membership_returns_403(self) -> None:
self.organization.flags.allow_joinleave = False
self.organization.save()

with patch(
"sentry.seer.endpoints.organization_seer_explorer_chat.get_seer_org_acknowledgement",
return_value=True,
):
response = self.client.get(f"{self.url}123/")

assert response.status_code == 403
assert (
response.data["detail"]
== "Organization does not have open team membership enabled. Seer requires this to aggregate context across all projects and allow members to ask questions freely."
)


class OrganizationSeerExplorerChatEndpointFeatureFlagTest(APITestCase):
"""Test feature flag requirements separately without the decorator"""
Expand Down
Loading