Skip to content

Commit 20a1cb0

Browse files
authored
fix(replays): dont let superusers affect viewed by (#69529)
- dont show superusers user_ids in the viewed by response - Don't create a viewed by record if the user is not in the org of the replay
1 parent 9952d7f commit 20a1cb0

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

src/sentry/replays/endpoints/project_replay_viewed_by.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ def get(self, request: Request, project: Project, replay_id: str) -> Response:
8484
if viewed_by_ids == []:
8585
return Response({"data": {"viewed_by": []}}, status=200)
8686

87-
# Note: in the rare/error case where Snuba returns non-existent user ids, this fx will filter them out.
8887
serialized_users = user_service.serialize_many(
89-
filter=dict(user_ids=viewed_by_ids),
88+
filter=dict(user_ids=viewed_by_ids, organization_id=project.organization.id),
9089
as_user=serialize_generic_user(request.user),
9190
)
91+
9292
serialized_users = [_normalize_user(user) for user in serialized_users]
9393

9494
return Response({"data": {"viewed_by": serialized_users}}, status=200)
@@ -105,6 +105,11 @@ def post(self, request: Request, project: Project, replay_id: str) -> Response:
105105
except ValueError:
106106
return Response(status=404)
107107

108+
user_orgs = user_service.get_organizations(user_id=request.user.id)
109+
if project.organization.id not in [org.id for org in user_orgs]:
110+
# If the user is not in the same organization as the replay, we don't need to do anything.
111+
return Response(status=204)
112+
108113
# make a query to avoid overwriting the `finished_at` column
109114
filter_params = self.get_filter_params(request, project, date_filter_optional=False)
110115
finished_at_response = execute_query(

tests/sentry/replays/test_project_replay_viewed_by.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,33 @@ def test_post_replay_viewed_by_not_exist(self):
152152
with self.feature(REPLAYS_FEATURES):
153153
response = self.client.post(self.url, data="")
154154
assert response.status_code == 404
155+
156+
@patch("sentry.replays.endpoints.project_replay_viewed_by.publish_replay_event")
157+
def test_post_replay_viewed_by_not_in_org(self, publish_replay_event):
158+
with self.feature(REPLAYS_FEATURES):
159+
finished_at_dt = datetime.datetime.now() - datetime.timedelta(seconds=20)
160+
self.store_replays(mock_replay(finished_at_dt, self.project.id, self.replay_id))
161+
self.login_as(user=self.create_user(is_superuser=True, is_staff=True), superuser=True)
162+
response = self.client.post(self.url, data="")
163+
assert response.status_code == 204
164+
assert not publish_replay_event.called
165+
166+
def test_get_replay_viewed_by_user_in_other_org(self):
167+
other_org_member = self.create_member(
168+
organization=self.create_organization(), user=self.create_user()
169+
)
170+
seq1_timestamp = datetime.datetime.now() - datetime.timedelta(seconds=10)
171+
seq2_timestamp = datetime.datetime.now() - datetime.timedelta(seconds=5)
172+
self.store_replays(mock_replay(seq1_timestamp, self.project.id, self.replay_id))
173+
self.store_replays(mock_replay(seq2_timestamp, self.project.id, self.replay_id))
174+
175+
self.store_replays(
176+
mock_replay_viewed(
177+
time.time(), self.project.id, self.replay_id, other_org_member.user_id
178+
)
179+
)
180+
181+
with self.feature(REPLAYS_FEATURES):
182+
response = self.client.get(self.url)
183+
assert response.status_code == 200
184+
assert len(response.data["data"]["viewed_by"]) == 0

0 commit comments

Comments
 (0)