diff --git a/docker-compose.yml b/docker-compose.yml index 9ef28f0d1..9d293b23e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: POSTGRES_DB: openspp volumes: - postgres_data:/var/lib/postgresql - - ./docker/postgresql.conf:/etc/postgresql/postgresql.conf:ro + - ./docker/postgresql.conf:/etc/postgresql/postgresql.conf:ro,z healthcheck: test: ["CMD-SHELL", "pg_isready -U odoo -d postgres"] interval: 5s diff --git a/spp_consent/README.rst b/spp_consent/README.rst index a025e6cd0..ac3e8493b 100644 --- a/spp_consent/README.rst +++ b/spp_consent/README.rst @@ -193,6 +193,18 @@ Dependencies Changelog ========= +19.0.2.0.2 +~~~~~~~~~~ + +- fix(security): the Tier-3 ``group_registry_read`` group can read the + consent models the registrant form depends on. This module extends + that form, and its models were granted only to the Tier-2 + ``group_registry_viewer`` group, so a Tier-3-scoped role hit an + AccessError opening a registrant. The two wizard models are + deliberately not included: their entry points are restricted to the + officer and manager tiers, so read access would grant nothing usable + while exposing other users' in-progress wizard rows. + 19.0.2.0.1 ~~~~~~~~~~ diff --git a/spp_consent/__manifest__.py b/spp_consent/__manifest__.py index 5c9570502..1fb338285 100644 --- a/spp_consent/__manifest__.py +++ b/spp_consent/__manifest__.py @@ -3,7 +3,7 @@ { "name": "OpenSPP Consent", "category": "OpenSPP", - "version": "19.0.2.0.1", + "version": "19.0.2.0.2", "summary": """DPV-aligned consent management for social protection programs. Implements ISO/IEC TS 27560:2023 consent record information structure diff --git a/spp_consent/readme/HISTORY.md b/spp_consent/readme/HISTORY.md index c9a9bc34c..425bb25aa 100644 --- a/spp_consent/readme/HISTORY.md +++ b/spp_consent/readme/HISTORY.md @@ -1,3 +1,7 @@ +### 19.0.2.0.2 + +- fix(security): the Tier-3 `group_registry_read` group can read the consent models the registrant form depends on. This module extends that form, and its models were granted only to the Tier-2 `group_registry_viewer` group, so a Tier-3-scoped role hit an AccessError opening a registrant. The two wizard models are deliberately not included: their entry points are restricted to the officer and manager tiers, so read access would grant nothing usable while exposing other users' in-progress wizard rows. + ### 19.0.2.0.1 - fix(views): apply `spp_registry.x2many_no_padding` widget to the Consents list on registrant forms, and hide the table entirely when there are no consents (showing a muted info line instead) — matches the empty-state treatment of read-only / no-create lists elsewhere (#943). diff --git a/spp_consent/security/ir.model.access.csv b/spp_consent/security/ir.model.access.csv index d9ad4ad11..8ea4fcaae 100644 --- a/spp_consent/security/ir.model.access.csv +++ b/spp_consent/security/ir.model.access.csv @@ -35,3 +35,10 @@ access_spp_bulk_record_consent_wizard_viewer,Bulk Record Consent Wizard Viewer A access_spp_bulk_record_consent_wizard_officer,Bulk Record Consent Wizard Officer Access,spp_consent.model_spp_bulk_record_consent_wizard,spp_registry.group_registry_officer,1,1,1,0 access_spp_bulk_record_consent_wizard_manager,Bulk Record Consent Wizard Manager Access,spp_consent.model_spp_bulk_record_consent_wizard,spp_registry.group_registry_manager,1,1,1,1 access_spp_bulk_record_consent_wizard_admin,Bulk Record Consent Wizard Admin Access,spp_consent.model_spp_bulk_record_consent_wizard,spp_security.group_spp_admin,1,1,1,1 +access_spp_consent_registry_read,Consent Registry Read Access,spp_consent.model_spp_consent,spp_registry.group_registry_read,1,0,0,0 +access_spp_consent_purpose_registry_read,Consent Purpose Registry Read Access,spp_consent.model_spp_consent_purpose,spp_registry.group_registry_read,1,0,0,0 +access_spp_consent_personal_data_registry_read,Consent Personal Data Registry Read Access,spp_consent.model_spp_consent_personal_data,spp_registry.group_registry_read,1,0,0,0 +access_spp_consent_processing_registry_read,Consent Processing Registry Read Access,spp_consent.model_spp_consent_processing,spp_registry.group_registry_read,1,0,0,0 +access_spp_consent_notice_registry_read,Consent Notice Registry Read Access,spp_consent.model_spp_consent_notice,spp_registry.group_registry_read,1,0,0,0 +access_spp_consent_history_registry_read,Consent History Registry Read Access,spp_consent.model_spp_consent_history,spp_registry.group_registry_read,1,0,0,0 +access_spp_consent_org_type_registry_read,Consent Org Type Registry Read Access,spp_consent.model_spp_consent_org_type,spp_registry.group_registry_read,1,0,0,0 diff --git a/spp_consent/static/description/index.html b/spp_consent/static/description/index.html index 556808ef9..6ee4b98fb 100644 --- a/spp_consent/static/description/index.html +++ b/spp_consent/static/description/index.html @@ -589,6 +589,19 @@

Changelog

+

19.0.2.0.2

+ +
+

19.0.2.0.1

-
+

19.0.2.0.0

+

19.0.2.1.1

+ +
+

19.0.2.1.0

-
+

