Skip to content

[feature] Introduced standalone certificate templates and device bindings#1378

Open
stktyagi wants to merge 22 commits into
gsoc26-x509-certificate-generator-templatesfrom
issues/1356-extend-abstract-template
Open

[feature] Introduced standalone certificate templates and device bindings#1378
stktyagi wants to merge 22 commits into
gsoc26-x509-certificate-generator-templatesfrom
issues/1356-extend-abstract-template

Conversation

@stktyagi
Copy link
Copy Markdown
Member

@stktyagi stktyagi commented May 26, 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 #1356

Description of Changes

This PR establishes the database architecture, UI, API and lifecycle for standalone X.509 certificate templates.

Closes #1356
Closes #1377
Closes #1357
Closes #1361
Closes #1358

Manual test plan

Setup

  • Go to PKI -> Certification Authorities and create two CAs: CA-1 and CA-2.
  • Go to PKI -> Certificates and create two certificates to act as blueprints:
  • Blueprint-1 (Must use CA-1)
  • Blueprint-2 (Must use CA-2)
  • Go to Devices and create a device (test-device).

Template Creation and Validation

  • Configuration -> Templates and click ADD TEMPLATE.
  • Set Type to Certificate.
    • Leave CA blank and try to save.
    • Expected Result: Validation error stating a CA is required.
  • Set CA to CA-1.
    • Set Blueprint to Blueprint-2 (which belongs to CA-2). Try to save.
    • When opening drop-down for blueprint you'll only see unassigned and unrevoked certificates.
    • Expected Result: Validation error stating the Blueprint must match the selected CA.
  • Change Blueprint to Blueprint-1. Name the template Active-Cert-Template. Save it.

Device Provisioning

  • Add configuration for test-device.
  • In the templates field, add Active-Cert-Template. Save.
  • Go to PKI -> Certificates.
  • Expected Result: You should see a brand new certificate automatically generated for test-device. Its status should be valid (not revoked).

Active Mutation Locks

  • Go back to Configuration -> Templates and edit Active-Cert-Template (which is now assigned to an active device).
  • Change the Type to Generic. Try to save.
  • Expected Result: Validation error: "You cannot change the template type from certificate on an active template."
  • Change the CA to CA-2. Try to save.
  • Expected Result: Validation error blocking the CA change.
  • Change the Blueprint to Blueprint-2 (ensure you also change the CA so they match, triggering the active lock). Try to save.
  • Expected Result: Validation error blocking the Blueprint change.

Revocation on Removal

  • Go to the Configuration for test-device.
  • Remove Active-Cert-Template entirely from the templates list. Save.
  • Go to PKI -> Certificates and locate the device's certificate.
  • Expected Result: The certificate should still exist in the database, but its status should now be marked as Revoked.

…1356

- Added 'cert' to TYPE_CHOICES.
- Introduced 'ca' and 'blueprint_cert' ForeignKeys with organization validation.
- Updated the clean() method to clear unneeded relations, require a CA for cert types, and validate that a blueprint certificate is not already assigned to a device.

Fixes #1356
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ee27ac20-1ab6-4e5f-8c82-5dea93afc226

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a "cert" template type with CA and blueprint_cert relations, introduces a DeviceCertificate through model with auto-provisioning and revocation, wires an AbstractConfig.device_certificates M2M and m2m_changed handler, updates serializers/admin/JS, includes production and sample migrations, and adds unit and Selenium tests covering validation and lifecycle.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client/API
  participant Config as ConfigModel
  participant Template as Template
  participant DeviceCert as DeviceCertificate
  participant X509 as django-x509.Cert

  Client->>Config: assign cert Template (m2m add)
  Config->>DeviceCert: manage_device_certs (post_add) -> create/get DeviceCertificate
  DeviceCert->>Template: read ca/blueprint_cert, auto_cert flag
  DeviceCert->>X509: _auto_create_cert (clone blueprint or CA defaults)
  X509-->>DeviceCert: saved Cert instance assigned
  Client->>Config: unassign cert Template (m2m remove)
  Config->>DeviceCert: delete DeviceCertificate -> post_delete revokes X509 cert
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • openwisp/openwisp-controller#1382: Implements overlapping DeviceCertificate + certificate-template changes including device_certificates M2M wiring, Template ca/blueprint_cert fields with unassigned-choice constraints, and validation/serializer behavior.

Suggested reviewers

  • nemesifier
  • devkapilbansal

Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Bug Fixes ❌ Error The get_unassigned_certs() bug is unfixed: lacks cert_id__isnull=False filter. No regression test validates blueprint_cert choices when NULL DeviceCertificates exist. Add cert_id__isnull=False filter to get_unassigned_certs() and write a regression test that creates DeviceCertificate without cert to verify blueprint_cert field functionality.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and accurately describes the main feature being implemented: introduction of standalone certificate templates and device bindings, which aligns with the changeset's core additions of cert-type templates and DeviceCertificate model.
Linked Issues check ✅ Passed The PR comprehensively addresses all five linked issues: #1356 adds cert template type with ca/blueprint_cert fields and validation, #1377 introduces DeviceCertificate model, #1357 updates admin UI with dynamic field visibility, #1361 exposes fields in API serializers with validation, and #1358 implements lifecycle handlers for generation and revocation.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the five linked issues; query count adjustments in API tests reflect legitimate query behavior changes from new model relationships and validations without introducing unrelated functionality.
Description check ✅ Passed The pull request description includes all required sections with substantive content and demonstrates thorough manual testing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issues/1356-extend-abstract-template

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.

@stktyagi
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 26, 2026

Code Review Summary

Status: 1 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
openwisp_controller/config/base/template.py 301 The certificate-template validation change still lacks a targeted regression test
Other Observations (not in diff)

Previous findings on openwisp_controller/config/api/serializers.py and openwisp_controller/config/static/config/js/template_ui.js are resolved in this revision.

No additional issues in unchanged code.

Files Reviewed (5 files)
  • openwisp_controller/config/api/serializers.py - 0 issues
  • openwisp_controller/config/base/template.py - 1 issue
  • openwisp_controller/config/static/config/js/template_ui.js - 0 issues
  • openwisp_controller/config/tests/test_selenium.py - 0 issues
  • openwisp_controller/pki/tests/test_api.py - 0 issues

Fix these issues in Kilo Cloud


Reviewed by gpt-5.4-20260305 · 41,749 tokens

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/openwisp/openwisp-controller/issues/comments/4548211157","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/openwisp/openwisp-controller/pull/1378?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- This is an auto-generated comment: review in progress by coderabbit.ai -->\n\n> [!NOTE]\n> Currently processing new changes in this PR. This may take a few minutes, please wait...\n> \n> <details>\n> <summary>⚙️ Run configuration</summary>\n> \n> **Configuration used**: Organization UI\n> \n> **Review profile**: ASSERTIVE\n> \n> **Plan**: Pro\n> \n> **Run ID**: `33bb61f8-c083-446e-8e45-44d753e7ff7b`\n> \n> </details>\n> \n> <details>\n> <summary>📥 Commits</summary>\n> \n> Reviewing files that changed from the base of the PR and between dc55622dfd09741ac51aad38afaaa206714ca875 and 25f1a213225299ecb5dc0ae4960630f68f8d8480.\n> \n> </details>\n> \n> <details>\n> <summary>📒 Files selected for processing (3)</summary>\n> \n> * `openwisp_controller/config/base/template.py`\n> * `openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py`\n> * `tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py`\n> \n> </details>\n> \n> ```ascii\n>  __________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________\n> < I've seen things you people wouldn't believe. Inefficient loops on fire off the shoulder of Orion. I've observed algorithms unfold in the dark near the Tannhäuser Gate, and watched data structures dissolve into the void of garbage collection. All those moments will be lost in my transient GPU cache, like tears in rain. >\n>  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n>   \\\n>    \\   (\\__/)\n>        (•ㅅ•)\n>        /   づ\n> ```\n\n<!-- end of auto-generated comment: review in progress by coderabbit.ai -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-4548221491\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-4548221491\"} -->   Commit unit tests in branch `issues/1356-extend-abstract-template`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=openwisp/openwisp-controller&utm_content=1378)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

Copy link
Copy Markdown
Contributor

@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 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/template.py`:
- Around line 265-267: The help text for the auto_cert field is out of date (it
still says it's only valid for VPN templates) — update the auto_cert field's
help/verbose/help_text in the Template definition in
openwisp_controller/config/base/template.py so it matches the new behavior
(auto_cert is allowed when type == "cert" as well as when type == "vpn"); locate
the auto_cert attribute (and any admin/API serializer or form label/help_text
referencing it) and change the message to something like "Valid for 'vpn' and
'cert' template types" or equivalent clear wording that includes both types.
🪄 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: 33bb61f8-c083-446e-8e45-44d753e7ff7b

📥 Commits

Reviewing files that changed from the base of the PR and between dc55622 and 25f1a21.

📒 Files selected for processing (3)
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.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:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
🧠 Learnings (4)
📚 Learning: 2026-01-12T22:27:40.078Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:40.078Z
Learning: In test migrations under tests/openwisp2/sample_config/migrations, verify scenarios where a swappable model (CONFIG_WHOISINFO_MODEL) is extended with extra fields (e.g., an additional 'details' field) to ensure compatibility and no errors when swapping to a custom implementation. This pattern helps confirm that extending AbstractWHOISInfo via a custom model works as intended.

Applied to files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.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:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
📚 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/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.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/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
🔇 Additional comments (3)
openwisp_controller/config/base/template.py (1)

25-29: LGTM!

Also applies to: 62-83, 251-253, 271-312

openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py (1)

1-59: LGTM!

tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py (1)

1-58: LGTM!

Comment thread openwisp_controller/config/base/template.py Outdated
@openwisp-companion
Copy link
Copy Markdown

{
  "failures": [
    {
      "type": "Build/Infrastructure/Other",
      "message": "Migrations check failed! Models' changes not migrated, please run './manage.py makemigrations' to solve the issue!",
      "details": "The CI environment detected that the database migrations are out of sync. This is indicated by the error message 'Migrations check failed! Models' changes not migrated, please run './manage.py makemigrations' to solve the issue!' and the mention of a specific migration file '0005_user_expiration_date_user_user_active_expiry_idx.py'."
    }
  ]
}

CI Failure Analysis

Hello @stktyagi,
(Analysis for commit 25f1a21)

  • Migrations Out of Sync: The CI build failed because the database migrations are not up-to-date. This is indicated by the error message "Migrations check failed! Models' changes not migrated, please run './manage.py makemigrations' to solve the issue!".

Fix: Run ./manage.py makemigrations to generate the necessary migration files and then commit them to the repository.

stktyagi added 2 commits May 27, 2026 01:58
Updated previous tests and added new tests for implemetation.

Fixes #1356
Fixed help text for auto cert and updated migration files
@openwisp-companion
Copy link
Copy Markdown

Migrations Check Failed

Hello @stktyagi,
(Analysis for commit a795e09)

The CI failed because there are unapplied database migrations. This means that the database schema is out of sync with the current state of the code.

Fix:
Run the following command to apply the pending migrations:

./manage.py makemigrations

@openwisp-companion
Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@stktyagi
Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@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: 2

🤖 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/template.py`:
- Around line 252-253: The validation of certificate relations (calls to
self._validate_org_relation("ca") and
self._validate_org_relation("blueprint_cert")) is running unconditionally and
can fail on stale relations when the object is being switched away from the
"cert" type; restrict these validations to the cert branch so they only run when
the template's type is "cert" (e.g., wrap or move the
self._validate_org_relation(...) calls inside the same conditional that handles
the "cert" branch or after the type check that preserves/clears ca and
blueprint_cert), ensuring they do not run when the code path clears those fields
(see the branch that clears ca and blueprint_cert).
- Around line 304-305: The current check "if not self.config" coerces any falsy
value ([], "", False) into {}, bypassing BaseConfig.clean() validation; change
the condition to only handle missing configs by checking "if self.config is
None" (or equivalent explicit None check) so only absent configs are replaced
with {} and invalid/falsy payloads are left intact for
full_clean()/BaseConfig.clean() to reject.
🪄 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: d3d93328-e58d-41dd-a374-dffebd6d1e38

📥 Commits

Reviewing files that changed from the base of the PR and between dc55622 and b946d26.

📒 Files selected for processing (5)
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.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). (11)
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.11 | 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.10 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.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/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
🧠 Learnings (4)
📚 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/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.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/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.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/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
📚 Learning: 2026-01-12T22:27:40.078Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:40.078Z
Learning: In test migrations under tests/openwisp2/sample_config/migrations, verify scenarios where a swappable model (CONFIG_WHOISINFO_MODEL) is extended with extra fields (e.g., an additional 'details' field) to ensure compatibility and no errors when swapping to a custom implementation. This pattern helps confirm that extending AbstractWHOISInfo via a custom model works as intended.

Applied to files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
🔇 Additional comments (4)
openwisp_controller/pki/tests/test_api.py (1)

155-155: LGTM!

Also applies to: 275-275

openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py (1)

12-16: LGTM!

Also applies to: 19-44, 45-58, 59-74

tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py (1)

11-14: LGTM!

Also applies to: 17-42, 43-56, 57-72

openwisp_controller/config/base/template.py (1)

25-29: LGTM!

Also applies to: 62-83, 119-120

Comment thread openwisp_controller/config/base/template.py Outdated
Comment thread openwisp_controller/config/base/template.py Outdated
@openwisp-companion
Copy link
Copy Markdown

Migrations Check Failed

Hello @stktyagi,
(Analysis for commit b946d26)

The CI failed because there are unapplied database migrations.

Failure: Migrations check failed! Models' changes not migrated, please run './manage.py makemigrations' to solve the issue!

Fix:
Run the following command to generate the missing migrations:

./manage.py makemigrations

stktyagi and others added 2 commits May 27, 2026 09:34
Validate cert relations only inside the cert branch and Only coerce missing cert configs, not every falsy value.

Fixes #1356
Comment thread openwisp_controller/config/base/template.py
Added test for the validation branch that now skips ca / blueprint_cert checks for non-cert templates

Fixes #1356
@coveralls
Copy link
Copy Markdown

coveralls commented May 27, 2026

Coverage Status

Coverage is 98.404%issues/1356-extend-abstract-template into gsoc26-x509-certificate-generator-templates. No base build found for gsoc26-x509-certificate-generator-templates.

Fixed line too long flake error

Fixes #1356
…1377

Implemented the DeviceCertificate M2M through-model to act as a strict relational bridge between Config, Template, and django_x509.Cert.

Fixes #1377
@stktyagi stktyagi added the gsoc Part of a Google Summer of Code project label May 28, 2026
Updated test by joining the list of strings into one sentence.

Fixes #1377
@stktyagi stktyagi changed the title [feature] Extended Template model for standalone X.509 certificates #1356 [feature] Introduced standalone certificate templates and device bindings May 29, 2026
stktyagi added 2 commits May 30, 2026 23:41
Added lifecycle and general test in test api for certificate template

Fixes #1361
Added minor improvements to model structure

Fixes #1377
@stktyagi
Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

✅ Actions performed

Full review triggered.

@openwisp-companion
Copy link
Copy Markdown

Multiple Test Failures Detected

Hello @stktyagi,
(Analysis for commit 298572f)

  • Test Failures: There are 6 test failures reported. The logs indicate issues primarily related to "floorplan image not found" and potential tampering detected in API interactions. Additionally, there are several RuntimeWarning messages regarding naive datetime fields in Django models.

  • Fix: Review the tests that are failing and ensure that all necessary fixtures (like floorplan images) are correctly set up and accessible. Investigate the "tampering detected" messages to understand the context and resolve any authorization or data validation issues. Address the RuntimeWarning by ensuring datetime fields are timezone-aware.

  • Runtime Warnings: Multiple RuntimeWarning: DateTimeField ... received a naive datetime while time zone support is active. are present.

  • Fix: Ensure that all DateTimeField instances that are expected to be timezone-aware are provided with timezone-aware datetime objects. This typically involves using timezone.now() instead of naive datetime objects.

Copy link
Copy Markdown
Contributor

@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: 3

♻️ Duplicate comments (1)
openwisp_controller/config/tests/test_api.py (1)

1476-1495: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Cover blueprint_cert organization scoping in the API tests too.

This only exercises a foreign-organization ca. A regression that accepts blueprint_cert from another organization would still pass, even though both relations are now exposed on the serializer. Please add a sibling assertion for blueprint_cert scoped outside the template organization.

🤖 Prompt for 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.

In `@openwisp_controller/config/tests/test_api.py` around lines 1476 - 1495, In
test_template_create_api_org_scoping, also assert that a blueprint_cert
belonging to a different organization is rejected: create a
blueprint/blueprint_cert tied to org2 (similar to ca_org2), set
data["blueprint_cert"] = that blueprint's pk when posting to template_list in
test_template_create_api_org_scoping, then assert r.status_code == 400 and that
"blueprint_cert" appears in r.data and the error message text for
r.data["blueprint_cert"] mentions the organization/related-object mismatch
(similar wording to the existing "related CA match" assertion).
🤖 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/api/serializers.py`:
- Around line 293-296: The DeviceCertificate.objects.filter(...).delete() call
in the serializer duplicates the centralized Config.templates m2m cleanup and
causes an extra write; remove that deletion so the serializer only calls
config.templates.set(config_templates, clear=True) and let the m2m handler own
lifecycle changes for Config.templates (locate the code around DeviceCertificate
and the config.templates.set call in the serializer and delete the
DeviceCertificate.objects.filter(...).exclude(...).delete() line).
- Around line 116-141: The validate method currently always performs
Config.objects.filter(templates=self.instance).exists() for existing instances;
change it so the expensive assignment lookup only runs when the certificate
fields are being modified by first checking if "ca" in data or "blueprint_cert"
in data before executing the block that references
Config.objects.filter(...).exists() and raises ValidationError (i.e., guard the
current if self.instance and self.instance.pk: ... block with an outer condition
like if ("ca" in data or "blueprint_cert" in data) so updates to other fields
like name/default_values/config avoid the extra query).

In
`@tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py`:
- Around line 132-137: The DeviceCertificate model options in the sample
migration (migrations/0010_template_blueprint_cert_template_ca_and_more.py) are
missing the swappable option; update the model's options dict for the
DeviceCertificate migration to include "swappable":
"CONFIG_DEVICECERTIFICATE_MODEL" (so the options contain verbose_name,
verbose_name_plural, abstract, unique_together and the swappable key) to match
production migration 0064_template_blueprint_cert_template_ca_and_more.py and
ensure Django's swapper mechanism works in tests with SAMPLE_APP=True.

---

Duplicate comments:
In `@openwisp_controller/config/tests/test_api.py`:
- Around line 1476-1495: In test_template_create_api_org_scoping, also assert
that a blueprint_cert belonging to a different organization is rejected: create
a blueprint/blueprint_cert tied to org2 (similar to ca_org2), set
data["blueprint_cert"] = that blueprint's pk when posting to template_list in
test_template_create_api_org_scoping, then assert r.status_code == 400 and that
"blueprint_cert" appears in r.data and the error message text for
r.data["blueprint_cert"] mentions the organization/related-object mismatch
(similar wording to the existing "related CA match" assertion).
🪄 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: 3d2308a2-f0c4-47fa-a62b-d31870807d39

📥 Commits

Reviewing files that changed from the base of the PR and between 897429b and badf1c1.

📒 Files selected for processing (16)
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/models.py
  • tests/openwisp2/settings.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). (2)
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • tests/openwisp2/settings.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • tests/openwisp2/settings.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • tests/openwisp2/settings.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.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:

  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.py
🧠 Learnings (4)
📚 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:

  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.py
📚 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/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.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/config/tests/test_selenium.py
  • openwisp_controller/config/models.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/tests/test_template.py
📚 Learning: 2026-01-12T22:27:40.078Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:40.078Z
Learning: In test migrations under tests/openwisp2/sample_config/migrations, verify scenarios where a swappable model (CONFIG_WHOISINFO_MODEL) is extended with extra fields (e.g., an additional 'details' field) to ensure compatibility and no errors when swapping to a custom implementation. This pattern helps confirm that extending AbstractWHOISInfo via a custom model works as intended.

Applied to files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
🔇 Additional comments (32)
openwisp_controller/config/admin.py (1)

120-125: LGTM!

Also applies to: 1067-1068, 1083-1083, 1090-1091, 1103-1103

openwisp_controller/config/static/config/js/template_ui.js (1)

1-24: LGTM!

openwisp_controller/config/tests/test_selenium.py (1)

772-804: LGTM!

openwisp_controller/config/base/template.py (7)

25-29: LGTM!


40-45: LGTM!


69-92: LGTM!


128-129: LGTM!


251-278: LGTM!


280-324: LGTM!


326-362: LGTM!

openwisp_controller/config/tests/test_template.py (9)

29-29: LGTM!


973-1012: LGTM!


1014-1046: LGTM!


1048-1065: LGTM!


1067-1084: LGTM!


1086-1114: LGTM!


1116-1168: LGTM!


1170-1201: LGTM!


1203-1283: LGTM!

openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py (1)

1-152: LGTM!

tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py (1)

106-107: LGTM!

tests/openwisp2/settings.py (1)

275-275: LGTM!

openwisp_controller/config/base/device_certificate.py (3)

1-56: LGTM!


67-143: LGTM!


145-162: LGTM!

openwisp_controller/config/base/config.py (2)

66-72: LGTM!


505-520: LGTM!

openwisp_controller/config/models.py (1)

5-5: LGTM!

Also applies to: 97-105

tests/openwisp2/sample_config/models.py (1)

5-5: LGTM!

Also applies to: 99-106

openwisp_controller/config/apps.py (3)

72-72: LGTM!


98-102: LGTM!


124-128: LGTM!

Comment thread openwisp_controller/config/api/serializers.py Outdated
Comment thread openwisp_controller/config/api/serializers.py
Automatically creates and assigns a client x509 certificate
using Blueprint cloning and custom hardware OID injection.
"""
import copy
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👀

Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Good progress here @stktyagi. I focused only on what is already implemented and found one concrete lifecycle issue to fix.

instance.devicecertificate_set.get_or_create(
template=tpl, defaults={"auto_cert": tpl.auto_cert}
)
elif action in ["post_remove", "pre_clear"]:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This deletes all DeviceCertificate rows on pre_clear. The templates m2m is also cleared and re-added when templates are reordered or replaced with set(..., clear=True), so a certificate template that is still selected gets deleted, post_delete revokes its generated cert, and post_add creates a new one. Reordering templates, or an API update that keeps the same cert template, should not revoke the existing device certificate.

Please mirror the VPN client handling and delete only after the final template set is known, or only delete certificates for templates that were actually removed.

Refactors to remove the action from the m2m_changed signal, preventing accidental deletion and revocation of valid device certificates when templates are reordered or updated.

Fixes #1358
@stktyagi
Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 31, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@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: 5

🤖 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/config.py`:
- Around line 516-530: The post_clear branch currently returns early and skips
reconciliation, leaving DeviceCertificate rows for optional templates that were
removed; change the post_clear handling (in the block guarded by action ==
"post_clear" inside the SortedManyToManyField reconcile code) so that after
handling instance.is_deactivating_or_deactivated() you also run the same cleanup
used elsewhere (delete
instance.devicecertificate_set.exclude(template_id__in=instance.templates.values_list("id",
flat=True))). In practice remove or avoid the early return in post_clear and
ensure the deletion of stale DeviceCertificate rows (reference
cls.get_template_model(), templates, pk_set, instance.devicecertificate_set, and
instance.templates) so optional templates removed during the clear→re-add cycle
are cleaned up.
- Around line 531-535: The current get_or_create path creates per-config
certificates via instance.devicecertificate_set but
AbstractConfig.certificate_updated() only resolves instance.vpnclient.config and
skips DeviceCertificate-managed certs, so renewals don't mark configs dirty;
update certificate_updated (and/or add a DeviceCertificate.save handler) to
detect when the changed object is a DeviceCertificate and then locate and notify
the owning AbstractConfig(s) (e.g., the config(s) related to instance.device or
the vpnclient.config(s) that reference that DeviceCertificate) — call the same
logic used for other certificate types (mark the config modified/invalidate
checksum or call the config.certificate_updated hook) so configs referencing
DeviceCertificate certs get their checksum invalidated and changes propagated.

In `@openwisp_controller/config/base/device_certificate.py`:
- Around line 32-34: The __str__ method in device_certificate.py returns a
user-facing string "Pending Generation" that must be marked for translation;
update the method (DeviceCertificate.__str__) to wrap that literal with Django's
i18n helper (import gettext or gettext_lazy from django.utils.translation and
use _(...)) and add the corresponding import at the top of the module so the
admin list view displays the translated text.

In `@openwisp_controller/config/base/template.py`:
- Around line 251-278: The check in _validate_cert_template_changes currently
uses Config.objects.filter(templates=self).exists() which blocks edits even when
related Configs are deactivated; change the query to only consider active
assignments (e.g. filter(templates=self, status__in=[...]) or
filter(templates=self, is_active=True) depending on the Config model) so that
only configs in active/activating states prevent changing ca_id or
blueprint_cert_id; update the Config variable usage inside
_validate_cert_template_changes accordingly.

In `@openwisp_controller/config/tests/test_template.py`:
- Around line 1248-1274: The large commented subtest should be restored as an
actual test but marked skipped so it stays version-controlled; create a new test
method (e.g., test_injects_custom_mac_and_uuid_oids) that contains the existing
logic using generated_cert, mac_oid ("1.3.6.1.4.1.65901.1"), uuid_oid
("1.3.6.1.4.1.65901.2"), the mac_ext/uuid_ext lookups, and assertions against
device.mac_address and str(device.id), and decorate that method with
`@unittest.skip`("Pending django-x509#228") (or call self.skipTest with the same
message) so the subtest/self.subTest structure and assertion logic remain intact
but are explicitly skipped until the upstream issue is resolved.
🪄 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: cd0f0953-0381-4844-a2a0-10a63b5d6ba9

📥 Commits

Reviewing files that changed from the base of the PR and between 897429b and a692c77.

📒 Files selected for processing (16)
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/models.py
  • tests/openwisp2/settings.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). (10)
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=4.2.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.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.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/pki/tests/test_api.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
🧠 Learnings (4)
📚 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/pki/tests/test_api.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.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/pki/tests/test_api.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.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/pki/tests/test_api.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • tests/openwisp2/settings.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/admin.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
📚 Learning: 2026-01-12T22:27:40.078Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:40.078Z
Learning: In test migrations under tests/openwisp2/sample_config/migrations, verify scenarios where a swappable model (CONFIG_WHOISINFO_MODEL) is extended with extra fields (e.g., an additional 'details' field) to ensure compatibility and no errors when swapping to a custom implementation. This pattern helps confirm that extending AbstractWHOISInfo via a custom model works as intended.

Applied to files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
🔇 Additional comments (29)
openwisp_controller/config/base/device_certificate.py (3)

1-9: LGTM!

Also applies to: 11-31, 36-56


58-81: LGTM!


83-109: LGTM!

Also applies to: 126-163

openwisp_controller/config/models.py (1)

5-5: LGTM!

Also applies to: 97-105

tests/openwisp2/sample_config/models.py (1)

5-5: LGTM!

Also applies to: 99-106

tests/openwisp2/settings.py (1)

275-275: LGTM!

openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py (1)

1-152: LGTM!

tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py (1)

1-150: LGTM!

openwisp_controller/config/tests/test_template.py (3)

29-29: LGTM!


973-1114: LGTM!


1285-1324: LGTM!

openwisp_controller/config/api/serializers.py (6)

20-20: LGTM!


42-43: LGTM!


52-61: LGTM!


78-83: LGTM!


85-145: LGTM!


297-299: LGTM!

openwisp_controller/config/tests/test_api.py (10)

33-33: LGTM!


742-742: LGTM!


1333-1350: LGTM!


1352-1367: LGTM!


1369-1388: LGTM!


1390-1413: LGTM!


1415-1445: LGTM!


1447-1474: LGTM!


1476-1495: LGTM!


1497-1532: LGTM!

openwisp_controller/pki/tests/test_api.py (2)

275-275: 💤 Low value

Verify the 6→8 query increase comes from expected DeviceCertificate (cert) checks.

