From 338763e82794306e07a8216c8eb5cfc8e6281d6c Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 11:06:27 +0800 Subject: [PATCH 1/6] test(demo): pin default-credential demo users deactivated on non-demo install Add safety tests for spp_demo and spp_farmer_registry_demo: the default- credential users (shared password 'demo', incl. sppadmin) must be archived when demo data is disabled (production-style install) and left active when demo data is enabled, plus a guard that neither module is marked Production/Stable. --- spp_demo/tests/__init__.py | 1 + spp_demo/tests/test_demo_user_safety.py | 63 +++++++++++++++++++ spp_farmer_registry_demo/tests/__init__.py | 1 + .../tests/test_demo_user_safety.py | 55 ++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 spp_demo/tests/test_demo_user_safety.py create mode 100644 spp_farmer_registry_demo/tests/test_demo_user_safety.py diff --git a/spp_demo/tests/__init__.py b/spp_demo/tests/__init__.py index 52c240bb5..999964c7e 100644 --- a/spp_demo/tests/__init__.py +++ b/spp_demo/tests/__init__.py @@ -6,3 +6,4 @@ from . import test_apps_wizard from . import test_demo_stories from . import test_demo_area_loader +from . import test_demo_user_safety diff --git a/spp_demo/tests/test_demo_user_safety.py b/spp_demo/tests/test_demo_user_safety.py new file mode 100644 index 000000000..6eddb0700 --- /dev/null +++ b/spp_demo/tests/test_demo_user_safety.py @@ -0,0 +1,63 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Safety tests for the default-credential demo users. + +``data/users_data.xml`` creates active users with the shared, documented +password ``demo`` (including ``sppadmin`` with SPP admin rights) via the +module's ``data`` section, so the accounts exist after any install. On a +production database (installed without demo data) they must be deactivated so +the well-known credentials cannot be used to log in; on a demo/evaluation +database they must stay active so demos and generators work. +""" + +from odoo.modules.module import get_manifest +from odoo.tests import TransactionCase, tagged + +from odoo.addons.spp_demo import DEFAULT_DEMO_USER_XMLIDS, deactivate_default_demo_users + + +@tagged("post_install", "-at_install") +class TestDemoUserSafety(TransactionCase): + def _default_users(self): + users = self.env["res.users"].browse() + for xmlid in DEFAULT_DEMO_USER_XMLIDS: + user = self.env.ref(xmlid, raise_if_not_found=False) + if user: + users |= user + return users + + def test_default_users_deactivated_when_demo_disabled(self): + """On a non-demo (production-style) install the default-credential + users are archived so their known password cannot be used.""" + users = self._default_users() + self.assertTrue(users, "demo users should exist in the test database") + users.active = True # precondition + + deactivated = deactivate_default_demo_users(self.env, DEFAULT_DEMO_USER_XMLIDS, demo_enabled=False) + + self.assertEqual(set(deactivated.ids), set(users.ids)) + self.assertFalse( + any(users.mapped("active")), + "all default-credential demo users must be inactive when demo is disabled", + ) + + def test_default_users_active_when_demo_enabled(self): + """On a demo/evaluation database the users stay active so the demos + and generators keep working.""" + users = self._default_users() + users.active = True + + deactivated = deactivate_default_demo_users(self.env, DEFAULT_DEMO_USER_XMLIDS, demo_enabled=True) + + self.assertFalse(deactivated) + self.assertTrue( + all(users.mapped("active")), + "demo users must remain active when demo data is enabled", + ) + + def test_spp_demo_not_marked_production_stable(self): + """A demo module that ships default-credential users must not signal + production-readiness via development_status.""" + self.assertNotEqual( + get_manifest("spp_demo").get("development_status"), + "Production/Stable", + ) diff --git a/spp_farmer_registry_demo/tests/__init__.py b/spp_farmer_registry_demo/tests/__init__.py index 4e93085ce..2a53d5754 100644 --- a/spp_farmer_registry_demo/tests/__init__.py +++ b/spp_farmer_registry_demo/tests/__init__.py @@ -3,3 +3,4 @@ from . import test_demo_generator from . import test_seeded_farm_generator from . import test_story_change_requests +from . import test_demo_user_safety diff --git a/spp_farmer_registry_demo/tests/test_demo_user_safety.py b/spp_farmer_registry_demo/tests/test_demo_user_safety.py new file mode 100644 index 000000000..f6087341f --- /dev/null +++ b/spp_farmer_registry_demo/tests/test_demo_user_safety.py @@ -0,0 +1,55 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Safety tests for this module's default-credential demo users. + +``data/demo_users.xml`` creates farmer-registry-specific users with the shared +password ``demo`` (in addition to re-roling the spp_demo users). On a +production database (installed without demo data) they must be deactivated; on +a demo/evaluation database they must stay active. +""" + +from odoo.modules.module import get_manifest +from odoo.tests import TransactionCase, tagged + +from odoo.addons.spp_demo import deactivate_default_demo_users +from odoo.addons.spp_farmer_registry_demo import DEFAULT_DEMO_USER_XMLIDS + + +@tagged("post_install", "-at_install") +class TestFarmerDemoUserSafety(TransactionCase): + def _default_users(self): + users = self.env["res.users"].browse() + for xmlid in DEFAULT_DEMO_USER_XMLIDS: + user = self.env.ref(xmlid, raise_if_not_found=False) + if user: + users |= user + return users + + def test_default_users_deactivated_when_demo_disabled(self): + users = self._default_users() + self.assertTrue(users, "farmer demo users should exist in the test database") + users.active = True + + deactivated = deactivate_default_demo_users(self.env, DEFAULT_DEMO_USER_XMLIDS, demo_enabled=False) + + self.assertEqual(set(deactivated.ids), set(users.ids)) + self.assertFalse( + any(users.mapped("active")), + "all farmer default-credential demo users must be inactive when demo is disabled", + ) + + def test_default_users_active_when_demo_enabled(self): + users = self._default_users() + users.active = True + + deactivate_default_demo_users(self.env, DEFAULT_DEMO_USER_XMLIDS, demo_enabled=True) + + self.assertTrue( + all(users.mapped("active")), + "farmer demo users must remain active when demo data is enabled", + ) + + def test_farmer_demo_not_marked_production_stable(self): + self.assertNotEqual( + get_manifest("spp_farmer_registry_demo").get("development_status"), + "Production/Stable", + ) From 9be0b9c5809612ba4e5ed90b1739e85872b7be1e Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 11:06:27 +0800 Subject: [PATCH 2/6] security(demo): deactivate default-credential demo users on production install The demo bundles ship active res.users with the well-known password 'demo' (including sppadmin with SPP admin rights) in their data section, so the accounts are created on every install. Marked Production/Stable, the modules signalled production-readiness, so an operator could install a demo bundle on an internet-facing instance and expose those default credentials to login. Add a post_init_hook to spp_demo (shared helper) and spp_farmer_registry_demo that archives the default-credential users when the database has no demo data (a production-style install); on demo/evaluation databases the users stay active so demos and generators are unaffected. Also lower development_status from Production/Stable to Alpha and add a DEMO-ONLY warning to each summary. --- spp_demo/__init__.py | 54 ++++++++++++++++++++++++ spp_demo/__manifest__.py | 9 ++-- spp_farmer_registry_demo/__init__.py | 17 ++++++++ spp_farmer_registry_demo/__manifest__.py | 9 ++-- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/spp_demo/__init__.py b/spp_demo/__init__.py index f5f1faaad..e1ae8902b 100644 --- a/spp_demo/__init__.py +++ b/spp_demo/__init__.py @@ -1,5 +1,59 @@ # Part of OpenSPP. See LICENSE file for full copyright and licensing details. +import logging from . import locale_providers from . import models from . import wizard + +_logger = logging.getLogger(__name__) + +# res.users records created with the shared, documented password "demo" by +# data/users_data.xml (including "sppadmin" with SPP admin rights). They load +# via the `data` section, so they exist after any install — including a +# production database installed without demo data, where the well-known +# credentials would be a login vector. +DEFAULT_DEMO_USER_XMLIDS = [ + "spp_demo.demo_viewer", + "spp_demo.demo_officer", + "spp_demo.demo_supervisor", + "spp_demo.demo_manager", + "spp_demo.demo_admin", +] + + +def demo_data_enabled(env, module_name): + """Return True if demo data was loaded for ``module_name`` on this database.""" + module = env["ir.module.module"].search([("name", "=", module_name)], limit=1) + return bool(module.demo) + + +def deactivate_default_demo_users(env, xmlids, demo_enabled): + """Deactivate default-credential demo users unless demo data is enabled. + + On a database with demo data (an evaluation/demo instance) the accounts are + left active so demos and generators work. On a database WITHOUT demo data (a + production-style install) they are archived so the well-known ``demo`` + password cannot be used to log in. Returns the users that were deactivated. + """ + if demo_enabled: + return env["res.users"].browse() + users = env["res.users"].browse() + for xmlid in xmlids: + user = env.ref(xmlid, raise_if_not_found=False) + if user and user.active: + users |= user + if users: + users.active = False + _logger.warning( + "Demo data is disabled; archived %d default-credential demo " + "user(s): %s. Re-activate them deliberately only on a " + "non-production instance.", + len(users), + ", ".join(users.mapped("login")), + ) + return users + + +def post_init_hook(env): + """Neutralize the default-credential demo users on a production install.""" + deactivate_default_demo_users(env, DEFAULT_DEMO_USER_XMLIDS, demo_data_enabled(env, "spp_demo")) diff --git a/spp_demo/__manifest__.py b/spp_demo/__manifest__.py index 3b85245a4..23064b2a7 100644 --- a/spp_demo/__manifest__.py +++ b/spp_demo/__manifest__.py @@ -3,14 +3,16 @@ { "name": "OpenSPP Demo", "category": "OpenSPP", - "version": "19.0.2.1.0", + "version": "19.0.2.1.1", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", "license": "LGPL-3", - "development_status": "Production/Stable", + "development_status": "Alpha", "maintainers": ["jeremi", "gonzalesedwin1123", "reichie020212", "emjay0921"], - "summary": "Core demo module with data generator and sample data for OpenSPP", + "summary": "Core demo module with data generator and sample data for OpenSPP. " + "DEMO ONLY: creates users with the well-known password 'demo' (including an " + "'sppadmin' SPP admin). Never install on a production or internet-facing instance.", "depends": [ "base", "spp_base_common", @@ -43,4 +45,5 @@ "application": False, "installable": True, "auto_install": False, + "post_init_hook": "post_init_hook", } diff --git a/spp_farmer_registry_demo/__init__.py b/spp_farmer_registry_demo/__init__.py index d33610325..9bda40a18 100644 --- a/spp_farmer_registry_demo/__init__.py +++ b/spp_farmer_registry_demo/__init__.py @@ -1,2 +1,19 @@ # Part of OpenSPP. See LICENSE file for full copyright and licensing details. from . import models + +# Farmer-registry-specific demo users created with the well-known password +# "demo" by data/demo_users.xml (in addition to the spp_demo users this module +# re-roles, which spp_demo's own post_init_hook already neutralizes). +DEFAULT_DEMO_USER_XMLIDS = [ + "spp_farmer_registry_demo.demo_user_cr_local_validator", + "spp_farmer_registry_demo.demo_user_cr_hq_validator", + "spp_farmer_registry_demo.demo_user_program_manager", + "spp_farmer_registry_demo.demo_user_cycle_approver", +] + + +def post_init_hook(env): + """Neutralize this module's default-credential demo users in production.""" + from odoo.addons.spp_demo import deactivate_default_demo_users, demo_data_enabled + + deactivate_default_demo_users(env, DEFAULT_DEMO_USER_XMLIDS, demo_data_enabled(env, "spp_farmer_registry_demo")) diff --git a/spp_farmer_registry_demo/__manifest__.py b/spp_farmer_registry_demo/__manifest__.py index 3a0fbab2b..78c90fad4 100644 --- a/spp_farmer_registry_demo/__manifest__.py +++ b/spp_farmer_registry_demo/__manifest__.py @@ -1,14 +1,16 @@ # Part of OpenSPP. See LICENSE file for full copyright and licensing details. { "name": "OpenSPP Farmer Registry Demo", - "summary": "Demo generator for Farmer Registry with fixed stories and volume generation", + "summary": "Demo generator for Farmer Registry with fixed stories and volume generation. " + "DEMO ONLY: creates users with the well-known password 'demo'. Never install on a " + "production or internet-facing instance.", "category": "OpenSPP", - "version": "19.0.2.1.1", + "version": "19.0.2.1.3", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", "license": "LGPL-3", - "development_status": "Production/Stable", + "development_status": "Alpha", "maintainers": ["jeremi", "gonzalesedwin1123", "emjay0921"], "depends": [ # Farmer Registry Starter Bundle @@ -55,4 +57,5 @@ "application": False, "installable": True, "auto_install": False, + "post_init_hook": "post_init_hook", } From 3f145ab88f1ff9eed998278616be6308c18b2625 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 11:06:27 +0800 Subject: [PATCH 3/6] docs(demo): version bump + HISTORY for default-credential deactivation spp_demo 19.0.2.1.0 -> 19.0.2.1.1; spp_farmer_registry_demo 19.0.2.1.1 -> 19.0.2.1.3 (skips .1.2 taken by the in-flight force-unlock PR #336 on the same module). README.rst / index.html regenerated from CI's pinned generator. --- spp_demo/readme/HISTORY.md | 10 ++++++++++ spp_farmer_registry_demo/readme/HISTORY.md | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/spp_demo/readme/HISTORY.md b/spp_demo/readme/HISTORY.md index 8c361d1fc..728a8695d 100644 --- a/spp_demo/readme/HISTORY.md +++ b/spp_demo/readme/HISTORY.md @@ -1,3 +1,13 @@ +### 19.0.2.1.1 + +- fix(security): deactivate the default-credential demo users (including the + ``sppadmin`` SPP admin, all created with the well-known password ``demo``) + when the module is installed on a database without demo data, so the known + credentials cannot be used to log in on a production instance. The accounts + stay active on demo/evaluation databases. Also lowers ``development_status`` + from ``Production/Stable`` to ``Alpha`` so the demo module no longer signals + production-readiness. + ### 19.0.2.1.0 - feat(demo): re-land curated PHL geodata and demo generator updates from #76: refreshed `data/shapes/phl_curated.geojson` and `data/countries/phl/areas.xml` (prepared via `scripts/prepare_phl_geodata.py`), with matching demo data generator and area loader test updates. Adds the companion `spp_demo_phl_luzon` module providing Luzon-scale demo areas and population weights. diff --git a/spp_farmer_registry_demo/readme/HISTORY.md b/spp_farmer_registry_demo/readme/HISTORY.md index f397e16d8..a48cdabb4 100644 --- a/spp_farmer_registry_demo/readme/HISTORY.md +++ b/spp_farmer_registry_demo/readme/HISTORY.md @@ -1,3 +1,12 @@ +### 19.0.2.1.3 + +- fix(security): deactivate this module's default-credential demo users + (created with the well-known password ``demo``) when installed on a database + without demo data, so the known credentials cannot be used to log in on a + production instance; the accounts stay active on demo/evaluation databases. + Also lowers ``development_status`` from ``Production/Stable`` to ``Alpha`` so + the demo module no longer signals production-readiness. + ### 19.0.2.1.1 - fix(demo): name each farm after its head member and give every member the head's family name so a household reads as one family; farm names and registry IDs stay unique and generation remains seed-deterministic, resolving duplicate farm names and duplicate Tax/National IDs (#1114) From 6c318c26206bc90afc1b3b8f7a8677dd4610e4dc Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 12:09:15 +0800 Subject: [PATCH 4/6] docs(demo): regenerate README + index.html for demo credential fix Generated files applied verbatim from CI's pinned oca-gen-addon-readme output for spp_demo (19.0.2.1.1) and spp_farmer_registry_demo (19.0.2.1.3). --- spp_demo/README.rst | 20 ++++++++++++++-- spp_demo/static/description/index.html | 22 ++++++++++++++++-- spp_farmer_registry_demo/README.rst | 19 +++++++++++++-- .../static/description/index.html | 23 ++++++++++++++++--- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/spp_demo/README.rst b/spp_demo/README.rst index bca243dbf..29e7921e9 100644 --- a/spp_demo/README.rst +++ b/spp_demo/README.rst @@ -10,9 +10,9 @@ OpenSPP Demo !! source digest: sha256:0000000000000000000000000000000000000000000000000000000000000000 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status - :alt: Production/Stable + :alt: Alpha .. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 @@ -138,6 +138,10 @@ Dependencies External Python dependency: ``faker`` +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + **Table of contents** .. contents:: @@ -146,6 +150,18 @@ External Python dependency: ``faker`` Changelog ========= +19.0.2.1.1 +~~~~~~~~~~ + +- fix(security): deactivate the default-credential demo users (including + the ``sppadmin`` SPP admin, all created with the well-known password + ``demo``) when the module is installed on a database without demo + data, so the known credentials cannot be used to log in on a + production instance. The accounts stay active on demo/evaluation + databases. Also lowers ``development_status`` from + ``Production/Stable`` to ``Alpha`` so the demo module no longer + signals production-readiness. + 19.0.2.1.0 ~~~~~~~~~~ diff --git a/spp_demo/static/description/index.html b/spp_demo/static/description/index.html index 94ef4dba8..8724f7567 100644 --- a/spp_demo/static/description/index.html +++ b/spp_demo/static/description/index.html @@ -369,7 +369,7 @@

OpenSPP Demo

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:0000000000000000000000000000000000000000000000000000000000000000 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Production/Stable License: LGPL-3 OpenSPP/OpenSPP2

+

Alpha License: LGPL-3 OpenSPP/OpenSPP2

Demo data generator for OpenSPP with Faker-based random registrant creation and fixed demo stories. Generates realistic individuals and groups with locale-specific data providers. Includes predefined personas @@ -514,6 +514,11 @@

Dependencies

base, spp_base_common, spp_registry, spp_vocabulary, queue_job, spp_security

External Python dependency: faker

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production.

+

Table of contents

+

19.0.2.1.1

+
    +
  • fix(security): deactivate the default-credential demo users (including +the sppadmin SPP admin, all created with the well-known password +demo) when the module is installed on a database without demo +data, so the known credentials cannot be used to log in on a +production instance. The accounts stay active on demo/evaluation +databases. Also lowers development_status from +Production/Stable to Alpha so the demo module no longer +signals production-readiness.
  • +
+
+

19.0.2.1.0

  • feat(demo): re-land curated PHL geodata and demo generator updates @@ -536,7 +554,7 @@

    19.0.2.1.0

    population weights.
-
+

19.0.2.0.0

  • Initial migration to OpenSPP2
  • diff --git a/spp_farmer_registry_demo/README.rst b/spp_farmer_registry_demo/README.rst index 281dd5018..ff1d762eb 100644 --- a/spp_farmer_registry_demo/README.rst +++ b/spp_farmer_registry_demo/README.rst @@ -10,9 +10,9 @@ OpenSPP Farmer Registry Demo !! source digest: sha256:force_regen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status - :alt: Production/Stable + :alt: Alpha .. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 @@ -112,6 +112,10 @@ Dependencies ``spp_farmer_registry_cr``, ``spp_studio``, ``spp_registry_group_hierarchy``, ``spp_area``, ``spp_programs`` +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + **Table of contents** .. contents:: @@ -120,6 +124,17 @@ Dependencies Changelog ========= +19.0.2.1.3 +~~~~~~~~~~ + +- fix(security): deactivate this module's default-credential demo users + (created with the well-known password ``demo``) when installed on a + database without demo data, so the known credentials cannot be used to + log in on a production instance; the accounts stay active on + demo/evaluation databases. Also lowers ``development_status`` from + ``Production/Stable`` to ``Alpha`` so the demo module no longer + signals production-readiness. + 19.0.2.1.1 ~~~~~~~~~~ diff --git a/spp_farmer_registry_demo/static/description/index.html b/spp_farmer_registry_demo/static/description/index.html index 4e9aa8e7b..a25394dd8 100644 --- a/spp_farmer_registry_demo/static/description/index.html +++ b/spp_farmer_registry_demo/static/description/index.html @@ -369,7 +369,7 @@

    OpenSPP Farmer Registry Demo

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:force_regen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

    Production/Stable License: LGPL-3 OpenSPP/OpenSPP2

    +

    Alpha License: LGPL-3 OpenSPP/OpenSPP2

    Demo data generator for the OpenSPP Farmer Registry. Creates 8 named farmer personas with complete Philippine farm profiles, 5 agricultural subsidy programs with CEL eligibility expressions, and optionally @@ -477,6 +477,11 @@

    Dependencies

    spp_starter_farmer_registry, spp_demo, spp_farmer_registry_cr, spp_studio, spp_registry_group_hierarchy, spp_area, spp_programs

    +
    +

    Important

    +

    This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production.

    +

    Table of contents

+

19.0.2.1.3

+
    +
  • fix(security): deactivate this module’s default-credential demo users +(created with the well-known password demo) when installed on a +database without demo data, so the known credentials cannot be used to +log in on a production instance; the accounts stay active on +demo/evaluation databases. Also lowers development_status from +Production/Stable to Alpha so the demo module no longer +signals production-readiness.
  • +
+
+

19.0.2.1.1

  • fix(demo): name each farm after its head member and give every member @@ -502,7 +519,7 @@

    19.0.2.1.1

    (#1114)
-
+

19.0.2.1.0

  • feat(demo): add GIS + irrigation scenario (FM4) with reservoir + canal @@ -521,7 +538,7 @@

    19.0.2.1.0

    tables and the CR overview
-
+

19.0.2.0.0

  • Initial migration to OpenSPP2
  • From d89b70650594737a096a00aa11400d4dc5477b3b Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 13:00:51 +0800 Subject: [PATCH 5/6] test(demo): cover demo_data_enabled + hook wiring; document upgrade coupling From staff review: the helper tests hard-coded demo_enabled, leaving the load-bearing demo_data_enabled() detection and the manifest hook wiring untested. Add assertions that demo_data_enabled matches the module's real demo flag and that both manifests register post_init_hook. Also document in users_data.xml that the archived-user safety depends on these records carrying no active field (the hook runs on install only). --- spp_demo/data/users_data.xml | 9 +++++++++ spp_demo/tests/test_demo_user_safety.py | 18 +++++++++++++++++- .../tests/test_demo_user_safety.py | 15 ++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/spp_demo/data/users_data.xml b/spp_demo/data/users_data.xml index 336c81f1c..4e6d6dfec 100644 --- a/spp_demo/data/users_data.xml +++ b/spp_demo/data/users_data.xml @@ -10,6 +10,15 @@ Login credentials: username / demo ========================================================================== + + SECURITY: on a database without demo data (a production-style install) + these default-credential users are archived by spp_demo's post_init_hook + (see __init__.py). That hook runs on install only. These records + intentionally set NO `active` field, so an upgrade re-run of this + noupdate="0" file does not reactivate archived users. Do NOT add + `` here without also re-archiving on + upgrade, or a production upgrade would silently re-enable the accounts. + ========================================================================== --> diff --git a/spp_demo/tests/test_demo_user_safety.py b/spp_demo/tests/test_demo_user_safety.py index 6eddb0700..15a4f771a 100644 --- a/spp_demo/tests/test_demo_user_safety.py +++ b/spp_demo/tests/test_demo_user_safety.py @@ -12,7 +12,11 @@ from odoo.modules.module import get_manifest from odoo.tests import TransactionCase, tagged -from odoo.addons.spp_demo import DEFAULT_DEMO_USER_XMLIDS, deactivate_default_demo_users +from odoo.addons.spp_demo import ( + DEFAULT_DEMO_USER_XMLIDS, + deactivate_default_demo_users, + demo_data_enabled, +) @tagged("post_install", "-at_install") @@ -61,3 +65,15 @@ def test_spp_demo_not_marked_production_stable(self): get_manifest("spp_demo").get("development_status"), "Production/Stable", ) + + def test_demo_data_enabled_matches_module_flag(self): + """The production-vs-demo decision is load-bearing: it must reflect the + module's real demo flag. A regression here (wrong module name, changed + Odoo semantics) would leave production exposed while the helper tests + above still pass.""" + module = self.env["ir.module.module"].search([("name", "=", "spp_demo")], limit=1) + self.assertEqual(demo_data_enabled(self.env, "spp_demo"), bool(module.demo)) + + def test_manifest_wires_post_init_hook(self): + """The guard only runs if the manifest actually registers the hook.""" + self.assertEqual(get_manifest("spp_demo").get("post_init_hook"), "post_init_hook") diff --git a/spp_farmer_registry_demo/tests/test_demo_user_safety.py b/spp_farmer_registry_demo/tests/test_demo_user_safety.py index f6087341f..9dc8615b9 100644 --- a/spp_farmer_registry_demo/tests/test_demo_user_safety.py +++ b/spp_farmer_registry_demo/tests/test_demo_user_safety.py @@ -10,7 +10,7 @@ from odoo.modules.module import get_manifest from odoo.tests import TransactionCase, tagged -from odoo.addons.spp_demo import deactivate_default_demo_users +from odoo.addons.spp_demo import deactivate_default_demo_users, demo_data_enabled from odoo.addons.spp_farmer_registry_demo import DEFAULT_DEMO_USER_XMLIDS @@ -53,3 +53,16 @@ def test_farmer_demo_not_marked_production_stable(self): get_manifest("spp_farmer_registry_demo").get("development_status"), "Production/Stable", ) + + def test_demo_data_enabled_matches_module_flag(self): + module = self.env["ir.module.module"].search( + [("name", "=", "spp_farmer_registry_demo")], limit=1 + ) + self.assertEqual( + demo_data_enabled(self.env, "spp_farmer_registry_demo"), bool(module.demo) + ) + + def test_manifest_wires_post_init_hook(self): + self.assertEqual( + get_manifest("spp_farmer_registry_demo").get("post_init_hook"), "post_init_hook" + ) From 13d04823ce5e2fe31ca1a8c72ed8e51538a1bb04 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 13:30:14 +0800 Subject: [PATCH 6/6] style(demo): ruff-format the farmer demo user-safety test --- .../tests/test_demo_user_safety.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/spp_farmer_registry_demo/tests/test_demo_user_safety.py b/spp_farmer_registry_demo/tests/test_demo_user_safety.py index 9dc8615b9..11c8b25a4 100644 --- a/spp_farmer_registry_demo/tests/test_demo_user_safety.py +++ b/spp_farmer_registry_demo/tests/test_demo_user_safety.py @@ -55,14 +55,8 @@ def test_farmer_demo_not_marked_production_stable(self): ) def test_demo_data_enabled_matches_module_flag(self): - module = self.env["ir.module.module"].search( - [("name", "=", "spp_farmer_registry_demo")], limit=1 - ) - self.assertEqual( - demo_data_enabled(self.env, "spp_farmer_registry_demo"), bool(module.demo) - ) + module = self.env["ir.module.module"].search([("name", "=", "spp_farmer_registry_demo")], limit=1) + self.assertEqual(demo_data_enabled(self.env, "spp_farmer_registry_demo"), bool(module.demo)) def test_manifest_wires_post_init_hook(self): - self.assertEqual( - get_manifest("spp_farmer_registry_demo").get("post_init_hook"), "post_init_hook" - ) + self.assertEqual(get_manifest("spp_farmer_registry_demo").get("post_init_hook"), "post_init_hook")