Skip to content

Conversation

@shellmayr
Copy link
Member

@shellmayr shellmayr commented Dec 5, 2025

  • Implement the guarding of endpoints by granular permissions for replays, building on the data model established in feat(replay): add model to allow per-user access control for replays #104446,
  • Introduce base endpoint classes OrganizationReplayEndpoint and ProjectReplayEndpoint to centralize feature and permission checks for replay access, ensuring consistent enforcement across all replay endpoints.
  • Updated all replay endpoints to use the new base classes and enforce granular permissions, returning appropriate HTTP status codes for unauthorized access.
  • More information on the plan & product impact can be found here Research granular permissions for viewing Replays

Closes TET-1564

@linear
Copy link

linear bot commented Dec 5, 2025

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Dec 5, 2025
@shellmayr shellmayr changed the base branch from master to shellmayr/feat/add-model-for-granular-replay-access December 5, 2025 14:11
@shellmayr shellmayr changed the title feat(replay): add model to allow per-user access control for replays feat(replay): guard replay endpoints with granular user permissions Dec 5, 2025
@codecov
Copy link

codecov bot commented Dec 5, 2025

❌ 7 Tests Failed:

Tests completed Failed Passed Skipped
28865 7 28858 234
View the top 3 failed test(s) by shortest run time
tests.sentry.replays.endpoints.test_project_replay_jobs_delete.ProjectReplayDeletionJobDetailTest::test_get_success
Stack Traces | 2.02s run time
#x1B[1m#x1B[.../replays/endpoints/test_project_replay_jobs_delete.py#x1B[0m:393: in test_get_success
    response = self.get_success_response(self.organization.slug, self.project.slug, job.id)
#x1B[1m#x1B[.../sentry/testutils/cases.py#x1B[0m:618: in get_success_response
    assert_status_code(response, status.HTTP_200_OK)
#x1B[1m#x1B[.../sentry/testutils/asserts.py#x1B[0m:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
#x1B[1m#x1B[31mE   AssertionError: (404, b'')#x1B[0m
#x1B[1m#x1B[31mE   assert 404 < 201#x1B[0m
#x1B[1m#x1B[31mE    +  where 404 = <Response status_code=404>.status_code#x1B[0m
tests.sentry.replays.endpoints.test_project_replay_jobs_delete.ProjectReplayDeletionJobDetailTest::test_get_count_deleted_reflects_offset
Stack Traces | 2.06s run time
#x1B[1m#x1B[.../replays/endpoints/test_project_replay_jobs_delete.py#x1B[0m:420: in test_get_count_deleted_reflects_offset
    response = self.get_success_response(self.organization.slug, self.project.slug, job.id)
#x1B[1m#x1B[.../sentry/testutils/cases.py#x1B[0m:618: in get_success_response
    assert_status_code(response, status.HTTP_200_OK)
#x1B[1m#x1B[.../sentry/testutils/asserts.py#x1B[0m:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
#x1B[1m#x1B[31mE   AssertionError: (404, b'')#x1B[0m
#x1B[1m#x1B[31mE   assert 404 < 201#x1B[0m
#x1B[1m#x1B[31mE    +  where 404 = <Response status_code=404>.status_code#x1B[0m
tests.sentry.replays.endpoints.test_project_replay_jobs_delete.ProjectReplayDeletionJobDetailTest::test_permission_granted_with_project_write
Stack Traces | 2.53s run time
#x1B[1m#x1B[.../replays/endpoints/test_project_replay_jobs_delete.py#x1B[0m:531: in test_permission_granted_with_project_write
    assert response.status_code == 200
#x1B[1m#x1B[31mE   assert 404 == 200#x1B[0m
#x1B[1m#x1B[31mE    +  where 404 = <Response status_code=404>.status_code#x1B[0m
tests.sentry.replays.endpoints.test_project_replay_jobs_delete.ProjectReplayDeletionJobDetailTest::test_permission_granted_with_project_admin
Stack Traces | 2.77s run time
#x1B[1m#x1B[.../replays/endpoints/test_project_replay_jobs_delete.py#x1B[0m:556: in test_permission_granted_with_project_admin
    assert response.status_code == 200
#x1B[1m#x1B[31mE   assert 404 == 200#x1B[0m
#x1B[1m#x1B[31mE    +  where 404 = <Response status_code=404>.status_code#x1B[0m
tests.sentry.replays.endpoints.test_replay_granular_permissions.TestReplayGranularPermissions::test_empty_allowlist_allows_all_users
Stack Traces | 2.97s run time
#x1B[1m#x1B[.../replays/endpoints/test_replay_granular_permissions.py#x1B[0m:125: in test_empty_allowlist_allows_all_users
    assert response.status_code == 200
#x1B[1m#x1B[31mE   assert 403 == 200#x1B[0m
#x1B[1m#x1B[31mE    +  where 403 = <Response status_code=403>.status_code#x1B[0m
tests.sentry.replays.endpoints.test_replay_granular_permissions.TestReplayGranularPermissions::test_removing_last_user_from_allowlist_reopens_access
Stack Traces | 3.37s run time
#x1B[1m#x1B[.../replays/endpoints/test_replay_granular_permissions.py#x1B[0m:174: in test_removing_last_user_from_allowlist_reopens_access
    assert response.status_code == 200
#x1B[1m#x1B[31mE   assert 403 == 200#x1B[0m
#x1B[1m#x1B[31mE    +  where 403 = <Response status_code=403>.status_code#x1B[0m
tests.sentry.replays.endpoints.test_project_replay_summary.ProjectReplaySummaryTestCase::test_feature_flag_disabled
Stack Traces | 18.1s run time
#x1B[1m#x1B[.../replays/endpoints/test_project_replay_summary.py#x1B[0m:87: in test_feature_flag_disabled
    assert response.status_code == 403, (replay, replay_ai, method)
#x1B[1m#x1B[31mE   AssertionError: (False, True, 'GET')#x1B[0m
#x1B[1m#x1B[31mE   assert 404 == 403#x1B[0m
#x1B[1m#x1B[31mE    +  where 404 = <Response status_code=404>.status_code#x1B[0m

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@shellmayr shellmayr force-pushed the shellmayr/feat/guard-api-endpoints-by-granular-replay-access branch from f8575a5 to 4b786f2 Compare December 5, 2025 15:29
@shellmayr shellmayr requested a review from a team December 5, 2025 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants