Skip to content

fix(starter_sp_mis): make registry access control persist and enforce - #411

Open
emjay0921 wants to merge 3 commits into
19.0from
fix/1142-spmis-registry-restriction
Open

fix(starter_sp_mis): make registry access control persist and enforce#411
emjay0921 wants to merge 3 commits into
19.0from
fix/1142-spmis-registry-restriction

Conversation

@emjay0921

Copy link
Copy Markdown
Contributor

Why is this change needed?

OP#1142 — SPMIS settings unable to toggle off. Test pass.

The "Restrict Registry Edits to Admin Only" setting could not be turned off, and did not restrict anything when it was on.

Odoo stores a False config_parameter by deleting the row, and default_get falls back to the field default when the row is missing — so default=True made "off" unrepresentable and the toggle sprang back on at every reload. Worse, the enforcement side read a missing row as False, so unticking the box in fact disabled the restriction while the form went on claiming the registry was locked.

Enforcement was client-side only: JavaScript hid buttons with display: none and nothing on the server refused the write, so RPC and import went straight through. The list "New" button was never hidden either, because the patch assigned a canCreate property that Odoo 19's ListController does not read — its template gates on activeActions.create.

How was the change implemented?

  • Persistenceset_values writes the value explicitly as a string, so "off" is a stored fact rather than an absence. The install default moved to data/config_parameters.xml, now noupdate so an upgrade stops silently re-locking a registry an administrator deliberately opened.
  • Enforcement — a _check_access override on res.partner. That is the single chokepoint behind check_access, has_access and the ORM's own create/write/unlink guards, so one override refuses the change on every path and removes New/Edit/Delete from registry views for free: ir.ui.view._postprocess_access_rights stamps create="false" onto an arch whenever has_access('create') is False.
  • Scoped to registrants, so the setting cannot lock the Contacts app.
  • registry_restriction.js (260 lines) and the controller it called are deleted — the DOM hiding is no longer needed and never worked on lists anyway.

New unit tests

14, in spp_starter_sp_mis/tests/test_registry_restriction.py, covering the toggle round-trip in both directions, that the form and the enforcement can never disagree, that non-admins are refused create/write/unlink on registrants, that admins are exempt, that plain contacts are unaffected, and that the registry arch drops create for a restricted user.

Unit tests executed by the author

Re-run after merging 19.0 up:

spp_starter_sp_mis    0 failed, 0 error(s) of 16 tests
spp_mis_demo_v2       0 failed, 0 error(s) of 273 tests

spp_mis_demo_v2 is the only dependent module. Four of its access-control tests asserted the plain role model and were only passing because the restriction did nothing; they now pin the switch off explicitly, since they measure ACLs rather than this setting.

./spp lint clean.

How to test manually

  1. Settings → SP-MIS Settings. Untick Restrict Registry Edits to Admin Only, save, navigate away and back — it must stay unticked. Tick it again and confirm that sticks too.
  2. Upgrade the module with it unticked; it must not be silently re-enabled.
  3. As a Global Registrar, with the restriction on: Individuals and Groups show no New, the form is read-only, and an API create of a registrant is refused.
  4. As an administrator, everything still works.
  5. With the restriction on, the Contacts app is unaffected.

Related links

https://openspp.openproject.com/work_packages/1142

Reviewer notes

This is a real behaviour change for the SP-MIS bundle. The setting ships enabled and now actually works, so registrars lose registrant create/edit/delete by default where the restriction was previously decorative.

One residual on upgrade: databases where an administrator had already unticked the setting have no parameter row, because the old code deleted it. The noupdate record is recreated as True once, then stays where it is put.

The "Restrict Registry Edits to Admin Only" setting could not be turned
off, and did not restrict anything when on.

Odoo stores a False config_parameter by deleting the row, and default_get
falls back to the field default when the row is missing, so default=True
made "off" unrepresentable: the toggle sprang back on at every reload.
The enforcement side read a missing row as False, so unticking the box in
fact disabled the restriction while the form went on claiming the registry
was locked. Persist the value explicitly as a string and take the install
default from the data file, which is now noupdate so an upgrade stops
re-locking a registry an administrator deliberately opened.

Enforcement was client-side only: JavaScript hid buttons with display:none
and nothing on the server refused the write, so RPC and import went
straight through. The list "New" button was never hidden either, because
the patch assigned a canCreate property that Odoo 19's ListController does
not read - its template gates on activeActions.create.

Replace all of it with a _check_access override on res.partner. That is
the single chokepoint behind check_access, has_access and the ORM's own
create/write/unlink guards, so one override refuses the change on every
path and removes New/Edit/Delete from registry views for free:
ir.ui.view._postprocess_access_rights stamps create="false" onto an arch
whenever has_access('create') is False. Scoped to registrants so the
setting cannot lock the Contacts app.

The demo access-control tests assert the plain role model and were only
passing because the restriction did nothing, so they now pin the switch
off explicitly.
@emjay0921
emjay0921 marked this pull request as ready for review August 12, 2026 02:32
Comment thread spp_starter_sp_mis/models/res_config_settings.py Fixed
Comment thread spp_starter_sp_mis/models/res_partner.py Fixed
Comment thread spp_starter_sp_mis/models/res_partner.py Fixed
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.54%. Comparing base (c33d3cb) to head (1bf76f6).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #411      +/-   ##
==========================================
+ Coverage   71.49%   71.54%   +0.04%     
==========================================
  Files         243      242       -1     
  Lines       20785    20814      +29     
==========================================
+ Hits        14860    14891      +31     
+ Misses       5925     5923       -2     
Flag Coverage Δ
spp_base_common 91.07% <ø> (ø)
spp_dci_demo 94.28% <ø> (ø)
spp_mis_demo_v2 70.38% <ø> (ø)
spp_programs 65.27% <ø> (ø)
spp_registry 87.22% <ø> (+0.07%) ⬆️
spp_security 69.56% <ø> (ø)
spp_starter_sp_mis 100.00% <100.00%> (+13.33%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_starter_sp_mis/__init__.py 100.00% <ø> (ø)
spp_starter_sp_mis/models/__init__.py 100.00% <100.00%> (ø)
spp_starter_sp_mis/models/res_config_settings.py 100.00% <100.00%> (ø)
spp_starter_sp_mis/models/res_partner.py 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

CI's semgrep flags odoo-sudo-without-context on all three sudo() calls.
Each is deliberate, so record why next to it:

- writing the config parameter is a Settings-manager operation, and the
  settings form is already gated on that group;
- reading it is how the guard decides whether to withhold access, so every
  user has to be able to read it;
- filtering on is_registrant as the acting user would recurse straight back
  into the access check being evaluated.

The pragma has to sit on the line immediately above the match, not at the
head of the comment block, or semgrep does not associate the two.

Also applies ruff's preferred spacing on a docstring that opens with a
quoted word.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants