[fix] Make VpnClient objects immutable after creation - #1431
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis change enforces post-creation immutability for Sequence Diagram(s)sequenceDiagram
participant TemplateOrCaller
participant AbstractVpnClient
participant Database
participant VpnSubnetDivisionRuleType
TemplateOrCaller->>AbstractVpnClient: full_clean()
AbstractVpnClient->>Database: load persisted row
Database-->>AbstractVpnClient: stored field values
alt immutable field changed
AbstractVpnClient-->>TemplateOrCaller: ValidationError
else update is allowed
TemplateOrCaller->>AbstractVpnClient: save()
AbstractVpnClient->>Database: persist changes
end
VpnSubnetDivisionRuleType->>AbstractVpnClient: set instance.ip
VpnSubnetDivisionRuleType->>AbstractVpnClient: save(update_fields=["ip"])
AbstractVpnClient->>Database: write ip only
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested labels: Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error, 1 inconclusive)
✅ Passed checks (6 passed)
✨ Finishing Touches 💡 1⚔️ Resolve merge conflicts 💡
🧪 Generate unit tests (beta)
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. Comment |
2e72025 to
749d9e6
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openwisp_controller/config/base/vpn.py`:
- Around line 911-923: The save() method in VpnClient currently uses only
_state.adding to decide whether this is a create, which can miss cases where a
new instance is constructed with an existing pk. Update the branching in save()
to treat objects with an existing self.pk as updates, and ensure
_check_immutable_fields() is called for those cases even when _state.adding is
still true. Keep the auto-provisioning path in save() for true creates, but gate
it on both _state.adding and absence of an existing primary key.
🪄 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: 1c0ce29a-b984-4dc9-aad2-6e6feb291f2d
📒 Files selected for processing (3)
openwisp_controller/config/base/vpn.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/subnet_division/rule_types/vpn.py
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Mark user-facing strings for translation with Django i18n helpers in Django code
Avoid unnecessary blank lines inside function and method bodies
Be careful with authentication, authorization, queryset filtering, serializers, admin behavior, cache invalidation, signals, Celery tasks, and websocket updates in Django code
Preserve validation around templates, VPN/PKI material, SSH credentials, device commands, uploaded files, URLs, and subnet/IP data
Write comments and docstrings only when they explain why code is shaped a certain way, placing them before the relevant code block instead of scattering them inside it
Files:
openwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.py
**/*.{py,html}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework.
Files:
openwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.py
🧠 Learnings (8)
📚 Learning: 2026-01-15T15:05:49.557Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/management/commands/clear_last_ip.py:38-42
Timestamp: 2026-01-15T15:05:49.557Z
Learning: In Django projects, when using select_related() to traverse relations (for example, select_related("organization__config_settings")), the traversed relation must not be deferred. If you also use .only() in the same query, include the relation name or FK field (e.g., "organization" or "organization_id") in the .only() list to avoid the error "Field X cannot be both deferred and traversed using select_related at the same time." Apply this guideline to Django code in openwisp_controller/config/management/commands/clear_last_ip.py and similar modules by ensuring any select_related with an accompanying only() includes the related field names to prevent deferred/traversed conflicts.
Applied to files:
openwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.py
📚 Learning: 2026-02-17T19:13:10.088Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/config/whois/commands.py:0-0
Timestamp: 2026-02-17T19:13:10.088Z
Learning: In reviews for the openwisp/openwisp-controller repository, do not propose changes based on Ruff warnings. The project does not use Ruff as its linter; ignore Ruff-related suggestions and follow the repository’s established linting and configuration rules. This guidance applies to all Python files under the openwisp_controller directory.
Applied to files:
openwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.py
📚 Learning: 2026-01-15T15:07:17.354Z
Learnt from: DragnEmperor
Repo: openwisp/openwisp-controller PR: 1175
File: openwisp_controller/geo/estimated_location/tests/tests.py:172-175
Timestamp: 2026-01-15T15:07:17.354Z
Learning: In this repository, flake8 enforces E501 (line too long) via setup.cfg (max-line-length = 88) while ruff ignores E501 via ruff.toml. Therefore, use '# noqa: E501' on lines that intentionally exceed 88 characters to satisfy flake8 without affecting ruff checks. This applies to Python files across the project (any .py) and is relevant for tests as well. Use sparingly and only where breaking lines is not feasible without hurting readability or functionality.
Applied to files:
openwisp_controller/subnet_division/rule_types/vpn.pyopenwisp_controller/config/tests/test_vpn.pyopenwisp_controller/config/base/vpn.py
📚 Learning: 2026-06-07T12:07:08.468Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_admin.py:2335-2335
Timestamp: 2026-06-07T12:07:08.468Z
Learning: In this project’s Python test suite (files under openwisp_controller/**/tests/), don’t require or request prose/inline comments that document the breakdown of query-count changes (e.g., assertions around template/DB query counts in helpers like _verify_template_queries). Treat query-count assertions as volatile implementation details that change frequently; review should focus on whether the test asserts the expected behavior, not on explaining the specific query-count deltas in comments.
Applied to files:
openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-06-07T12:07:24.608Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/pki/tests/test_api.py:155-155
Timestamp: 2026-06-07T12:07:24.608Z
Learning: When reviewing Python test files in this repository, avoid recommending inline comments that explain or justify `assertNumQueries` (Django query count) expectations. Query counts can change frequently as implementations evolve, and inline explanations add maintenance burden; the expected count should be understandable without added comment blocks.
Applied to files:
openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-06-25T12:20:18.414Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/base/models.py:571-572
Timestamp: 2026-06-25T12:20:18.414Z
Learning: When writing or reviewing tests that override pagination behavior via OpenWispPagination.paginate_queryset(), patch `view.pagination_page_size` (not `page_size`). The method uses `getattr(view, "pagination_page_size", self.page_size)`, so tests must set the attribute on the view to affect pagination. If the view class does not define `pagination_page_size`, using `unittest.mock.patch(..., create=True)` is intentional and correct because the attribute may not exist until patched.
Applied to files:
openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-06-07T12:07:25.164Z
Learnt from: stktyagi
Repo: openwisp/openwisp-controller PR: 1378
File: openwisp_controller/config/tests/test_config.py:864-865
Timestamp: 2026-06-07T12:07:25.164Z
Learning: When reviewing this repo’s Python test suite, treat changes to the *expected* query count in `assertNumQueries(...)` calls as routine test maintenance. If a PR updates the numeric argument (e.g., in `test_config.py`, `test_api.py`, `test_admin.py`, `test_pki.py`) and the test remains consistent with the feature changes, reviewers should not flag the increased number as a performance regression that requires investigation solely because the count went up; instead, focus on whether the update is intentional and the surrounding test/code changes justify the revised expectation.
Applied to files:
openwisp_controller/config/tests/test_vpn.py
📚 Learning: 2026-06-25T12:20:45.387Z
Learnt from: dee077
Repo: openwisp/openwisp-controller PR: 1395
File: openwisp_controller/connection/tests/test_api.py:916-932
Timestamp: 2026-06-25T12:20:45.387Z
Learning: When reviewing API pagination behavior in openwisp-controller, assume `OpenWispPagination.paginate_queryset()` allows a per-view page-size override via `getattr(view, "pagination_page_size", self.page_size)` (so `view.pagination_page_size`, if present, should affect pagination). In Python tests, it is valid to patch `pagination_page_size` on a view class even if the attribute isn’t declared on the class by default, by using `unittest.mock.patch.object(..., "pagination_page_size", ..., create=True)` so the override is available for the pagination logic during the test.
Applied to files:
openwisp_controller/config/tests/test_vpn.py
🔇 Additional comments (2)
openwisp_controller/subnet_division/rule_types/vpn.py (1)
55-56: LGTM!openwisp_controller/config/tests/test_vpn.py (1)
275-302: LGTM!Also applies to: 512-584
749d9e6 to
d07983c
Compare
Code Review SummaryStatus: No Issues Found | Recommendation: Merge The coderabbitai comment about Files Reviewed (2 files)
Previous Review Summaries (3 snapshots, latest commit fd4f09e)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit fd4f09e)Status: No Issues Found | Recommendation: Merge No new issues found in the incremental diff. Documentation and translation changes added since the previous review are clean. Files Reviewed (6 files)
Previous review (commit 8983946)Status: No Issues Found | Recommendation: Merge No new code changes since the previous review — current HEAD ( The immutability enforcement remains clean and well-targeted: Policy checks from the custom instructions are satisfied:
No critical bugs or security vulnerabilities were found in the changed lines. An existing reviewer comment already tracks the Files Reviewed (3 files)
Previous review (commit d07983c)Status: No Issues Found | Recommendation: Merge Nice work — the immutability enforcement is clean and well-targeted. The policy checks from the custom instructions are satisfied:
No critical bugs or security vulnerabilities were found in the changed lines. An existing reviewer comment already tracks the Files Reviewed (3 files)
Reviewed by ling-3.0-flash:free · Input: 180.5K · Output: 22.5K · Cached: 301.7K |
Make VpnClient objects immutable after creation while allowing managed IP updates. Fixes openwisp#1428
d07983c to
8983946
Compare
There was a problem hiding this comment.
Thanks for the contribution! Overall, you did a good job with this PR. Before it can be merged, I'd like to request a couple of changes:
- Please add documentation mentioning that
VPNClientinstances are now immutable after creation. - Since you've introduced a new translatable string, please update the corresponding .po file as well.
- Please update the PR description and PR title to follow the provided pull request template. PRs that don't use the template may be flagged by the maintainers as AI-generated or low-quality ("AI Slop"). You can find the relevant guidance here: https://openwisp.io/docs/dev/general/code-of-conduct.html.
- Please address the comments given by coderabbit.
Once those changes are in place, I'll be happy to take another look.
|
Hi @sameerrandive-dev 👋, This is a friendly reminder that this pull request has had no activity for 7 days since changes were requested. We'd love to see this contribution merged! Please take a moment to:
If you're busy or need more time, no worries! Just leave a comment to let us know you're still working on it. Note: within 7 more days, the linked issue will be unassigned to allow other contributors to work on it. Thank you for your contribution! 🙏 |
|
Hi @sameerrandive-dev 👋, This pull request has been marked as stale due to 14 days of inactivity after changes were requested. As a result, any linked issues are being unassigned from you so other contributors can pick them up. However, you can still continue working on this PR! If you push new commits or respond to the review feedback:
If you need more time or have questions about the requested changes, please let us know. We're happy to help! 🤝 |
- added docs/partials/vpn-client-immutability.rst and included it in the OpenVPN, WireGuard, VXLAN over WireGuard and ZeroTier tutorials - bootstrapped locale catalog (locale/en/LC_MESSAGES/django.po) with the new translatable string introduced by the immutability validation
…h an existing pk openwisp#1428 _state.adding is True on any freshly constructed instance, so VpnClient(pk=<existing pk>, ...).save() bypassed the immutability check and updated the persisted row. The persisted row's existence is now the authoritative signal for create vs update.
Summary
Fixes #1428 by making
VpnClientobjects immutable after creation while allowing the internally managedipfield to be updated.Changes
VpnClientidentity fields by enforcing immutability in bothclean()andsave().ipupdates required by the asynchronous subnet provisioning workflow.VpnClientobjects.ipupdates.Validation
subnet_divisionand swapped-model test suites.