DeviceCertificate.cert is a OneToOneField(..., on_delete=models.CASCADE) and there’s a post_delete receiver, so extra ORM work is plausible—but the evidence provided doesn’t tie the additional 2 queries inside openwisp_controller/pki/tests/test_api.py (around self.assertNumQueries(8)) to those DeviceCertificate/cert lookups. Capture/inspect the exact SQL executed in that block and map it to the expected DeviceCertificate operations.


155-155: 💤 Low value

DeviceCertificate delete cascade explains the extra queries in PKI API deletions

config.DeviceCertificate is now linked to django_x509.Cert via cert = models.OneToOneField(..., on_delete=models.CASCADE) and its post_delete hook (instance.cert.revoke() when auto_cert=True) is wired via post_delete.connect(..., dispatch_uid="devicecert.post_delete"). When deleting a CA/Cert, Django must traverse these new CASCADE relationships during delete, which accounts for the increased query counts (+1 for CA deletion, +2 for Cert deletion) without implying an N+1 regression.

Comment thread openwisp_controller/config/base/config.py
Comment thread openwisp_controller/config/base/config.py
Comment thread openwisp_controller/config/base/device_certificate.py
Comment thread openwisp_controller/config/base/template.py Outdated
Comment thread openwisp_controller/config/tests/test_template.py
@openwisp-companion
Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

Improved mutation lock and string formatting
@openwisp-companion
Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (2/3).

@openwisp-companion
Copy link
Copy Markdown

Multiple Test Failures Detected

Hello @stktyagi,
(Analysis for commit 16703f1)

The CI build failed due to the following issues:

  1. Test Failures: Several tests failed, indicated by [31;1m (red) in the logs. The specific errors are not detailed in the provided logs, but the sheer number of failed tests suggests a broader issue.

Action:
Please review the test output for specific assertion errors and fix the failing tests. If the failures are related to the changes introduced in this commit, adjust the code accordingly. If the failures appear unrelated to your changes, consider if your changes might have inadvertently affected other parts of the application.

Copy link
Copy Markdown
Member

@Aryamanz29 Aryamanz29 left a comment

Choose a reason for hiding this comment

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

Good progress! @stktyagi - requested changes below:

Tip: also, don't hesitate to add one-liner comments to complex if-else ladders, or abstract them out into small functions/methods for reusability. It helps us follow the code more easily and keeps things maintainable!

Automatically creates and assigns a client x509 certificate
using Blueprint cloning and custom hardware OID injection.
"""
import copy
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could you please move inline imports to top of the file 🙏

Comment on lines +52 to +56
def save(self, *args, **kwargs):
"""Performs automatic provisioning if ``auto_cert`` is True."""
if self.auto_cert and not self.cert:
self._auto_x509()
super().save(*args, **kwargs)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

_auto_create_cert is not transactional ^

cert.save() runs, then super().save() (the DeviceCertificate). If the second save raises (constraint, signal handler error), you've leaked a Cert row with no DeviceCertificate pointing at it. Should be wrapped in transaction.atomic() eg:

from django.db import transaction

def save(self, *args, **kwargs):
    with transaction.atomic():
        if self.auto_cert and not self.cert:
            self._auto_x509()
        super().save(*args, **kwargs)

Comment on lines +111 to +125
# inject MAC and UUID as custom OIDs, prerequisite: #228 in django-x509
# mac_oid = "1.3.6.1.4.1.65901.1"
# uuid_oid = "1.3.6.1.4.1.65901.2"
# extensions.extend([
# {
# "oid": mac_oid,
# "value": f"ASN1:UTF8:string:{device.mac_address}",
# "critical": False
# },
# {
# "oid": uuid_oid,
# "value": f"ASN1:UTF8:string:{device.id}",
# "critical": False
# }
# ])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

^ Dead commented-out code committed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this is blocked by openwisp/django-x509#228 so i commented it out to make it easier to implement later and just uncomment. If that seems better i can remove it for now.

Comment on lines +1248 to +1274
# uncomment after #228 in django-x509
# with self.subTest("Injects custom MAC and UUID OIDs"):
# extensions = generated_cert.extensions
# mac_oid = "1.3.6.1.4.1.65901.1"
# uuid_oid = "1.3.6.1.4.1.65901.2"
# mac_ext = next(
# (
# ext
# for ext in extensions
# if ext.get("oid") == mac_oid
# or ext.get("name") == mac_oid
# ),
# None,
# )
# uuid_ext = next(
# (
# ext
# for ext in extensions
# if ext.get("oid") == uuid_oid
# or ext.get("name") == uuid_oid
# ),
# None,
# )
# self.assertIsNotNone(mac_ext, "MAC OID extension missing")
# self.assertIn(device.mac_address, mac_ext["value"])
# self.assertIsNotNone(uuid_ext, "UUID OID extension missing")
# self.assertIn(str(device.id), uuid_ext["value"])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Dead commented-out code committed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

same here, this test is to verify custom oid implementation currently blocked by openwisp/django-x509#228

Comment on lines +506 to +535
def manage_device_certs(cls, sender, instance, action, pk_set, **kwargs):
"""
Syncs DeviceCertificate objects when templates are added/removed
"""
if instance._state.adding or action not in [
"post_add",
"post_remove",
"post_clear",
]:
return
if action == "post_clear":
if instance.is_deactivating_or_deactivated():
instance.devicecertificate_set.all().delete()
return
if isinstance(pk_set, set):
template_model = cls.get_template_model()
templates = template_model.objects.filter(pk__in=list(pk_set)).order_by(
"created"
)
else:
templates = pk_set
if len(pk_set) != templates.filter(required=True).count():
instance.devicecertificate_set.exclude(
template_id__in=instance.templates.values_list("id", flat=True)
).delete()
if action == "post_add":
for template in templates.filter(type="cert"):
instance.devicecertificate_set.get_or_create(
template=template, defaults={"auto_cert": template.auto_cert}
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you please add a code comment to explain this if-else ladder in manage_device_certs? 🙏

Comment on lines +262 to +265
if (
current.ca_id != self.ca_id
or current.blueprint_cert_id != self.blueprint_cert_id
):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we only blocks ca_id / blueprint_cert_id mutation here. If a user keeps ca the same but changes type from cert -> generic, _clean_cert_template then sets ca = None and blueprint_cert = None on the active template - bypassing the protection and leaving DeviceCertificate rows that won't get cleaned up by the template save (only m2m signals clean them). Either block type-change-off-cert on active templates or trigger the cert cleanup explicitly.

Comment on lines +52 to +61
extra_kwargs = {
"blueprint_cert": {
"error_messages": {
"does_not_exist": _(
"This certificate does not exist or is already "
"assigned to an active device."
)
}
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Misleading error message^

does_not_exist message says "already assigned to an active device" but the queryset filter (get_unassigned_certs()) excludes any cert with a DeviceCertificate, regardless of whether the device is active/deactivated. Either tighten the filter or relax the message.

Copy link
Copy Markdown
Member Author

@stktyagi stktyagi May 31, 2026

Choose a reason for hiding this comment

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

Thank you for this one, let me share a bit of context on why I did that in the first place:
If a user tries to assign a certificate that is already in use, DRF falls back to its native, generic relational error:
{'blueprint_cert': [ErrorDetail(string='Invalid pk "37" - object does not exist.', code='does_not_exist')]} so to avoid unhelpful error message I added that but you are right and we can improve the wording.
I will update the string to:
"This certificate does not exist or is already assigned to a device configuration profile."
This keeps our helpful error override intact while making the text technically accurate. If you have any other suggestion to do this better I'll appreciate that 🙏

Comment on lines +85 to +88
def validate(self, data):
"""
Explicitly validate certificate template fields and locks for the API.
"""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you please add a code comment below for each if-else ladder in validate()? 🙏

@openwisp-companion
Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

- Wrap DeviceCertificate.save() in transaction.atomic() to ensure
  automatic X.509 provisioning does not leak orphaned rows on failure.
- Add validation to block changing the template type away from 'cert'
  on templates currently assigned to active devices.
@stktyagi
Copy link
Copy Markdown
Member Author

stktyagi commented Jun 1, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (1)
openwisp_controller/config/base/config.py (1)

518-522: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

post_clear still leaks stale DeviceCertificate rows on real clears.

For active configs this branch returns without any reconciliation, so any authoritative templates.clear() path leaves old devicecertificate_set rows behind. DeviceDetailSerializer.update() still does exactly that during organization changes, and direct config.templates.clear() has the same effect, so certificates can remain linked and unreconciled even though their templates are no longer assigned. Please add an explicit cleanup path for non-reorder clears, or pass enough context to distinguish sortedm2m reordering from a real clear.

🤖 Prompt for 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.

In `@openwisp_controller/config/base/config.py` around lines 518 - 522, The
post_clear branch currently only deletes DeviceCertificate rows when
instance.is_deactivating_or_deactivated() is true, which allows real clears
(e.g., Config.templates.clear() or DeviceDetailSerializer.update() during org
changes) to leak stale devicecertificate_set entries; modify the handler for
action == "post_clear" in the config cleanup logic so that it also performs an
explicit reconciliation/cleanup of instance.devicecertificate_set for
non-deactivate clears (or use passed context to detect a sortedm2m reorder vs a
true clear) — specifically, update the post_clear logic that checks
instance.is_deactivating_or_deactivated() to also remove or reconcile
certificates when templates are cleared (and/or accept a flag from
DeviceDetailSerializer.update()/templates.clear() to indicate a real clear) so
stale DeviceCertificate rows are deleted.
🤖 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/template.py`:
- Around line 40-44: get_unassigned_certs() currently builds assigned_cert_ids
from DeviceCertificate.objects.values_list("cert_id", flat=True) which can
include NULL and break the exclude(id__in=...) query; update the query to omit
NULL cert IDs (e.g., use
DeviceCertificate.objects.exclude(cert_id__isnull=True).values_list("cert_id",
flat=True) or .filter(cert_id__isnull=False).values_list(...)) so
assigned_cert_ids contains only real IDs before using it in
Cert.objects.exclude(id__in=assigned_cert_ids) in get_unassigned_certs().

---

Duplicate comments:
In `@openwisp_controller/config/base/config.py`:
- Around line 518-522: The post_clear branch currently only deletes
DeviceCertificate rows when instance.is_deactivating_or_deactivated() is true,
which allows real clears (e.g., Config.templates.clear() or
DeviceDetailSerializer.update() during org changes) to leak stale
devicecertificate_set entries; modify the handler for action == "post_clear" in
the config cleanup logic so that it also performs an explicit
reconciliation/cleanup of instance.devicecertificate_set for non-deactivate
clears (or use passed context to detect a sortedm2m reorder vs a true clear) —
specifically, update the post_clear logic that checks
instance.is_deactivating_or_deactivated() to also remove or reconcile
certificates when templates are cleared (and/or accept a flag from
DeviceDetailSerializer.update()/templates.clear() to indicate a real clear) so
stale DeviceCertificate rows are deleted.
🪄 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: 4b38b811-4f6b-4a60-91c5-a30808887443

📥 Commits

Reviewing files that changed from the base of the PR and between 897429b and 212b839.

📒 Files selected for processing (16)
  • openwisp_controller/config/admin.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/pki/tests/test_api.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/models.py
  • tests/openwisp2/settings.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). (11)
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}

📄 CodeRabbit inference engine (Custom checks)

**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp}: Flag potential security vulnerabilities in code
Avoid unnecessary comments or docstrings for code that is already clear
Code formatting is compact and readable. Do not add excessive blank lines, especially inside function or method bodies
Flag unused or redundant code
Ensure variables, functions, classes, and files have descriptive and consistent names
New code must handle errors properly: log errors that cannot be resolved by the user with error level, log unusual conditions with warning level, log important background actions with info level, and provide user-facing messages for errors that the user can solve autonomously

Files:

  • tests/openwisp2/settings.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sql}

📄 CodeRabbit inference engine (Custom checks)

Flag obvious performance regressions, such as heavy loops, repeated I/O, or unoptimized queries

Files:

  • tests/openwisp2/settings.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
**/*.{js,ts,tsx,jsx,py,java,go,cs,rb,php,c,cpp,h,hpp,sh,bash,sql}

📄 CodeRabbit inference engine (Custom checks)

Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable

Files:

  • tests/openwisp2/settings.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/static/config/js/template_ui.js
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.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:

  • tests/openwisp2/settings.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
🧠 Learnings (4)
📚 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:

  • tests/openwisp2/settings.py
  • tests/openwisp2/sample_config/models.py
  • openwisp_controller/config/models.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
📚 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/config/models.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.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/config/models.py
  • openwisp_controller/config/admin.py
  • openwisp_controller/pki/tests/test_api.py
  • openwisp_controller/config/api/serializers.py
  • openwisp_controller/config/apps.py
  • openwisp_controller/config/tests/test_selenium.py
  • openwisp_controller/config/base/config.py
  • openwisp_controller/config/tests/test_template.py
  • openwisp_controller/config/base/template.py
  • openwisp_controller/config/tests/test_api.py
  • openwisp_controller/config/base/device_certificate.py
  • openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py
📚 Learning: 2026-01-12T22:27:40.078Z
Learnt from: nemesifier
Repo: openwisp/openwisp-controller PR: 1175
File: tests/openwisp2/sample_config/migrations/0008_whoisinfo_organizationconfigsettings_whois_enabled.py:18-67
Timestamp: 2026-01-12T22:27:40.078Z
Learning: In test migrations under tests/openwisp2/sample_config/migrations, verify scenarios where a swappable model (CONFIG_WHOISINFO_MODEL) is extended with extra fields (e.g., an additional 'details' field) to ensure compatibility and no errors when swapping to a custom implementation. This pattern helps confirm that extending AbstractWHOISInfo via a custom model works as intended.

Applied to files:

  • tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py
🔇 Additional comments (16)
tests/openwisp2/settings.py (1)

275-275: LGTM!

openwisp_controller/config/static/config/js/template_ui.js (1)

1-25: LGTM!

openwisp_controller/config/admin.py (2)

118-126: LGTM!


1067-1068: LGTM!

Also applies to: 1083-1083, 1090-1091, 1103-1103

openwisp_controller/config/apps.py (2)

72-72: LGTM!


98-102: LGTM!

Also applies to: 124-128

openwisp_controller/config/tests/test_selenium.py (1)

772-805: LGTM!

openwisp_controller/config/base/device_certificate.py (4)

13-52: LGTM!


54-84: LGTM!


86-151: LGTM!


153-163: LGTM!

tests/openwisp2/sample_config/migrations/0010_template_blueprint_cert_template_ca_and_more.py (1)

13-150: LGTM!

openwisp_controller/pki/tests/test_api.py (1)

155-155: ⚡ Quick win

Confirm the delete query bump matches Django’s related-object handling

openwisp_controller/pki/tests/test_api.py now expects more queries for deletes (test_ca_delete_api: 6, test_cert_delete_api: 8). With the new relations:

  • DeviceCertificate.certdjango_x509.Cert uses on_delete=CASCADE
  • Template.blueprint_certdjango_x509.Cert uses on_delete=SET_NULL
  • DeviceCertificate has a post_delete hook that can call instance.cert.revoke() when auto_cert=True

That explains why cert deletion can add more work than CA deletion (CASCADE on DeviceCertificate.cert plus SET_NULL updates for Template.blueprint_cert). Since pki/tests/test_api.py doesn’t explicitly create Template/DeviceCertificate objects, the increase should be coming from Django’s delete collector existence checks/SET_NULL handling—not per-row churn.

Check the actual SQL for these two DELETE endpoints and confirm the extra queries are constant-time (no N+1) even when multiple related DeviceCertificate/Template rows exist.

openwisp_controller/config/base/template.py (1)

25-29: LGTM!

Also applies to: 69-92, 128-129, 251-341, 351-355, 369-370, 378-378

openwisp_controller/config/tests/test_api.py (1)

33-33: LGTM!

Also applies to: 742-742, 1333-1540

openwisp_controller/config/migrations/0064_template_blueprint_cert_template_ca_and_more.py (1)

1-152: LGTM!

Comment thread openwisp_controller/config/base/template.py Outdated
Fixed NULL handling in get_unassigned_certs() to avoid empty blueprint_cert choices
@openwisp-companion
Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (1/3).

@openwisp-companion
Copy link
Copy Markdown

The CI is failing due to transient infrastructure issues (not related to your code). I have restarted the failed jobs automatically (2/3).

Updated get_unassigned_certs to include revoked certificates
@openwisp-companion
Copy link
Copy Markdown

Dependency Conflict in pip install

Hello @stktyagi,
(Analysis for commit ef90a6c)

There is a dependency conflict between openwisp-controller and openwisp-ipam.
openwisp-controller requires django-reversion~=6.1.0, while openwisp-ipam requires django-reversion~=6.2.0.

To resolve this, you should update the version specifiers in your requirements.txt or setup.py to be compatible. For example, you could try loosening the range for django-reversion to allow either version, or align them to a common compatible version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement gsoc Part of a Google Summer of Code project

Projects

Development

Successfully merging this pull request may close these issues.

4 participants