Skip to content

feat: add RBAC (Viewer/Member/Admin) to Django admin#1398

Open
thomasrockhu-codecov wants to merge 4 commits into
mainfrom
th/admin-owner-id-array-fields
Open

feat: add RBAC (Viewer/Member/Admin) to Django admin#1398
thomasrockhu-codecov wants to merge 4 commits into
mainfrom
th/admin-owner-id-array-fields

Conversation

@thomasrockhu-codecov

@thomasrockhu-codecov thomasrockhu-codecov commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

fixes https://harness.atlassian.net/browse/COV-8

Summary

Adds a three-tier RBAC model to the Codecov Django admin, gated on is_staff, enforced centrally so every registered ModelAdmin is covered without per-app edits.

  • Viewer: read-only everywhere — no add/change/delete, no admin actions, all fields read-only, inlines read-only, Redis/Celery queue models (redis_admin) hidden, and custom action views (commit reprocess/notify, redis chart fragment) return 403.
  • Member: matches today's staff-level access (unchanged behavior).
  • Admin: driven solely by is_superuser; toggling it to True forces the admin role, and demoting (True -> False) drops the user to Viewer.

How it works

  • New User.staff_role field (viewer/member/admin) on the shared codecov_auth model, kept in sync with is_superuser in User.save(). effective_staff_role derives Admin from is_superuser.
  • Migration 0076_user_staff_role adds the column and backfills: existing staff -> Member, superusers -> Admin, everyone else -> Viewer (the default).
  • RBACAdminMixin + CodecovAdminSite (wired via CodecovAdminConfig.default_site, replacing django.contrib.admin in INSTALLED_APPS) auto-wrap every registered ModelAdmin.
  • deny_viewers() guards GET-triggered custom admin views in core and redis_admin.
  • UserAdmin surfaces is_staff / is_superuser / staff_role, editable only by superusers, with the Admin level managed automatically.
  • UserFactory defaults staff test users to Member so the existing admin suite keeps its historical access; Viewer tests opt in explicitly.

Test plan

  • Run make test (Docker + Postgres/Timescale) — new suite codecov_auth/tests/test_admin_rbac.py covers role sync, superuser demotion to Viewer, Viewer read-only/no-actions, Redis models hidden, custom-view 403 vs Member access, and UserAdmin field/choice gating.
  • Confirm makemigrations --check is clean and 0076 applies.
  • Manual: log in as a Viewer (staff, staff_role=viewer) and confirm read-only admin with Redis hidden; as a Member confirm unchanged access; toggle is_superuser and confirm Admin/Viewer transitions.

Made with Cursor

Introduce three staff access levels gated on `is_staff`, enforced centrally
via a `CodecovAdminSite` that wraps every registered `ModelAdmin` with an
`RBACAdminMixin`:

- Viewer: read-only, no actions, Redis/Celery queue models hidden, and
  custom action views (reprocess/notify) denied.
- Member: matches today's staff access.
- Admin: driven solely by `is_superuser`; setting it False demotes to Viewer.

Adds a `User.staff_role` field (synced with `is_superuser` on save) plus a
data migration mapping existing staff to Member and superusers to Admin, and
surfaces the role controls in `UserAdmin` (superuser-only edits).
@thomasrockhu-codecov thomasrockhu-codecov requested a review from a team as a code owner July 2, 2026 17:32
@codspeed-hq

codspeed-hq Bot commented Jul 2, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing th/admin-owner-id-array-fields (0651bd0) with main (ee7a2b4)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (701e991) during the generation of this report, so ee7a2b4 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.63265% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.86%. Comparing base (f4b9ae7) to head (0651bd0).
⚠️ Report is 5 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...s/shared/shared/django_apps/codecov_auth/models.py 52.94% 12 Missing and 4 partials ⚠️
apps/codecov-api/codecov/admin.py 87.30% 8 Missing ⚠️
apps/codecov-api/core/admin.py 72.72% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1398      +/-   ##
==========================================
- Coverage   91.89%   91.86%   -0.04%     
==========================================
  Files        1324     1327       +3     
  Lines       50851    51011     +160     
  Branches     1626     1634       +8     
==========================================
+ Hits        46730    46861     +131     
- Misses       3815     3840      +25     
- Partials      306      310       +4     
Flag Coverage Δ
apiunit 94.91% <89.21%> (-0.04%) ⬇️
sharedintegration 36.85% <24.44%> (-0.07%) ⬇️
sharedunit 84.80% <64.44%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@codecov-notifications

codecov-notifications Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.63265% with 27 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...s/shared/shared/django_apps/codecov_auth/models.py 52.94% 12 Missing and 4 partials ⚠️
apps/codecov-api/codecov/admin.py 87.30% 8 Missing ⚠️
apps/codecov-api/core/admin.py 72.72% 3 Missing ⚠️

❌ Your patch check has failed because the patch coverage (81.63%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage.

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Non-staff users now have an explicit `none` staff role instead of defaulting
to Viewer:

- `User.save()` sets `staff_role` to `none` whenever `is_staff` is False, and
  `effective_staff_role` returns `NONE` for non-staff users.
- Migration 0077 adds the `none` choice/default and backfills existing
  non-staff, non-superuser rows.

Also adds a "role" filter to the Users admin changelist via
`StaffRoleListFilter`, which mirrors the effective-role logic (superuser =>
Admin, non-staff => None), and surfaces `None` as the read-only derived role
for non-staff targets in `UserAdmin`.
Remove the unused is_admin_staff/is_member_staff/is_viewer_staff/is_none_staff
User properties and condense redundant comments/docstrings across the admin
RBAC code for readability.
Simplify StaffRoleListFilter to query the synced staff_role column directly,
and combine the two staff_role migrations into one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant