Repository navigation
Releases: krasotinpa/netbox-passwork-plugin
Releases · krasotinpa/netbox-passwork-plugin
Release list
v1.7.2
Removed
- Two empty, unused files:
static/netbox_passwork/styles.css(never loaded by any page) andtemplates/netbox_passwork/secret_detail.html(thesecrets/<id>/detail/endpoint returns JSON and renders no template). No behaviour change
v1.7.1
Changed
- License hygiene per Apache Software Foundation guidance (license type unchanged, still Apache-2.0): new
NOTICEfile shipped in the sdist and wheel alongsideLICENSE;LICENSErestored to the canonical Apache License 2.0 text;SPDX-License-Identifier: Apache-2.0headers in the project's source files; a "License of contributions" section inCONTRIBUTING.md; a trademark disclaimer for NetBox and Passwork in the README
v1.7.0
Added
- Support for NetBox 4.7: the full test suite passes on NetBox 4.7.2 (Django 6.1) with no code changes, and the Passwork tabs on Device, Virtual Machine and Service were checked in a browser. CI now tests NetBox 4.5.10, 4.6.10 and 4.7.2 on Python 3.12 / 3.14
- README: NetBox 4.7 needs PostgreSQL 15+; the compatibility table now covers 1.3–1.6 and 1.7
v1.6.0
Changed
- Breaking / security (#1): every object-scoped operation (secrets list, secret detail, reveal, copy, binding create, binding delete) now also requires
viewaccess to the bound NetBox object (dcim.view_device/virtualization.view_virtualmachine/ipam.view_service), evaluated against the user'sObjectPermissionconstraints (ADR-0002). Users relying on plugin permissions alone, without aviewpermission on the object type, lose access to those secrets — grant the objectviewpermission (constrained or not) alongside the plugin permissions. An object outside the user's constraints now answers404 object_not_found(matching NetBox core behaviour;binding_not_foundon binding deletion), checked before the binding lookup so binding existence is not disclosed. Bindings whose object was deleted are unreadable but remain deletable withdelete_bindingalone POST bindings/now validates the target: a non-integerobject_idreturns400 invalid_object_id, and a missing (or hidden) object returns404 object_not_foundinstead of creating a dangling binding
Fixed
- Secret custom fields (and the Password row of the detail card) now re-mask automatically after
SECRET_REVEAL_TIMEOUT, the same way the main password does — previously a revealed value stayed on screen until the eye icon was clicked again (#30). Hiding a field no longer sends an extra reveal request, so it no longer writes a spurious audit entry
v1.5.0
Changed
- The Bind Passwork secret modal is now an Explorer-style browser (spec #26): a folder tree on the left (vaults are roots; a vault's folders load as one flat list per vault), the selected node's direct children on the right as a Name/Login table with clickable breadcrumbs, and a single click on a folder in the right pane opening it with the tree kept in sync. A vault node now shows only its top-level folders and root passwords instead of every password in the vault
- Picker search: starts from 3 characters as you type (debounced, stale responses are discarded), results are a flat table with each secret's path, clicking a result jumps to its folder with the tree expanded ("show in folder"), clearing the box returns to the folder you were in, and a "This vault only" checkbox scopes the search to the selected vault (new
vault_idparameter ofGET picker/search/; the Passwork request now uses the documented POST search variant) - Picker errors: a vault that answers 403 stays in the tree marked "no access" while other vaults keep working, and a Passwork timeout shows a retryable message naming the
PASSWORK_REQUEST_TIMEOUTsetting
Added
GET picker/folders/<vault_id>/folders/— the vault's flat folder list backing the picker tree (gateway operationlist_vault_folders())
v1.4.0
Added
- CI runs the Python test suite against a real NetBox (PostgreSQL + Redis service containers, matrix of NetBox 4.5/4.6 × Python 3.12/3.14), plus
makemigrations --checkandmanage.py check testing/configuration.py— a ready-made NetBox configuration for running the test suite, used by CI and usable locally- README: a compatibility matrix (plugin ↔ NetBox ↔ Python), the runtime dependency list, a Support section (where to report bugs, how to report a vulnerability privately) and a plugin icon (
docs/img/icon.svg, CC BY 4.0) - README: a Screenshots section walking through the plugin — the Passwork tab, the secret picker asking for a login, the login and two-factor modals, and the secrets of a bound object
Changed
- Breaking:
requires-pythonis now>=3.12, matching NetBox 4.5+, which does not run on Python 3.11
Fixed
- README: the minimum PostgreSQL version is 14, as required by NetBox 4.5 (13 was stated)
- Secrets tab: with no active Passwork session the header button now reads "Authenticate" and opens the login modal directly, instead of "Bind secret" leading into a picker that can only fail with 401; after a successful login (including TOTP) it switches back to "Bind secret"
- Picker: clicking a vault in the Bind Passwork secret modal now shows its contents in the right pane — subfolders (with drill-down) and passwords — instead of sending the folder ID to the text search and always showing "No secrets found" (new endpoint
GET picker/folders/<vault_id>/items/, backed by Passwork's/api/v1/foldersand/api/v1/itemslisting endpoints)
v1.3.1
Fixed
- The wheel no longer ships
netbox_passwork/tests(it was pulled in as package data despite thepackages.findexclusion, which also produced a setuptools warning during the build)
v1.3.0
Changed
- All plugin API errors now share one body format
{"code", "detail"}— the remaining endpoints (GET secrets/,POST bindings/,DELETE bindings/<id>/,GET audit/) and the NetBox permission checks (not_authenticated/netbox_permission_denied) were aligned with the Passwork-facing ones:detailis always present, the extraparamfield ofinvalid_param(audit filters/pagination) is folded intodetail,status: "error"is gone. Values ofcode, routes and successful bodies are unchanged - Passwork-facing endpoints (login/TOTP/detail/copy/picker) answer
405 Method Not Allowedfor an unsupported HTTP method before touching the Passwork session (previously401without a session), andOPTIONSno longer requires a Passwork session - Picker endpoints (
GET picker/folders/,GET picker/search/) now go through the Passwork gateway like the other Passwork-facing views: a Passwork 401 in the picker returns HTTP 401pw_session_expiredand a Passwork 403 returns HTTP 403pw_access_deniedinstead of HTTP 200 with an empty list; error bodies are uniform{"code", "detail"}(missing_querygained adetail), successful bodies (arrays of vaults / items) are unchanged - Secret detail (
GET secrets/<pw_id>/detail/) and copy (POST secrets/<pw_id>/copy/) endpoints now go through the Passwork gateway (like login/TOTP): the order of checks is unchanged (NetBox permission →reveal_secretforreveal=true→ Passwork session → parameters → binding → Passwork → audit), error bodies are uniform{"code", "detail"}(invalid_object_idandbinding_not_foundgained adetail), successful bodies are unchanged - Package metadata (
pyproject.toml): author, PEP 639 license expression, project URLs; tests are no longer shipped in the wheel - First public release. The project is now open source under Apache-2.0; the documentation in
docs/, the code comments and the helper scripts are in English
Added
LICENSE(Apache-2.0) and a License section in README- Published on PyPI:
pip install netbox-passwork;update.shnow upgrades from PyPI by default - GitHub Actions CI (
.github/workflows/ci.yml): ruff, the JS tests and a package build/twine checkon every push tomainand every pull request (the Python tests still run locally — they need a NetBox installation) - Issue and pull request templates (
.github/ISSUE_TEMPLATE/,.github/PULL_REQUEST_TEMPLATE.md) and CI/license badges in the README scripts/test.sh— single entry point for lint / Python tests / JS tests with a markdown summary for PRs;scripts/release.sh— release preparation (release PR) and publishing (signed tag, sdist/wheel, GitHub Release)
Fixed
- Changing
SESSION_ENCRYPT_KEYno longer leaves users with a permanent401: a Passwork session record that cannot be decrypted is dropped (with a warning in the log) and the user is asked to log in to Passwork again (401 pw_not_authenticated); unexpected exceptions while reading the Passwork session are no longer masked as401 - A
403from Passwork on token refresh (/sessions/refresh) is treated like401— the Passwork session is dropped and the client gets401 pw_session_expired— instead of being silently accepted as a successful refresh
Removed
- Unused
passwork-pythondependency (the plugin uses its ownrequests-based client;requestsis now declared explicitly) and the stalerequirements.txt— dev dependencies live only in[project.optional-dependencies].dev - Internal modules/helpers superseded by the Passwork gateway:
netbox_passwork/middleware.py(PassworkSessionMiddlewaremixin), the Fernet helpers inutils.py(fernet_encrypt/fernet_decrypt), andPassworkAuthClient.encrypt_session/decrypt_sessiontogether with itssession_encrypt_keyconstructor argument. No configuration change is required (MIDDLEWAREwas never touched); existing Passwork sessions stay valid after the upgrade — the session record format is unchanged
Internal
- Passwork gateway (
netbox_passwork/gateway.py, see ADR-0001): one deep module owns the Passwork session (reading/decrypting/refreshing/encrypting/writing it in the Django session, the only place that reads the plugin config and the only Fernet implementation) and exposes six operations (login,confirm_totp,get_item,list_vaults,search_items,require_session); Passwork refusals are raised asPassworkErrorsubclasses carryingcode/http_status/detail. All Passwork-facing views inherit one basePassworkView(NetBox permission → 405 → gateway → Passwork session → method →PassworkError/ApiError→ JSON error). The HTTP client (passwork_client.py) no longer depends on Django (everything via the constructor) and gained publiclist_vaults/search_items;urllib3.disable_warnings()is called only when SSL verification is off. Within one request the Passwork session record is read and decrypted once. This also removed the duplicated Fernet implementation, the repeated localimport jsoncalls and the unconditionalurllib3.disable_warnings()at import time - Tests: the gateway is swapped in views through one explicit seam (
as_view(gateway_factory=...),FakeGatewayinconftest.py); nounittest.mockleft in the plugin tests (onlyresponsesfor HTTP); new end-to-end release scenario on the real gateway (login → TOTP → list → reveal → copy → picker → binding) and a compatibility test for session records written by ≤ 1.2.x