Skip to content

security(roles): scope program/CR roles to Tier-3 registry read (drop registry-search menu) - #353

Draft
gonzalesedwin1123 wants to merge 6 commits into
19.0from
security-program-cr-registry-viewer
Draft

security(roles): scope program/CR roles to Tier-3 registry read (drop registry-search menu)#353
gonzalesedwin1123 wants to merge 6 commits into
19.0from
security-program-cr-registry-viewer

Conversation

@gonzalesedwin1123

Copy link
Copy Markdown
Member

Problem

Role links added in the OP#951 menu audit gave several program/CR-scoped roles the Tier-2
spp_registry.group_registry_viewer group:

  • spp_programs: Program Viewer
  • spp_change_request_v2: CR Requestor, CR Local Validator, CR HQ Validator

group_registry_viewer gates the standalone Registry Search portal menu
(spp_registry_search.menu_registry_search) — a broad registrant-PII enumeration surface (name, ID
number, phone, email) — on top of the registrant read ACLs. So a read-only "Program Viewer" (and the
CR roles) got the full registry browse/search UI, not just the registrant data needed for the
program/CR records they work with.

Important nuance (report framing corrected): OpenSPP does not scope the registry per-program.
Row-level need-to-know is area-based (spp_area rule_res_partner_area_filter, keyed on
user.center_area_ids, populated from local role assignments); it is a no-op for global roles, which
see the whole registry by design. group_registry_restrict_self scopes to the user's own partner
(self-service), not per-program. So this change does not (and cannot, without a new feature) make these
roles see "only their program's beneficiaries" — it removes the Registry Search UI surface while
leaving the underlying RPC read governed by the area filter. Severity: medium (over-broad PII
enumeration UI for program/CR roles).

Fix

Switch the four roles from Tier-2 group_registry_viewer to Tier-3 group_registry_read:

  • Tier-3 group_registry_read grants the same registrant read ACLs on res.partner,
    spp.registry.id, spp.phone.number (defined in spp_base_common) — so all registrant
    cross-reference reads keep working — but it does not gate the Registry app menu.
  • The CR roles also imply Tier-3 read/write via their group_cr_* chain, so read is doubly preserved.
  • spp.cycle / spp.cycle.membership / spp.program.membership read for Program Viewer comes from
    group_programs_viewer, independent of the registry group — unaffected.
  • This mirrors the existing Program Cycle Approver precedent, which already uses Tier-3 for exactly
    this reason.

Migration: user_roles.xml is noupdate="1" and the roles ship in the 2026.07 release, so a
plain upgrade would keep the old link. Added post-migration.py for each module
(spp_programs/migrations/19.0.2.2.2, spp_change_request_v2/migrations/19.0.3.0.3) that unlinks the
viewer group, links the read group, and re-materializes already-assigned users' group membership via
res.users.role.action_update_users().

Tests

Test-first (red → green). New tests in each module assert that each role, once assigned to a fresh user
and synced, does not carry group_registry_viewer (would fail before the fix) but does keep
group_registry_read and can functionally read a registrant's res.partner/spp.registry.id/
spp.phone.number (and Program Viewer keeps group_programs_viewer).

./spp t spp_programs0 failed of 680; ./spp t spp_change_request_v20 failed of 336.
No existing tests removed or modified.

Scope / notes

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.72%. Comparing base (208d975) to head (f228b96).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #353      +/-   ##
==========================================
+ Coverage   70.17%   71.72%   +1.54%     
==========================================
  Files         205      282      +77     
  Lines       17751    20577    +2826     
==========================================
+ Hits        12457    14759    +2302     
- Misses       5294     5818     +524     
Flag Coverage Δ
spp_api_v2_change_request 66.53% <ø> (ø)
spp_programs 65.30% <ø> (+0.02%) ⬆️
spp_security 69.56% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 79 files 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.

@gonzalesedwin1123

Copy link
Copy Markdown
Member Author

Staff-review residual filed as follow-up: #355global_role_program_validator still carries Tier-2 group_registry_viewer (Registry Search menu). Left out of this PR's scope (it holds registry write, so the menu is more defensible); tracked for consistency with Cycle Approver and the roles fixed here.

gonzalesedwin1123 added a commit that referenced this pull request Aug 10, 2026
The changed-field derivation reads apply_mapping_ids, which only the
field_mapping apply strategy populates. For a type with dynamic approval and
a custom strategy the mapping set is empty, so the derivation returned an
empty set rather than None — which makes the field-scoped conflict filter
match nothing and drives duplicate similarity to 0, disabling both checks.
That was strictly more permissive than the selected_field_name path it
replaces, which is set regardless of strategy.

No shipped CR type enables dynamic approval today, so this was latent rather
than live, but it is reachable by configuration. Now returns None (use the
full configured field set) unless the type is field_mapping with mappings
present, with a regression test covering the custom-strategy case.

Renumbered to 19.0.3.1.5: 19.0 carries 19.0.3.1.1, so the previous
19.0.3.0.3 would have been a downgrade (and collided with #353).
Add tests asserting Program Viewer and the CR Requestor/Local/HQ Validator
roles do NOT confer spp_registry.group_registry_viewer (which gates the
Registry Search portal menu) but DO keep registrant read (res.partner,
spp.registry.id, spp.phone.number) via Tier-3 group_registry_read.
… viewer menu

Program Viewer, CR Requestor, CR Local Validator and CR HQ Validator implied
spp_registry.group_registry_viewer (Tier-2), which gates the standalone
Registry Search portal menu and exposed a broad registrant-PII enumeration
surface to these program/CR-scoped roles. Switch them to Tier-3
group_registry_read, which grants the same registrant read ACLs (defined in
spp_base_common; also provided to CR roles via the group_cr_* chain) without
the Registry app menu.

user_roles.xml is noupdate=1 and the roles ship in the 2026.07 release, so add
post-migrations (spp_programs 19.0.2.2.2, spp_change_request_v2 19.0.3.0.3)
that unlink the viewer group, link the read group, and re-sync already-assigned
users via action_update_users.

Reduces the registry-search UI surface; underlying RPC read scope is unchanged
(governed by the area filter rule_res_partner_area_filter).
spp_programs 19.0.2.2.0 -> 19.0.2.2.2 (dodges #336's .2.1);
spp_change_request_v2 19.0.3.0.0 -> 19.0.3.0.3 (dodges #264 .0.1 / #261 .0.2).
README.rst / index.html to be regenerated from CI's pinned generator.
…y-viewer scoping

README.rst / index.html regenerated from CI's pinned oca-gen output for
spp_programs (19.0.2.2.2) and spp_change_request_v2 (19.0.3.0.3); test files
reformatted to CI's ruff-format line length (local hook env differed).
…ration

19.0 now carries 19.0.3.1.1, so the previous 19.0.3.0.3 was below the
installed version. Odoo selects a migration script only when the installed
version is strictly below it, so the role-swap script would never have run
on any database already upgraded to 3.1.1 — and because the role data is
noupdate, the migration is the only carrier of this fix for existing
deployments. The directory moves with the version; the script itself reads
no version and needed no edit.

The previous 19.0.3.0.3 also collided with #343, which claimed the same
version.
@gonzalesedwin1123
gonzalesedwin1123 force-pushed the security-program-cr-registry-viewer branch from 5f743ea to 2845af9 Compare August 10, 2026 06:13
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.

1 participant