Skip to content

[fix] Use UUID converters for users API detail routes #487#492

Open
czarflix wants to merge 2 commits intoopenwisp:masterfrom
czarflix:codex/issue-487-uuid-route-validation
Open

[fix] Use UUID converters for users API detail routes #487#492
czarflix wants to merge 2 commits intoopenwisp:masterfrom
czarflix:codex/issue-487-uuid-route-validation

Conversation

@czarflix
Copy link

@czarflix czarflix commented Mar 12, 2026

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #487.

Description of Changes

This fixes a router-to-model PK mismatch in the users API.

The user and organization detail routes were defined with str converters even though the underlying resources are UUID-backed, which allowed malformed values like not-a-uuid to resolve to DRF views and return 401 instead of failing at the routing boundary with 404.

This change updates the five affected routes to use uuid converters and adds a focused regression test to assert malformed UUID paths return 404.

Because the route converter change makes kwargs["pk"] a UUID object, this also updates the self-password permission check in ChangePasswordView to compare UUID values directly. Without that follow-up, self password changes would incorrectly take the stricter permission path and fail existing API tests.

Manual validation performed:

  • python tests/manage.py test openwisp_users.tests.test_api.test_views --parallel 2
  • python tests/manage.py test openwisp_users.tests.test_api --parallel 2
  • python tests/manage.py test openwisp_users.tests.test_api.test_api.TestUsersApi.test_organization_detail_api openwisp_users.tests.test_api.test_api.TestUsersApi.test_get_user_detail_api openwisp_users.tests.test_api.test_api.TestUsersApi.test_change_password_of_superuser_by_superuser openwisp_users.tests.test_api.test_api.TestUsersApi.test_get_email_list_api openwisp_users.tests.test_api.test_api.TestUsersApi.test_put_email_update_api --parallel 2
  • ./run-qa-checks

No manual documentation update was needed because this change corrects the route definition itself, which is the source of truth for the generated API schema/docs.

Screenshot

N/A (backend/API change)

User and organization API detail routes currently use string path
converters even though the underlying resources are UUID-backed. This
allows malformed IDs to resolve to DRF views and return 401 instead of
being rejected at the routing boundary with 404.

Update the five affected routes to use uuid converters and add a
regression test covering malformed UUID paths.

Closes openwisp#487
@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Warning

Rate limit exceeded

@czarflix has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 39 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 45cb0cc0-99f1-4b30-91b5-76f389284bff

📥 Commits

Reviewing files that changed from the base of the PR and between a42ea59 and 3bb4ee5.

📒 Files selected for processing (1)
  • openwisp_users/api/views.py
📝 Walkthrough

Walkthrough

The PR updates several API URL patterns to use UUID path converters (<uuid:pk>) instead of string converters, ensuring route-level UUID validation for user and organization detail, password change, and email endpoints. It adds a test that verifies invalid UUID routes return 404. Additionally, ChangePasswordView permission logic was adjusted to compare the authenticated user’s id to the pk without casting to string.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: converting detail routes to use UUID converters instead of string converters, fixing issue #487.
Description check ✅ Passed The description covers all required sections: checklist completed, issue reference, detailed explanation of changes, manual testing performed, and rationale for follow-up code changes.
Linked Issues check ✅ Passed The PR directly addresses issue #487 by converting five detail routes from str to uuid converters and adding a regression test for malformed UUID rejection.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the UUID converter routes: URL pattern updates, a regression test, and a necessary permission check fix in ChangePasswordView.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openwisp_users/api/urls.py`:
- Around line 29-32: ChangePasswordView.get_permissions currently compares
str(self.request.user.id) to self.kwargs["pk"] which is a uuid.UUID (because the
URL uses "<uuid:pk>") causing the check to always fail; update the comparison in
ChangePasswordView.get_permissions to compare the UUID objects directly (i.e.,
remove the str() conversion so it compares self.request.user.id to
self.kwargs["pk"]) so the self-check correctly allows IsAuthenticated for a user
changing their own password.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5c961879-2f6c-43b0-a6c8-3146e978c126

📥 Commits

Reviewing files that changed from the base of the PR and between e904269 and 559e766.

📒 Files selected for processing (2)
  • openwisp_users/api/urls.py
  • openwisp_users/tests/test_api/test_views.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.0.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.0.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
🔇 Additional comments (2)
openwisp_users/api/urls.py (1)

18-22: URL converter changes align with model definitions.

The changes correctly enforce UUID validation at the routing level, matching the UUIDField primary key definition in the User model (openwisp_users/base/models.py:52). Django's ORM handles UUID objects properly in filter queries (e.g., User.objects.filter(pk=self.kwargs["pk"])), so database lookups will work correctly.

Also applies to: 28-28, 34-42

openwisp_users/tests/test_api/test_views.py (1)

28-40: Good regression test coverage for UUID routing validation.

The test correctly validates that malformed UUIDs are rejected at the routing level with 404. Using hardcoded paths is appropriate here since reverse() requires valid parameters. The use of subTest provides good isolation for debugging failures.

One minor consideration: the hardcoded /api/v1/ prefix could become brittle if the API version or URL configuration changes. Consider using Django's test client URL prefix configuration or extracting the prefix to a constant if this becomes a maintenance concern.

After switching the users API detail routes to uuid converters,
ChangePasswordView receives a UUID object in kwargs['pk'] instead of a
string. The self-password permission check was still comparing
str(self.request.user.id) to that UUID, which broke self password
changes and caused API test regressions.

Compare UUID values directly so self-password requests keep using the
intended IsAuthenticated permission path.

Related to openwisp#487
@czarflix czarflix force-pushed the codex/issue-487-uuid-route-validation branch from a42ea59 to 3bb4ee5 Compare March 12, 2026 17:09
@czarflix
Copy link
Author

I pushed a follow-up update to fix the branch-specific regression caused by the UUID route converter change and to satisfy commit validation.

Local verification on the current branch:

  • python tests/manage.py test openwisp_users.tests.test_api --parallel 2
  • ./run-qa-checks

If the remaining CI failure is still the full-suite selenium error

TypeError: 'DummyList' object is not iterable

that appears to be preexisting: I reproduced the same failure on a clean upstream/master worktree with coverage run ./runtests.py --parallel 2.

@czarflix
Copy link
Author

czarflix commented Mar 12, 2026

@nemesifier I investigated the failing CI job on this PR and the failure appears to be outside the UUID route change itself.
./run-qa-checks passes locally on this branch, and the failing matrix job is crashing in openwisp_utils/tests/selenium.py. From my local inspection/reproduction, this looks related to the skipped-test handling introduced in openwisp-utils#570, specifically under Django parallel test execution where the result object differs from the normal unittest case.
I’m happy to open a separate upstream issue/PR in openwisp-utils with a minimal repro if that would help.

@nemesifier
Copy link
Member

I have seen the same CI failure in other PRs. Help is welcome to fix it (in a separate PR).

@czarflix
Copy link
Author

I have seen the same CI failure in other PRs. Help is welcome to fix it (in a separate PR).

alright, I will get to it. Thanks for your time.

@czarflix
Copy link
Author

I have seen the same CI failure in other PRs. Help is welcome to fix it (in a separate PR).

I opened the separate upstream fix PR here: openwisp/openwisp-utils#620. From my testing, it preserves the serial skip behavior added in #570 and fixes the parallel runner crash. I also added focused regression coverage for both result types.

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.

[bug] API endpoint URLs should use <pk:uuid> instead of <pk:str>

2 participants