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/__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_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/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_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
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

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_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..15a4f771a
--- /dev/null
+++ b/spp_demo/tests/test_demo_user_safety.py
@@ -0,0 +1,79 @@
+# 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,
+ demo_data_enabled,
+)
+
+
+@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",
+ )
+
+ 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/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/__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",
}
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)
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
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

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
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..11c8b25a4
--- /dev/null
+++ b/spp_farmer_registry_demo/tests/test_demo_user_safety.py
@@ -0,0 +1,62 @@
+# 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, demo_data_enabled
+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",
+ )
+
+ 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")