Skip to content

Task #2590: Harden User API Write Permissions - #2581

Open
julhoang wants to merge 2 commits into
developfrom
julia/harden-user-api-endpoints
Open

Task #2590: Harden User API Write Permissions#2581
julhoang wants to merge 2 commits into
developfrom
julia/harden-user-api-endpoints

Conversation

@julhoang

@julhoang julhoang commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Issue: #2590

Summary & Context

CustomUserPermissions gated writes by listing POST, PUT and DELETE explicitly, so PATCH fell through to the authenticated-only branch and any logged-in member passed the permission check on another member's record. This PR keys the check on SAFE_METHODS instead, and adds the tests that were missing on this endpoint.

It was not exploitable — a member's PATCH returned 200 and wrote nothing, because non-staff callers get UserSerializer and both its fields (id, display_name) are read-only. What changes here is a status code, not a data outcome. The reason to fix it now rather than log it: making any field on that serializer writable — plausible, e.g. to let users rename themselves — would open cross-member edits with nothing failing to signal it.

Changes

  • Gate every write method behind the staff/superuser check in CustomUserPermissions, keyed on SAFE_METHODS rather than an enumerated list. PATCH is now rejected for non-staff callers where it previously returned 200, and any method added later is gated by default. Read access is unchanged: still authenticated-only. UserViewSet is the only consumer of this permission class.
  • Add read-path tests for /api/v1/users/: list and detail reject anonymous callers, and the serializer switch is pinned in both directions — a member reading another user's record gets exactly {id, display_name}, a staff reader also gets email, date_joined and is_staff. That switch is load-bearing for this endpoint's safety and was untested.
  • Add write-rejection tests — PUT/PATCH/DELETE on another member's record for anonymous and member callers, plus create for a member — and a staff PATCH test asserting the write still applies, so widening the gate can't silently take admin writes with it.
  • Remove the commented-out legacy suite this test file consisted of. It imported users/factories.py, which no longer exists, so it could never be uncommented and run; its cases are covered by the real tests above.

‼️ Risks & Considerations ‼️

  • The fix is a guard, not a patch for live damage. See Summary — no data outcome changes, only the status code a member gets from PATCH.
  • The profile edit UI is unaffected. It writes via PATCH /api/v1/users/me/, served by CurrentUserAPIView under IsAuthenticated, which never goes through CustomUserPermissions. Grepping templates and JS, that is the only user API path the frontend calls; nothing calls /api/v1/users/<id>/.
  • Staff and superuser writes are unchanged, with a test asserting a staff PATCH still applies.

Peer-Testing Guidelines

  1. Confirm a member can't reach another member's record. Signed in as a non-staff user, in the browser console, with a different user's id:
    await fetch('/api/v1/users/CHANGE_TO_ANOTHER_USER_ID/', {
      method: 'PATCH',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
      },
      body: JSON.stringify({ display_name: 'outsider-test-name' }),
    })
    Expect 403. On develop the same call returns 200 — with no actual change to the record, which is why this went unnoticed.
  2. Repeat step 1 as a staff user and confirm both the 200 and that the target's display name actually changed at http://localhost:8000/admin/users/user/. This is the check that admin writes survived.
  3. Confirm the profile edit flow still saves. At http://localhost:8000/users/me/ edit your tagline, biography and profile links, save, and reload. This exercises /api/v1/users/me/, the path the frontend actually uses.
  4. Confirm read access is unchanged: curl -i http://localhost:8000/api/v1/users/ returns 403 anonymously, and the same URL in a signed-in browser tab returns the user list.

Self-review Checklist

  • Tag at least one team member from each team to review this PR
  • Link this PR to the related GitHub Project ticket

Frontend

N/A

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened API access controls for all write operations, including PATCH and future write methods.
    • Unauthenticated users can continue accessing only permitted read operations.
    • Regular users are prevented from creating, updating, or deleting protected resources.
    • Staff users can successfully update resources using PATCH.
  • Tests

    • Expanded API coverage for anonymous, regular-user, and staff permissions and responses.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the task and the main change: hardening user API write permissions.
Description check ✅ Passed The description includes the issue, context, changes, risks, testing guidance, and checklist; screenshots are not applicable to this backend change.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@julhoang julhoang changed the title Harden User API Write Permissions Task #2590: Harden User API Write Permissions Aug 7, 2026
@julhoang
julhoang marked this pull request as ready for review August 7, 2026 18:30

@jlchilders11 jlchilders11 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me, thanks for the catch!

@ycanales
ycanales self-requested a review August 12, 2026 17:22
@ycanales

Copy link
Copy Markdown
Collaborator

Reviewed and tested locally. Thanks for this @julhoang !

@julhoang
julhoang force-pushed the julia/harden-user-api-endpoints branch from 0edfc24 to 054d563 Compare August 14, 2026 18:51
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.

Task: Close the write gap on the user API so members can't edit each other's records

3 participants