From f6f27d380a5a3971d50976bbd8539412f7396c4b Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Wed, 1 Jul 2026 14:09:59 +0800 Subject: [PATCH 1/3] security(hazard): restrict impact records to hazard/registry roles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spp_hazard's ACL granted base.group_user (every internal user) read on spp.hazard.impact, which links named registrants to incidents with damage level, dates, verification status, notes and verifier. The menu is gated to hazard groups, but the ACL left this sensitive data open to direct RPC/ search_read by any internal user. Dedicated hazard groups and registry_viewer already grant the intended read, so the base.group_user grant on impact was redundant and over-broad. Fix (surgical — impact only): - Remove the base.group_user read row for spp.hazard.impact. The four non-PII reference/operational models (category, incident, incident.area, impact.type) keep broad internal read, which sibling modules (spp_drims) rely on. - spp_hazard_programs: read spp.hazard.impact via sudo in the emergency-program eligibility computes, so a program user without a hazard group is not blocked (only aggregate counts / eligible registrants are surfaced, not impact rows). - Registrant form: gate the hazard-impact stat button, Emergency Response page, list columns and search filters to users with impact read (hazard groups / registry_viewer / admin), so non-hazard users reaching the form (e.g. via a CR's registrant link) don't render impact data. Tests: base.group_user is denied read on impact but retains the 4 non-sensitive models; hazard viewer and registry officer retain full impact access; a program user without a hazard group can still compute emergency eligibility. spp_hazard 86/86, spp_hazard_programs 25/25, spp_drims 250/250. --- spp_hazard/security/ir.model.access.csv | 1 - spp_hazard/tests/__init__.py | 2 + spp_hazard/tests/test_acl_group_user.py | 107 ++++++++++++++++++ spp_hazard/views/hazard_incident_views.xml | 7 +- spp_hazard/views/registrant_views.xml | 16 ++- spp_hazard_programs/models/program.py | 15 ++- spp_hazard_programs/tests/__init__.py | 2 + .../tests/test_program_user_access.py | 49 ++++++++ 8 files changed, 191 insertions(+), 8 deletions(-) create mode 100644 spp_hazard/tests/test_acl_group_user.py create mode 100644 spp_hazard_programs/tests/test_program_user_access.py diff --git a/spp_hazard/security/ir.model.access.csv b/spp_hazard/security/ir.model.access.csv index 36c1b5f6f..826732791 100644 --- a/spp_hazard/security/ir.model.access.csv +++ b/spp_hazard/security/ir.model.access.csv @@ -3,7 +3,6 @@ access_spp_hazard_category_user,spp.hazard.category user,model_spp_hazard_catego access_spp_hazard_incident_user,spp.hazard.incident user,model_spp_hazard_incident,base.group_user,1,0,0,0 access_spp_hazard_incident_area_user,spp.hazard.incident.area user,model_spp_hazard_incident_area,base.group_user,1,0,0,0 access_spp_hazard_impact_type_user,spp.hazard.impact.type user,model_spp_hazard_impact_type,base.group_user,1,0,0,0 -access_spp_hazard_impact_user,spp.hazard.impact user,model_spp_hazard_impact,base.group_user,1,0,0,0 access_spp_hazard_category_sysadmin,Hazard Category System Admin,model_spp_hazard_category,base.group_system,1,1,1,1 access_spp_hazard_incident_sysadmin,Hazard Incident System Admin,model_spp_hazard_incident,base.group_system,1,1,1,1 access_spp_hazard_incident_area_sysadmin,Hazard Incident Area System Admin,model_spp_hazard_incident_area,base.group_system,1,1,1,1 diff --git a/spp_hazard/tests/__init__.py b/spp_hazard/tests/__init__.py index b10eae59f..c25b529f7 100644 --- a/spp_hazard/tests/__init__.py +++ b/spp_hazard/tests/__init__.py @@ -7,3 +7,5 @@ from . import test_geofence from . import test_alert_ingestion from . import test_registrant + +from . import test_acl_group_user diff --git a/spp_hazard/tests/test_acl_group_user.py b/spp_hazard/tests/test_acl_group_user.py new file mode 100644 index 000000000..4f63b8cfe --- /dev/null +++ b/spp_hazard/tests/test_acl_group_user.py @@ -0,0 +1,107 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Security: hazard models must not be readable by every internal user. + +Regression test for "Broad internal read access exposes hazard impact records": +the ACL granted ``base.group_user`` read on the hazard models, so any internal +user could read hazard data (including registrant-linked impact records) via RPC, +even without a hazard role. Access must require a dedicated hazard group (or +``registry_viewer``/admin), not merely being an internal user. +""" + +from odoo import Command +from odoo.exceptions import AccessError +from odoo.tests import tagged + +from .common import HazardTestCase + +# The registrant-linked impact model is sensitive and must NOT be readable by +# every internal user. The other hazard models are non-PII reference/operational +# data that sibling modules (e.g. spp_drims) legitimately read broadly. +SENSITIVE_MODEL = "spp.hazard.impact" +NON_SENSITIVE_MODELS = [ + "spp.hazard.category", + "spp.hazard.incident", + "spp.hazard.incident.area", + "spp.hazard.impact.type", +] +ALL_HAZARD_MODELS = [SENSITIVE_MODEL, *NON_SENSITIVE_MODELS] + + +@tagged("post_install", "-at_install") +class TestHazardBaseUserNoAccess(HazardTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.plain_user = cls.env["res.users"].create( + { + "name": "Plain Internal User", + "login": "plain_internal_hazard_test", + "group_ids": [Command.link(cls.env.ref("base.group_user").id)], + } + ) + + def test_plain_internal_user_cannot_read_impact(self): + """base.group_user (any internal user) must NOT read the sensitive impact model.""" + with self.assertRaises(AccessError): + self.env[SENSITIVE_MODEL].with_user(self.plain_user).check_access("read") + + def test_plain_internal_user_can_read_non_sensitive_models(self): + """Non-PII hazard reference/operational models remain internally readable + (sibling modules such as spp_drims depend on reading incidents).""" + for model in NON_SENSITIVE_MODELS: + # Raises AccessError only if broad read was wrongly removed here. + self.env[model].with_user(self.plain_user).check_access("read") + + def test_hazard_viewer_retains_read(self): + """A hazard-group user must keep read access to all hazard models.""" + for model in ALL_HAZARD_MODELS: + self.env[model].with_user(self.hazard_viewer).check_access("read") + + def test_registry_user_can_still_read_registrant_hazard_fields(self): + """Regression: the registrant form's hazard indicator fields read + spp.hazard.impact in their compute. A registry user (Officer implies + Registry Viewer, which retains hazard read) must still be able to load + them after the ACL tightening — i.e. the fix must not break the form.""" + officer = self.env["res.users"].create( + { + "name": "Registry Officer (no hazard group)", + "login": "registry_officer_hazard_test", + "group_ids": [Command.link(self.env.ref("spp_registry.group_registry_officer").id)], + } + ) + # Sanity: this user is NOT in any hazard group. + self.assertFalse(officer.has_group("spp_hazard.group_hazard_read")) + + incident = self.env["spp.hazard.incident"].create( + { + "name": "Registry Officer Incident", + "code": "ROI-HAZ-001", + "category_id": self.category_typhoon.id, + "start_date": "2024-01-01", + } + ) + self.env["spp.hazard.impact"].create( + { + "incident_id": incident.id, + "registrant_id": self.registrant.id, + "impact_type_id": self.impact_type_displacement.id, + "damage_level": "moderate", + "impact_date": "2024-01-02", + } + ) + registrant_as_officer = self.registrant.with_user(officer) + # Force a live read through the impact O2M (not just the stored count), + # which must not raise AccessError for a registry user. + self.assertEqual(registrant_as_officer.hazard_impact_ids.mapped("damage_level"), ["moderate"]) + + def test_incident_form_hides_impacts_from_non_hazard_user(self): + """The incident form's Impacts O2M reads spp.hazard.impact; it must be + stripped from the arch for a user without impact read (e.g. a DRIMS-only + user), so opening an incident does not raise AccessError.""" + arch = self.env["spp.hazard.incident"].with_user(self.plain_user).get_view(view_type="form")["arch"] + self.assertNotIn("impact_ids", arch) + + def test_incident_form_shows_impacts_to_hazard_user(self): + """A hazard user still gets the Impacts O2M on the incident form.""" + arch = self.env["spp.hazard.incident"].with_user(self.hazard_viewer).get_view(view_type="form")["arch"] + self.assertIn("impact_ids", arch) diff --git a/spp_hazard/views/hazard_incident_views.xml b/spp_hazard/views/hazard_incident_views.xml index 6bfba2971..1fae4f15c 100644 --- a/spp_hazard/views/hazard_incident_views.xml +++ b/spp_hazard/views/hazard_incident_views.xml @@ -84,6 +84,7 @@ type="object" class="oe_stat_button" icon="fa-users" + groups="spp_hazard.group_hazard_read,spp_registry.group_registry_viewer,spp_security.group_spp_admin" > - +

50 - + @@ -121,16 +129,20 @@ 50 - + diff --git a/spp_hazard_programs/models/program.py b/spp_hazard_programs/models/program.py index 4d48edc0e..0778b030d 100644 --- a/spp_hazard_programs/models/program.py +++ b/spp_hazard_programs/models/program.py @@ -84,8 +84,12 @@ def _compute_affected_registrant_count(self): # Build domain for qualifying damage levels damage_domain = rec._get_damage_level_domain() - # Count unique registrants with qualifying impacts - impacts = self.env["spp.hazard.impact"].search( + # Count unique registrants with qualifying impacts. + # sudo: emergency-program eligibility must consider all qualifying + # impacts regardless of the viewing user's hazard access; impact rows + # are not exposed, only the aggregate count. + impact_sudo = self.env["spp.hazard.impact"].sudo() # nosemgrep: odoo-sudo-without-context + impacts = impact_sudo.search( [ ("incident_id", "in", rec.target_incident_ids.ids), ("verification_status", "=", "verified"), @@ -123,8 +127,11 @@ def get_emergency_eligible_registrants(self): damage_domain = self._get_damage_level_domain() - # Find qualifying impacts - impacts = self.env["spp.hazard.impact"].search( + # Find qualifying impacts. + # sudo: eligibility must consider all qualifying impacts regardless of the + # viewing user's hazard access; only the resulting registrants are returned. + impact_sudo = self.env["spp.hazard.impact"].sudo() # nosemgrep: odoo-sudo-without-context + impacts = impact_sudo.search( [ ("incident_id", "in", self.target_incident_ids.ids), ("verification_status", "=", "verified"), diff --git a/spp_hazard_programs/tests/__init__.py b/spp_hazard_programs/tests/__init__.py index 66fbca0e0..669012f1b 100644 --- a/spp_hazard_programs/tests/__init__.py +++ b/spp_hazard_programs/tests/__init__.py @@ -1,3 +1,5 @@ # Part of OpenSPP. See LICENSE file for full copyright and licensing details. from . import test_hazard_programs + +from . import test_program_user_access diff --git a/spp_hazard_programs/tests/test_program_user_access.py b/spp_hazard_programs/tests/test_program_user_access.py new file mode 100644 index 000000000..bdfe7bed1 --- /dev/null +++ b/spp_hazard_programs/tests/test_program_user_access.py @@ -0,0 +1,49 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Regression: emergency-eligibility logic must work for non-hazard program users. + +After tightening the hazard-impact ACL (removing the broad ``base.group_user`` +read grant), the program eligibility computes read ``spp.hazard.impact`` via +``sudo`` so a program user without any hazard group can still use them. Without +that sudo, ``affected_registrant_count`` / ``get_emergency_eligible_registrants`` +would raise ``AccessError`` for such users. +""" + +from odoo import Command +from odoo.tests import tagged + +from .common import HazardProgramsTestCase + + +@tagged("post_install", "-at_install") +class TestProgramUserHazardAccess(HazardProgramsTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.program_user = cls.env["res.users"].create( + { + "name": "Program Manager (no hazard group)", + "login": "program_mgr_no_hazard_test", + "group_ids": [ + Command.link(cls.env.ref("base.group_user").id), + Command.link(cls.env.ref("spp_programs.group_programs_manager").id), + ], + } + ) + cls.program.write( + { + "target_incident_ids": [Command.link(cls.incident_active.id)], + "qualifying_damage_levels": "any", + } + ) + + def test_program_user_without_hazard_group_can_compute_eligibility(self): + """A program user with no hazard group must still compute emergency + eligibility (the impact reads are sudo'd).""" + self.assertFalse(self.program_user.has_group("spp_hazard.group_hazard_read")) + program = self.program.with_user(self.program_user) + # Non-stored compute -> runs live as this user; reads impact via sudo. + self.assertEqual(program.affected_registrant_count, 2) + # Method -> runs live as this user; reads impact via sudo. + eligible = program.get_emergency_eligible_registrants() + self.assertIn(self.registrant_1, eligible) + self.assertIn(self.registrant_2, eligible) From 5e9de018775723b1035c2c0fe0bd8cfb734c3ff8 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Thu, 2 Jul 2026 22:23:59 +0800 Subject: [PATCH 2/3] chore(spp_hazard,spp_hazard_programs): bump versions + changelog Record the hazard impact ACL restriction (spp_hazard 19.0.2.0.3) and the sudo eligibility-compute fix (spp_hazard_programs 19.0.2.0.1) in versions and HISTORY changelogs (regenerated READMEs). --- spp_hazard/README.rst | 154 ++++++++++-------- spp_hazard/__manifest__.py | 2 +- spp_hazard/readme/HISTORY.md | 4 + spp_hazard/static/description/index.html | 51 +++--- spp_hazard_programs/README.rst | 10 ++ spp_hazard_programs/__manifest__.py | 2 +- spp_hazard_programs/readme/HISTORY.md | 4 + .../static/description/index.html | 11 ++ 8 files changed, 146 insertions(+), 92 deletions(-) diff --git a/spp_hazard/README.rst b/spp_hazard/README.rst index 4634a07c0..940460833 100644 --- a/spp_hazard/README.rst +++ b/spp_hazard/README.rst @@ -312,20 +312,20 @@ Log in as **Admin** or **Manager**. **3.6 Search and Grouping** -+-------+-------------------------------+------------------------------+ -| Step | Action | Expected Result | -+=======+===============================+==============================+ -| 3.6.1 | Type in search bar, select | Filters by name | -| | "Name" search | | -+-------+-------------------------------+------------------------------+ -| 3.6.2 | Type in search bar, select | Filters by code | -| | "Code" search | | -+-------+-------------------------------+------------------------------+ -| 3.6.3 | Use Filters > Active / | Filters correctly | -| | Inactive | | -+-------+-------------------------------+------------------------------+ -| 3.6.4 | Use Group By > Parent | Categories grouped by parent | -+-------+-------------------------------+------------------------------+ ++-------+------------------------------+------------------------------+ +| Step | Action | Expected Result | ++=======+==============================+==============================+ +| 3.6.1 | Type in search bar, select | Filters by name | +| | "Name" search | | ++-------+------------------------------+------------------------------+ +| 3.6.2 | Type in search bar, select | Filters by code | +| | "Code" search | | ++-------+------------------------------+------------------------------+ +| 3.6.3 | Use Filters > Active / | Filters correctly | +| | Inactive | | ++-------+------------------------------+------------------------------+ +| 3.6.4 | Use Group By > Parent | Categories grouped by parent | ++-------+------------------------------+------------------------------+ -------------- @@ -365,18 +365,18 @@ Log in as **Admin** or **Manager**. **4.2 Create an Impact Type** -+-------+----------------------------------+---------------------------+ -| Step | Action | Expected Result | -+=======+==================================+===========================+ -| 4.2.1 | Click **New** | Form opens | -+-------+----------------------------------+---------------------------+ -| 4.2.2 | Enter Name: | Fields accept input | -| | ``Water Contamination``, Code: | | -| | ``WATER_CONTAM``, Category: | | -| | Health | | -+-------+----------------------------------+---------------------------+ -| 4.2.3 | Save | Record saves successfully | -+-------+----------------------------------+---------------------------+ ++-------+------------------------------+------------------------------+ +| Step | Action | Expected Result | ++=======+==============================+==============================+ +| 4.2.1 | Click **New** | Form opens | ++-------+------------------------------+------------------------------+ +| 4.2.2 | Enter Name: | Fields accept input | +| | ``Water Contamination``, | | +| | Code: ``WATER_CONTAM``, | | +| | Category: Health | | ++-------+------------------------------+------------------------------+ +| 4.2.3 | Save | Record saves successfully | ++-------+------------------------------+------------------------------+ **4.3 Reorder via Drag** @@ -594,17 +594,17 @@ Use the incident created in 5.2 (starts as "Active"). **5.8 Stat Buttons** -+-------+------------------------------+-------------------------------+ -| Step | Action | Expected Result | -+=======+==============================+===============================+ -| 5.8.1 | Click "Affected" stat button | Opens Impact Records list | -| | | filtered to this incident | -+-------+------------------------------+-------------------------------+ -| 5.8.2 | Click browser back | Returns to incident form | -+-------+------------------------------+-------------------------------+ -| 5.8.3 | Click "Areas" stat button | Opens Area list filtered to | -| | | linked areas | -+-------+------------------------------+-------------------------------+ ++-------+------------------------------+------------------------------+ +| Step | Action | Expected Result | ++=======+==============================+==============================+ +| 5.8.1 | Click "Affected" stat button | Opens Impact Records list | +| | | filtered to this incident | ++-------+------------------------------+------------------------------+ +| 5.8.2 | Click browser back | Returns to incident form | ++-------+------------------------------+------------------------------+ +| 5.8.3 | Click "Areas" stat button | Opens Area list filtered to | +| | | linked areas | ++-------+------------------------------+------------------------------+ **5.9 Search and Filters (Incident List)** @@ -645,24 +645,25 @@ Use the incident created in 5.2 (starts as "Active"). **5.10 List View Decorations** -+--------+----------------------------+--------------------------------+ -| Step | Action | Expected Result | -+========+============================+================================+ -| 5.10.1 | Check row coloring | Alert rows: blue tint. | -| | | Recovery rows: yellow tint. | -| | | Closed rows: grey/muted. | -| | | Active rows: default (no | -| | | special coloring) | -+--------+----------------------------+--------------------------------+ -| 5.10.2 | Check Status column badges | Alert: blue badge. Active: | -| | | green badge. Recovery: yellow | -| | | badge. Closed: grey badge | -+--------+----------------------------+--------------------------------+ -| 5.10.3 | Check columns visible | Name, Code, Category, Start | -| | | Date, End Date (optional), | -| | | Status, Severity, Areas, | -| | | Affected | -+--------+----------------------------+--------------------------------+ ++--------+------------------------------+------------------------------+ +| Step | Action | Expected Result | ++========+==============================+==============================+ +| 5.10.1 | Check row coloring | Alert rows: blue tint. | +| | | Recovery rows: yellow tint. | +| | | Closed rows: grey/muted. | +| | | Active rows: default (no | +| | | special coloring) | ++--------+------------------------------+------------------------------+ +| 5.10.2 | Check Status column badges | Alert: blue badge. Active: | +| | | green badge. Recovery: | +| | | yellow badge. Closed: grey | +| | | badge | ++--------+------------------------------+------------------------------+ +| 5.10.3 | Check columns visible | Name, Code, Category, Start | +| | | Date, End Date (optional), | +| | | Status, Severity, Areas, | +| | | Affected | ++--------+------------------------------+------------------------------+ -------------- @@ -832,22 +833,22 @@ Using the impact created in 6.1 (starts as "Reported"): **6.7 Impact List Decorations** -+-------+----------------------------+---------------------------------+ -| Step | Action | Expected Result | -+=======+============================+=================================+ -| 6.7.1 | Check row coloring | Reported: blue. Verified: | -| | | green. Disputed: yellow. | -| | | Closed: grey/muted | -+-------+----------------------------+---------------------------------+ -| 6.7.2 | Verification Status badges | Same color coding as rows | -+-------+----------------------------+---------------------------------+ -| 6.7.3 | Damage Level column | Shows as badge widget (neutral | -| | | color) | -+-------+----------------------------+---------------------------------+ -| 6.7.4 | Optional columns | "Verified By" and "Verified | -| | | Date" available under column | -| | | options (hidden by default) | -+-------+----------------------------+---------------------------------+ ++-------+------------------------------+------------------------------+ +| Step | Action | Expected Result | ++=======+==============================+==============================+ +| 6.7.1 | Check row coloring | Reported: blue. Verified: | +| | | green. Disputed: yellow. | +| | | Closed: grey/muted | ++-------+------------------------------+------------------------------+ +| 6.7.2 | Verification Status badges | Same color coding as rows | ++-------+------------------------------+------------------------------+ +| 6.7.3 | Damage Level column | Shows as badge widget | +| | | (neutral color) | ++-------+------------------------------+------------------------------+ +| 6.7.4 | Optional columns | "Verified By" and "Verified | +| | | Date" available under column | +| | | options (hidden by default) | ++-------+------------------------------+------------------------------+ -------------- @@ -1186,6 +1187,17 @@ encounter unexpected behavior, please report it as a new issue. Changelog ========= +19.0.2.0.3 +~~~~~~~~~~ + +- fix(security): remove the ``base.group_user`` read grant on + ``spp.hazard.impact`` so registrant-linked impact records (name, + damage level, verification, notes) are readable only by hazard roles, + ``registry_viewer``, and admins — not every internal user via RPC. + Gate the impact UI on the registrant and incident forms (stat buttons, + Emergency Response / Impacts pages, list columns, search filters) to + users with impact read. + 19.0.2.0.2 ~~~~~~~~~~ diff --git a/spp_hazard/__manifest__.py b/spp_hazard/__manifest__.py index 259353313..1c08ebd3f 100644 --- a/spp_hazard/__manifest__.py +++ b/spp_hazard/__manifest__.py @@ -8,7 +8,7 @@ "for emergency response. Links registrants to disaster events with geographic scope " "and severity tracking to enable targeted humanitarian assistance.", "category": "OpenSPP/Targeting", - "version": "19.0.2.0.2", + "version": "19.0.2.0.3", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", diff --git a/spp_hazard/readme/HISTORY.md b/spp_hazard/readme/HISTORY.md index c02593c5b..24041ef76 100644 --- a/spp_hazard/readme/HISTORY.md +++ b/spp_hazard/readme/HISTORY.md @@ -1,3 +1,7 @@ +### 19.0.2.0.3 + +- fix(security): remove the `base.group_user` read grant on `spp.hazard.impact` so registrant-linked impact records (name, damage level, verification, notes) are readable only by hazard roles, `registry_viewer`, and admins — not every internal user via RPC. Gate the impact UI on the registrant and incident forms (stat buttons, Emergency Response / Impacts pages, list columns, search filters) to users with impact read. + ### 19.0.2.0.2 - fix(security): grant `group_hazard_viewer` to spp_user_roles roles (Registry Viewer, Program Manager, Global/Local Registrar) that the OP#951 menu audit identifies as needing read-only Hazard menu access. Other affected roles defined outside this module (program/CR/farm roles) are wired in their own modules. diff --git a/spp_hazard/static/description/index.html b/spp_hazard/static/description/index.html index 9df1ed8e5..9ce1c14e5 100644 --- a/spp_hazard/static/description/index.html +++ b/spp_hazard/static/description/index.html @@ -816,8 +816,8 @@

Usage

--++ @@ -910,8 +910,8 @@

Usage

Step
--++ @@ -926,9 +926,9 @@

Usage

+Water Contamination, +Code: WATER_CONTAM, +Category: Health @@ -1346,8 +1346,8 @@

Usage

Step
4.2.2 Enter Name: -Water Contamination, Code: -WATER_CONTAM, Category: -Health Fields accept input
4.2.3
--++ @@ -1447,8 +1447,8 @@

Usage

Step
--++ @@ -1468,8 +1468,9 @@

Usage

+green badge. Recovery: +yellow badge. Closed: grey +badge @@ -1787,8 +1788,8 @@

Usage

Step
5.10.2 Check Status column badges Alert: blue badge. Active: -green badge. Recovery: yellow -badge. Closed: grey badge
5.10.3 Check columns visible
--++ @@ -1809,8 +1810,8 @@

Usage

- + @@ -2454,6 +2455,18 @@

Changelog

+

19.0.2.0.3

+
    +
  • fix(security): remove the base.group_user read grant on +spp.hazard.impact so registrant-linked impact records (name, +damage level, verification, notes) are readable only by hazard roles, +registry_viewer, and admins — not every internal user via RPC. +Gate the impact UI on the registrant and incident forms (stat buttons, +Emergency Response / Impacts pages, list columns, search filters) to +users with impact read.
  • +
+
+

19.0.2.0.2

  • fix(security): grant group_hazard_viewer to spp_user_roles roles @@ -2469,7 +2482,7 @@

    19.0.2.0.2

    Support).
-
+

19.0.2.0.1

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

    19.0.2.0.1

    (showing a muted info line instead) (#943).
-
+

19.0.2.0.0

  • Initial migration to OpenSPP2
  • diff --git a/spp_hazard_programs/README.rst b/spp_hazard_programs/README.rst index 0b6fdee90..2d9b09aac 100644 --- a/spp_hazard_programs/README.rst +++ b/spp_hazard_programs/README.rst @@ -324,6 +324,16 @@ Test Scenario 9: Incident List View Column Changelog ========= +19.0.2.0.1 +~~~~~~~~~~ + +- fix(security): read ``spp.hazard.impact`` via ``sudo`` in the + emergency-eligibility computes (``affected_registrant_count``, + ``get_emergency_eligible_registrants``), so they keep working for + non-hazard program users after impact read access was restricted to + hazard/registry roles. Only aggregate counts / eligible registrants + are surfaced, not impact rows. + 19.0.2.0.0 ~~~~~~~~~~ diff --git a/spp_hazard_programs/__manifest__.py b/spp_hazard_programs/__manifest__.py index 1871df01a..dfb115f96 100644 --- a/spp_hazard_programs/__manifest__.py +++ b/spp_hazard_programs/__manifest__.py @@ -7,7 +7,7 @@ "summary": "Links hazard impacts to program eligibility and entitlements. " "Enables emergency programs to use hazard data for targeting and benefit calculation.", "category": "OpenSPP/Targeting", - "version": "19.0.2.0.0", + "version": "19.0.2.0.1", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", diff --git a/spp_hazard_programs/readme/HISTORY.md b/spp_hazard_programs/readme/HISTORY.md index 4aaf9afef..329ff0b7d 100644 --- a/spp_hazard_programs/readme/HISTORY.md +++ b/spp_hazard_programs/readme/HISTORY.md @@ -1,3 +1,7 @@ +### 19.0.2.0.1 + +- fix(security): read `spp.hazard.impact` via `sudo` in the emergency-eligibility computes (`affected_registrant_count`, `get_emergency_eligible_registrants`), so they keep working for non-hazard program users after impact read access was restricted to hazard/registry roles. Only aggregate counts / eligible registrants are surfaced, not impact rows. + ### 19.0.2.0.0 - Initial migration to OpenSPP2 diff --git a/spp_hazard_programs/static/description/index.html b/spp_hazard_programs/static/description/index.html index 8a1fc0b68..68329ad5b 100644 --- a/spp_hazard_programs/static/description/index.html +++ b/spp_hazard_programs/static/description/index.html @@ -698,6 +698,17 @@

    Changelog

+

19.0.2.0.1

+
    +
  • fix(security): read spp.hazard.impact via sudo in the +emergency-eligibility computes (affected_registrant_count, +get_emergency_eligible_registrants), so they keep working for +non-hazard program users after impact read access was restricted to +hazard/registry roles. Only aggregate counts / eligible registrants +are surfaced, not impact rows.
  • +
+
+

19.0.2.0.0

  • Initial migration to OpenSPP2
  • From 47c271687223387f0695bf67e68f9c5e28140ee4 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 3 Jul 2026 00:33:51 +0800 Subject: [PATCH 3/3] docs(spp_hazard): normalize README table widths to match CI generator The earlier README regeneration was run in a local env whose RST renderer produced wider table columns than CI's pinned oca-gen environment, causing the 'Generate addons README files from fragments' pre-commit hook to fail on CI. Restore the column widths CI's generator produces. --- spp_hazard/README.rst | 143 +++++++++++------------ spp_hazard/static/description/index.html | 35 +++--- 2 files changed, 88 insertions(+), 90 deletions(-) diff --git a/spp_hazard/README.rst b/spp_hazard/README.rst index 940460833..ff3b65dcc 100644 --- a/spp_hazard/README.rst +++ b/spp_hazard/README.rst @@ -312,20 +312,20 @@ Log in as **Admin** or **Manager**. **3.6 Search and Grouping** -+-------+------------------------------+------------------------------+ -| Step | Action | Expected Result | -+=======+==============================+==============================+ -| 3.6.1 | Type in search bar, select | Filters by name | -| | "Name" search | | -+-------+------------------------------+------------------------------+ -| 3.6.2 | Type in search bar, select | Filters by code | -| | "Code" search | | -+-------+------------------------------+------------------------------+ -| 3.6.3 | Use Filters > Active / | Filters correctly | -| | Inactive | | -+-------+------------------------------+------------------------------+ -| 3.6.4 | Use Group By > Parent | Categories grouped by parent | -+-------+------------------------------+------------------------------+ ++-------+-------------------------------+------------------------------+ +| Step | Action | Expected Result | ++=======+===============================+==============================+ +| 3.6.1 | Type in search bar, select | Filters by name | +| | "Name" search | | ++-------+-------------------------------+------------------------------+ +| 3.6.2 | Type in search bar, select | Filters by code | +| | "Code" search | | ++-------+-------------------------------+------------------------------+ +| 3.6.3 | Use Filters > Active / | Filters correctly | +| | Inactive | | ++-------+-------------------------------+------------------------------+ +| 3.6.4 | Use Group By > Parent | Categories grouped by parent | ++-------+-------------------------------+------------------------------+ -------------- @@ -365,18 +365,18 @@ Log in as **Admin** or **Manager**. **4.2 Create an Impact Type** -+-------+------------------------------+------------------------------+ -| Step | Action | Expected Result | -+=======+==============================+==============================+ -| 4.2.1 | Click **New** | Form opens | -+-------+------------------------------+------------------------------+ -| 4.2.2 | Enter Name: | Fields accept input | -| | ``Water Contamination``, | | -| | Code: ``WATER_CONTAM``, | | -| | Category: Health | | -+-------+------------------------------+------------------------------+ -| 4.2.3 | Save | Record saves successfully | -+-------+------------------------------+------------------------------+ ++-------+----------------------------------+---------------------------+ +| Step | Action | Expected Result | ++=======+==================================+===========================+ +| 4.2.1 | Click **New** | Form opens | ++-------+----------------------------------+---------------------------+ +| 4.2.2 | Enter Name: | Fields accept input | +| | ``Water Contamination``, Code: | | +| | ``WATER_CONTAM``, Category: | | +| | Health | | ++-------+----------------------------------+---------------------------+ +| 4.2.3 | Save | Record saves successfully | ++-------+----------------------------------+---------------------------+ **4.3 Reorder via Drag** @@ -594,17 +594,17 @@ Use the incident created in 5.2 (starts as "Active"). **5.8 Stat Buttons** -+-------+------------------------------+------------------------------+ -| Step | Action | Expected Result | -+=======+==============================+==============================+ -| 5.8.1 | Click "Affected" stat button | Opens Impact Records list | -| | | filtered to this incident | -+-------+------------------------------+------------------------------+ -| 5.8.2 | Click browser back | Returns to incident form | -+-------+------------------------------+------------------------------+ -| 5.8.3 | Click "Areas" stat button | Opens Area list filtered to | -| | | linked areas | -+-------+------------------------------+------------------------------+ ++-------+------------------------------+-------------------------------+ +| Step | Action | Expected Result | ++=======+==============================+===============================+ +| 5.8.1 | Click "Affected" stat button | Opens Impact Records list | +| | | filtered to this incident | ++-------+------------------------------+-------------------------------+ +| 5.8.2 | Click browser back | Returns to incident form | ++-------+------------------------------+-------------------------------+ +| 5.8.3 | Click "Areas" stat button | Opens Area list filtered to | +| | | linked areas | ++-------+------------------------------+-------------------------------+ **5.9 Search and Filters (Incident List)** @@ -645,25 +645,24 @@ Use the incident created in 5.2 (starts as "Active"). **5.10 List View Decorations** -+--------+------------------------------+------------------------------+ -| Step | Action | Expected Result | -+========+==============================+==============================+ -| 5.10.1 | Check row coloring | Alert rows: blue tint. | -| | | Recovery rows: yellow tint. | -| | | Closed rows: grey/muted. | -| | | Active rows: default (no | -| | | special coloring) | -+--------+------------------------------+------------------------------+ -| 5.10.2 | Check Status column badges | Alert: blue badge. Active: | -| | | green badge. Recovery: | -| | | yellow badge. Closed: grey | -| | | badge | -+--------+------------------------------+------------------------------+ -| 5.10.3 | Check columns visible | Name, Code, Category, Start | -| | | Date, End Date (optional), | -| | | Status, Severity, Areas, | -| | | Affected | -+--------+------------------------------+------------------------------+ ++--------+----------------------------+--------------------------------+ +| Step | Action | Expected Result | ++========+============================+================================+ +| 5.10.1 | Check row coloring | Alert rows: blue tint. | +| | | Recovery rows: yellow tint. | +| | | Closed rows: grey/muted. | +| | | Active rows: default (no | +| | | special coloring) | ++--------+----------------------------+--------------------------------+ +| 5.10.2 | Check Status column badges | Alert: blue badge. Active: | +| | | green badge. Recovery: yellow | +| | | badge. Closed: grey badge | ++--------+----------------------------+--------------------------------+ +| 5.10.3 | Check columns visible | Name, Code, Category, Start | +| | | Date, End Date (optional), | +| | | Status, Severity, Areas, | +| | | Affected | ++--------+----------------------------+--------------------------------+ -------------- @@ -833,22 +832,22 @@ Using the impact created in 6.1 (starts as "Reported"): **6.7 Impact List Decorations** -+-------+------------------------------+------------------------------+ -| Step | Action | Expected Result | -+=======+==============================+==============================+ -| 6.7.1 | Check row coloring | Reported: blue. Verified: | -| | | green. Disputed: yellow. | -| | | Closed: grey/muted | -+-------+------------------------------+------------------------------+ -| 6.7.2 | Verification Status badges | Same color coding as rows | -+-------+------------------------------+------------------------------+ -| 6.7.3 | Damage Level column | Shows as badge widget | -| | | (neutral color) | -+-------+------------------------------+------------------------------+ -| 6.7.4 | Optional columns | "Verified By" and "Verified | -| | | Date" available under column | -| | | options (hidden by default) | -+-------+------------------------------+------------------------------+ ++-------+----------------------------+---------------------------------+ +| Step | Action | Expected Result | ++=======+============================+=================================+ +| 6.7.1 | Check row coloring | Reported: blue. Verified: | +| | | green. Disputed: yellow. | +| | | Closed: grey/muted | ++-------+----------------------------+---------------------------------+ +| 6.7.2 | Verification Status badges | Same color coding as rows | ++-------+----------------------------+---------------------------------+ +| 6.7.3 | Damage Level column | Shows as badge widget (neutral | +| | | color) | ++-------+----------------------------+---------------------------------+ +| 6.7.4 | Optional columns | "Verified By" and "Verified | +| | | Date" available under column | +| | | options (hidden by default) | ++-------+----------------------------+---------------------------------+ -------------- diff --git a/spp_hazard/static/description/index.html b/spp_hazard/static/description/index.html index 9ce1c14e5..3110f6ed5 100644 --- a/spp_hazard/static/description/index.html +++ b/spp_hazard/static/description/index.html @@ -816,8 +816,8 @@

    Usage

Step
6.7.3 Damage Level columnShows as badge widget (neutral -color)Shows as badge widget +(neutral color)
6.7.4 Optional columns
--++ @@ -910,8 +910,8 @@

Usage

Step
--++ @@ -926,9 +926,9 @@

Usage

+Water Contamination, Code: +WATER_CONTAM, Category: +Health @@ -1346,8 +1346,8 @@

Usage

Step
4.2.2 Enter Name: -Water Contamination, -Code: WATER_CONTAM, -Category: Health Fields accept input
4.2.3
--++ @@ -1447,8 +1447,8 @@

Usage

Step
--++ @@ -1468,9 +1468,8 @@

Usage

+green badge. Recovery: yellow +badge. Closed: grey badge @@ -1788,8 +1787,8 @@

Usage

Step
5.10.2 Check Status column badges Alert: blue badge. Active: -green badge. Recovery: -yellow badge. Closed: grey -badge
5.10.3 Check columns visible
--++ @@ -1810,8 +1809,8 @@

Usage

- +
Step
6.7.3 Damage Level columnShows as badge widget -(neutral color)Shows as badge widget (neutral +color)
6.7.4 Optional columns