19.0.2.0.0

+

19.0.2.3.4

+ +
+

19.0.2.3.3

-
+

19.0.2.3.2

-
+

19.0.2.3.1

  • fix(security): make the async operation lock a server-side boundary. @@ -702,7 +713,7 @@

    19.0.2.3.1

    acquire/release from the initiating user keeps working.
-
+

19.0.2.3.0

  • feat(spp_programs): Duplicate Detection is a card with an Add @@ -727,7 +738,7 @@

    19.0.2.3.0

    still blocked its own re-adding (#1171)
-
+

19.0.2.2.1

  • fix(spp_programs): stop Enroll Eligible undoing a deliberate pause. A @@ -737,7 +748,7 @@

    19.0.2.2.1

    Pausing is a decision that only Resume reverses (#1117)
-
+

19.0.2.1.3

  • fix(security): align Program Viewer / Validator / Cycle Approver roles @@ -756,7 +767,7 @@

    19.0.2.1.3

    cross-references — only the dedicated top-level menu disappears.
-
+

19.0.2.1.2

  • fix(security): add global ir.rule records on @@ -770,7 +781,7 @@

    19.0.2.1.2

    no-op for users with no center areas (global roles).
-
+

19.0.2.1.1

  • fix(views): apply spp_registry.x2many_no_padding widget to the @@ -779,7 +790,7 @@

    19.0.2.1.1

    19 inserts on inline list-in-form views (#943).
-
+

19.0.2.0.11

  • Fix TypeError: 'NoneType' object is not iterable when clicking @@ -790,7 +801,7 @@

    19.0.2.0.11

    omit the state filter instead of crashing on tuple(None)
-
+

19.0.2.0.10

  • Increase parallel-safe channel limits (cycle, eligibility_manager, @@ -803,7 +814,7 @@

    19.0.2.0.10

    submission on double-click
-
+

19.0.2.0.9

  • Add context flags (skip_registrant_statistics, @@ -816,7 +827,7 @@

    19.0.2.0.9

    _compute_has_members
-
+

19.0.2.0.8

  • Replace OFFSET pagination with NTILE-based ID-range batching in all @@ -827,7 +838,7 @@

    19.0.2.0.8

    program and cycle
-
+

19.0.2.0.7

  • Bulk membership creation using raw SQL INSERT ON CONFLICT DO NOTHING @@ -836,7 +847,7 @@

    19.0.2.0.7

    _add_beneficiaries with bulk SQL path
-
+

19.0.2.0.6

  • Remove unused entitlement_base_model.py (dead code, never imported)
  • @@ -845,34 +856,34 @@

    19.0.2.0.6

    payment, and fund tests (172 → 492 tests)
-
+

19.0.2.0.5

  • Batch create entitlements and payments instead of one-by-one ORM creates
-
+

19.0.2.0.4

  • Fetch fund balance once per approval batch instead of per entitlement
-
+

19.0.2.0.3

  • Replace cycle computed fields (total_amount, entitlements_count, approval flags) with SQL aggregation queries
-
+

19.0.2.0.2

  • Add composite indexes for frequent query patterns on entitlements and program memberships
-
+

19.0.2.0.1

  • Replace Python-level uniqueness checks with SQL UNIQUE constraints for @@ -881,7 +892,7 @@

    19.0.2.0.1

    constraint creation
-
+

19.0.2.0.0

  • Initial migration to OpenSPP2
  • diff --git a/spp_programs/tests/__init__.py b/spp_programs/tests/__init__.py index 86ea5bfae..9c0b90101 100644 --- a/spp_programs/tests/__init__.py +++ b/spp_programs/tests/__init__.py @@ -46,3 +46,4 @@ from . import test_approve_entitlements_program_isolation from . import test_payment_batch_payment_ids from . import test_program_viewer_registry_scope +from . import test_registry_read_access diff --git a/spp_programs/tests/test_registry_read_access.py b/spp_programs/tests/test_registry_read_access.py new file mode 100644 index 000000000..78a829b6b --- /dev/null +++ b/spp_programs/tests/test_registry_read_access.py @@ -0,0 +1,40 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Tier-3 ``group_registry_read`` must cover this module's registrant-form models. + +``spp_registry.group_registry_viewer`` (Tier-2) implies ``group_registry_read`` +(Tier-3), so any ACL granted only to the viewer tier disappears for a role +scoped to the read tier. ``spp_programs/views/registrant_view.xml`` renders ``cycle_id`` in the +entitlement lists on the registrant form, +so a read-tier role that opens a registrant hits an AccessError unless the +Tier-3 group carries these models too. +""" + +from odoo import Command +from odoo.tests import TransactionCase, tagged + +_MODELS = [ + "spp.cycle", + "spp.cycle.membership", +] + + +@tagged("post_install", "-at_install") +class TestRegistryReadAccess(TransactionCase): + def test_tier3_registry_read_covers_registrant_form_models(self): + user = self.env["res.users"].create( + { + "name": "Tier3 Reader", + "login": "tier3_reader_spp_programs", + "email": "tier3_reader_spp_programs@example.com", + "group_ids": [ + Command.link(self.env.ref("base.group_user").id), + Command.link(self.env.ref("spp_registry.group_registry_read").id), + ], + } + ) + Access = self.env["ir.model.access"].with_user(user) + missing = sorted(m for m in _MODELS if not Access.check(m, "read", raise_exception=False)) + self.assertFalse( + missing, + "Tier-3 group_registry_read cannot read registrant-form models: " + ", ".join(missing), + )