diff --git a/spp_drims_sl/README.rst b/spp_drims_sl/README.rst
index adb1b9b14..b458e92b2 100644
--- a/spp_drims_sl/README.rst
+++ b/spp_drims_sl/README.rst
@@ -159,6 +159,15 @@ Dependencies
Changelog
=========
+19.0.2.0.1
+~~~~~~~~~~
+
+- fix(security): archive the 14 default-credential DRIMS-SL demo users
+ (shared password ``demo``, including ``admin.dmc@drims.gov.lk`` which
+ holds ``base.group_system``) on a production install via a
+ self-contained ``post_init_hook``; they stay active only when demo
+ data is enabled.
+
19.0.2.0.0
~~~~~~~~~~
diff --git a/spp_drims_sl/__init__.py b/spp_drims_sl/__init__.py
index 7f8d9f342..7f6bb16a8 100644
--- a/spp_drims_sl/__init__.py
+++ b/spp_drims_sl/__init__.py
@@ -1,2 +1,65 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
-# Configuration module - no Python imports
+import logging
+
+_logger = logging.getLogger(__name__)
+
+# res.users records created with the shared, documented password "demo" by
+# data/demo_users.xml (including "user_admin_dmc", which holds base.group_system).
+# 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. This module does not depend on spp_demo,
+# so the archiving helper is kept self-contained here.
+DEFAULT_DEMO_USER_XMLIDS = [
+ "spp_drims_sl.user_admin_dmc",
+ "spp_drims_sl.user_wh_staff_colombo",
+ "spp_drims_sl.user_wh_staff_kandy",
+ "spp_drims_sl.user_wh_colombo",
+ "spp_drims_sl.user_wh_kandy",
+ "spp_drims_sl.user_approver_national",
+ "spp_drims_sl.user_approver_western",
+ "spp_drims_sl.user_approver_central",
+ "spp_drims_sl.user_officer_colombo",
+ "spp_drims_sl.user_officer_gampaha",
+ "spp_drims_sl.user_officer_kandy",
+ "spp_drims_sl.user_officer_galle",
+ "spp_drims_sl.user_viewer_secretary",
+ "spp_drims_sl.user_viewer_director",
+]
+
+
+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 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 DRIMS-SL 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_drims_sl"))
diff --git a/spp_drims_sl/__manifest__.py b/spp_drims_sl/__manifest__.py
index cf7133664..cf0e53435 100644
--- a/spp_drims_sl/__manifest__.py
+++ b/spp_drims_sl/__manifest__.py
@@ -5,7 +5,7 @@
"inventory management. Includes geographic hierarchy, government agencies, "
"and approval thresholds per DMC requirements.",
"category": "OpenSPP/Inventory",
- "version": "19.0.2.0.0",
+ "version": "19.0.2.0.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
@@ -39,6 +39,7 @@
# Second pass: enable multitier (must load after tiers are created)
"data/approval_config_multitier.xml",
],
+ "post_init_hook": "post_init_hook",
"application": False,
"installable": True,
"auto_install": False,
diff --git a/spp_drims_sl/data/demo_users.xml b/spp_drims_sl/data/demo_users.xml
index 0220a9998..dcd8bdaec 100644
--- a/spp_drims_sl/data/demo_users.xml
+++ b/spp_drims_sl/data/demo_users.xml
@@ -26,6 +26,12 @@
- officer.galle@drims.gov.lk - Field Officer
- secretary@drims.gov.lk - Secretary (Viewer, for dashboard tutorial)
- director@drims.gov.lk - Director (Viewer)
+
+ SECURITY: these users share the well-known password "demo" and load via the
+ manifest `data` section, so they exist after any install. On a production
+ database (installed without demo data) they are archived by this module's
+ post_init_hook (see __init__.py) so the well-known password cannot be used
+ to log in; on a demo/evaluation database they stay active.
-->
diff --git a/spp_drims_sl/readme/HISTORY.md b/spp_drims_sl/readme/HISTORY.md
index 4aaf9afef..ce5e72f93 100644
--- a/spp_drims_sl/readme/HISTORY.md
+++ b/spp_drims_sl/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.0.1
+
+- fix(security): archive the 14 default-credential DRIMS-SL demo users (shared password `demo`, including `admin.dmc@drims.gov.lk` which holds `base.group_system`) on a production install via a self-contained `post_init_hook`; they stay active only when demo data is enabled.
+
### 19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_drims_sl/static/description/index.html b/spp_drims_sl/static/description/index.html
index bc3c7f646..42988a7c9 100644
--- a/spp_drims_sl/static/description/index.html
+++ b/spp_drims_sl/static/description/index.html
@@ -572,6 +572,16 @@
+
19.0.2.0.1
+
+- fix(security): archive the 14 default-credential DRIMS-SL demo users
+(shared password demo, including admin.dmc@drims.gov.lk which
+holds base.group_system) on a production install via a
+self-contained post_init_hook; they stay active only when demo
+data is enabled.
+
+
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_drims_sl/tests/__init__.py b/spp_drims_sl/tests/__init__.py
index defe031fd..a2c939edd 100644
--- a/spp_drims_sl/tests/__init__.py
+++ b/spp_drims_sl/tests/__init__.py
@@ -1,2 +1,3 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
from . import test_spp_drims_sl
+from . import test_demo_user_safety
diff --git a/spp_drims_sl/tests/test_demo_user_safety.py b/spp_drims_sl/tests/test_demo_user_safety.py
new file mode 100644
index 000000000..0f75bb082
--- /dev/null
+++ b/spp_drims_sl/tests/test_demo_user_safety.py
@@ -0,0 +1,83 @@
+# 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 DRIMS Sri Lanka users with the shared password
+``demo`` (including ``user_admin_dmc``, which holds ``base.group_system``). On a
+production database (installed without demo data) they must be deactivated; on a
+demo/evaluation database they must stay active. This module does not depend on
+spp_demo, so the archiving helper is self-contained here.
+"""
+
+from odoo.modules.module import get_manifest
+from odoo.tests import TransactionCase, tagged
+
+from odoo.addons.spp_drims_sl import (
+ DEFAULT_DEMO_USER_XMLIDS,
+ deactivate_default_demo_users,
+ demo_data_enabled,
+ post_init_hook,
+)
+
+
+@tagged("post_install", "-at_install")
+class TestDrimsSlDemoUserSafety(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, "DRIMS-SL 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 DRIMS-SL default-credential demo users must be inactive when demo is disabled",
+ )
+
+ def test_admin_user_is_covered(self):
+ """The base.group_system super-admin demo account must be in the archive set."""
+ self.assertIn("spp_drims_sl.user_admin_dmc", DEFAULT_DEMO_USER_XMLIDS)
+ admin = self.env.ref("spp_drims_sl.user_admin_dmc", raise_if_not_found=False)
+ self.assertTrue(admin, "user_admin_dmc should exist")
+ self.assertTrue(admin.has_group("base.group_system"))
+
+ 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")),
+ "DRIMS-SL demo users must remain active when demo data is enabled",
+ )
+
+ def test_post_init_hook_archives_users_on_production(self):
+ users = self._default_users()
+ users.active = True
+ module = self.env["ir.module.module"].search([("name", "=", "spp_drims_sl")], limit=1)
+
+ if module.demo:
+ self.skipTest("demo data enabled on this database; production path not exercised")
+
+ post_init_hook(self.env)
+
+ self.assertFalse(
+ any(users.mapped("active")),
+ "post_init_hook must archive default-credential users when demo is disabled",
+ )
+
+ def test_demo_data_enabled_matches_module_flag(self):
+ module = self.env["ir.module.module"].search([("name", "=", "spp_drims_sl")], limit=1)
+ self.assertEqual(demo_data_enabled(self.env, "spp_drims_sl"), bool(module.demo))
+
+ def test_manifest_wires_post_init_hook(self):
+ self.assertEqual(get_manifest("spp_drims_sl").get("post_init_hook"), "post_init_hook")
diff --git a/spp_drims_sl_demo/README.rst b/spp_drims_sl_demo/README.rst
index 07011511f..e50d28283 100644
--- a/spp_drims_sl_demo/README.rst
+++ b/spp_drims_sl_demo/README.rst
@@ -130,6 +130,15 @@ Dependencies
Changelog
=========
+19.0.2.0.1
+~~~~~~~~~~
+
+- fix(security): archive the six default-credential DRIMS-SL demo users
+ (``kumari``, ``rajitha``, ``silva``, ``perera``, ``fernando``,
+ ``secretary``, shared password ``demo``) on a production install via a
+ ``post_init_hook`` reusing spp_drims_sl's helper; they stay active
+ only when demo data is enabled.
+
19.0.2.0.0
~~~~~~~~~~
diff --git a/spp_drims_sl_demo/__init__.py b/spp_drims_sl_demo/__init__.py
index 0450a0c80..2a3bb88e7 100644
--- a/spp_drims_sl_demo/__init__.py
+++ b/spp_drims_sl_demo/__init__.py
@@ -1,3 +1,24 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
from . import models
from . import wizard
+
+# res.users records created with the shared, documented password "demo" by
+# data/demo_users.xml. 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. This module depends
+# on spp_drims_sl, so it reuses that module's self-contained archiving helper.
+DEFAULT_DEMO_USER_XMLIDS = [
+ "spp_drims_sl_demo.user_kumari",
+ "spp_drims_sl_demo.user_rajitha",
+ "spp_drims_sl_demo.user_silva",
+ "spp_drims_sl_demo.user_perera",
+ "spp_drims_sl_demo.user_fernando",
+ "spp_drims_sl_demo.user_secretary",
+]
+
+
+def post_init_hook(env):
+ """Neutralize this module's default-credential demo users in production."""
+ from odoo.addons.spp_drims_sl import deactivate_default_demo_users, demo_data_enabled
+
+ deactivate_default_demo_users(env, DEFAULT_DEMO_USER_XMLIDS, demo_data_enabled(env, "spp_drims_sl_demo"))
diff --git a/spp_drims_sl_demo/__manifest__.py b/spp_drims_sl_demo/__manifest__.py
index 17074375b..77e357d73 100644
--- a/spp_drims_sl_demo/__manifest__.py
+++ b/spp_drims_sl_demo/__manifest__.py
@@ -4,7 +4,7 @@
"summary": "Demo data generator for DRIMS Sri Lanka implementation. "
"Creates sample incidents, donations, requests, and stock for demonstrations.",
"category": "OpenSPP/Inventory",
- "version": "19.0.2.0.0",
+ "version": "19.0.2.0.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
@@ -24,6 +24,7 @@
"data/demo_gis_reports.xml",
"wizard/demo_generator_views.xml",
],
+ "post_init_hook": "post_init_hook",
"application": False,
"installable": True,
"auto_install": False,
diff --git a/spp_drims_sl_demo/data/demo_users.xml b/spp_drims_sl_demo/data/demo_users.xml
index 0e5417c74..ca78038be 100644
--- a/spp_drims_sl_demo/data/demo_users.xml
+++ b/spp_drims_sl_demo/data/demo_users.xml
@@ -11,6 +11,13 @@
- secretary: Viewer - dashboard and reports only
Login credentials: username / demo
+
+ SECURITY: these users share the well-known password "demo" and load via the
+ manifest `data` section, so they exist after any install. On a production
+ database (installed without demo data) they are archived by this module's
+ post_init_hook (see __init__.py, reusing spp_drims_sl's helper) so the
+ well-known password cannot be used to log in; on a demo/evaluation database
+ they stay active.
==========================================================================
-->
diff --git a/spp_drims_sl_demo/readme/HISTORY.md b/spp_drims_sl_demo/readme/HISTORY.md
index 4aaf9afef..214c3fb1f 100644
--- a/spp_drims_sl_demo/readme/HISTORY.md
+++ b/spp_drims_sl_demo/readme/HISTORY.md
@@ -1,3 +1,7 @@
+### 19.0.2.0.1
+
+- fix(security): archive the six default-credential DRIMS-SL demo users (`kumari`, `rajitha`, `silva`, `perera`, `fernando`, `secretary`, shared password `demo`) on a production install via a `post_init_hook` reusing spp_drims_sl's helper; they stay active only when demo data is enabled.
+
### 19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_drims_sl_demo/static/description/index.html b/spp_drims_sl_demo/static/description/index.html
index f0a2f58e5..b538d1b00 100644
--- a/spp_drims_sl_demo/static/description/index.html
+++ b/spp_drims_sl_demo/static/description/index.html
@@ -503,6 +503,16 @@
+
19.0.2.0.1
+
+- fix(security): archive the six default-credential DRIMS-SL demo users
+(kumari, rajitha, silva, perera, fernando,
+secretary, shared password demo) on a production install via a
+post_init_hook reusing spp_drims_sl’s helper; they stay active
+only when demo data is enabled.
+
+
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_drims_sl_demo/tests/__init__.py b/spp_drims_sl_demo/tests/__init__.py
index 197e91754..336caed89 100644
--- a/spp_drims_sl_demo/tests/__init__.py
+++ b/spp_drims_sl_demo/tests/__init__.py
@@ -1,2 +1,3 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
from . import test_demo_generator
+from . import test_demo_user_safety
diff --git a/spp_drims_sl_demo/tests/test_demo_user_safety.py b/spp_drims_sl_demo/tests/test_demo_user_safety.py
new file mode 100644
index 000000000..ffeb41b6b
--- /dev/null
+++ b/spp_drims_sl_demo/tests/test_demo_user_safety.py
@@ -0,0 +1,71 @@
+# 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 DRIMS Sri Lanka demo users with the shared
+password ``demo``. On a production database (installed without demo data) they
+must be deactivated; on a demo/evaluation database they must stay active. The
+archiving helper is reused from spp_drims_sl (this module's dependency).
+"""
+
+from odoo.modules.module import get_manifest
+from odoo.tests import TransactionCase, tagged
+
+from odoo.addons.spp_drims_sl import deactivate_default_demo_users, demo_data_enabled
+from odoo.addons.spp_drims_sl_demo import DEFAULT_DEMO_USER_XMLIDS, post_init_hook
+
+
+@tagged("post_install", "-at_install")
+class TestDrimsSlDemoDemoUserSafety(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, "DRIMS-SL 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 DRIMS-SL demo default-credential 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")),
+ "DRIMS-SL demo users must remain active when demo data is enabled",
+ )
+
+ def test_post_init_hook_archives_users_on_production(self):
+ users = self._default_users()
+ users.active = True
+ module = self.env["ir.module.module"].search([("name", "=", "spp_drims_sl_demo")], limit=1)
+
+ if module.demo:
+ self.skipTest("demo data enabled on this database; production path not exercised")
+
+ post_init_hook(self.env)
+
+ self.assertFalse(
+ any(users.mapped("active")),
+ "post_init_hook must archive default-credential users when demo is disabled",
+ )
+
+ def test_demo_data_enabled_matches_module_flag(self):
+ module = self.env["ir.module.module"].search([("name", "=", "spp_drims_sl_demo")], limit=1)
+ self.assertEqual(demo_data_enabled(self.env, "spp_drims_sl_demo"), bool(module.demo))
+
+ def test_manifest_wires_post_init_hook(self):
+ self.assertEqual(get_manifest("spp_drims_sl_demo").get("post_init_hook"), "post_init_hook")
diff --git a/spp_grm_demo/README.rst b/spp_grm_demo/README.rst
index cbf53f0c0..af52c94b2 100644
--- a/spp_grm_demo/README.rst
+++ b/spp_grm_demo/README.rst
@@ -10,9 +10,9 @@ OpenSPP GRM Demo Data
!! 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
@@ -115,6 +115,10 @@ Dependencies
``spp_demo``, ``spp_grm``, ``spp_grm_registry``, ``spp_grm_programs``,
``spp_security``, ``faker`` (Python)
+.. 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::
@@ -123,6 +127,15 @@ Dependencies
Changelog
=========
+19.0.2.0.1
+~~~~~~~~~~
+
+- fix(security): archive the default-credential GRM demo users
+ (``demo_grm_manager``, ``demo_grm_officer``, shared password ``demo``)
+ on a production install via a ``post_init_hook``; they stay active
+ only when demo data is enabled. Also drop ``Production/Stable`` (this
+ is a demo-only bundle).
+
19.0.2.0.0
~~~~~~~~~~
diff --git a/spp_grm_demo/__init__.py b/spp_grm_demo/__init__.py
index ebdc65039..de5f016ad 100644
--- a/spp_grm_demo/__init__.py
+++ b/spp_grm_demo/__init__.py
@@ -1,4 +1,56 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
+import logging
from . import models
from . import wizard
+
+_logger = logging.getLogger(__name__)
+
+# res.users records created with the shared, documented password "demo" by
+# data/demo_users.xml. 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. The archiving
+# helper is kept self-contained here so this module carries no dependency on
+# another module's security helper.
+DEFAULT_DEMO_USER_XMLIDS = [
+ "spp_grm_demo.demo_user_grm_manager",
+ "spp_grm_demo.demo_user_grm_officer",
+]
+
+
+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 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 GRM 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 this module's default-credential demo users in production."""
+ deactivate_default_demo_users(env, DEFAULT_DEMO_USER_XMLIDS, demo_data_enabled(env, "spp_grm_demo"))
diff --git a/spp_grm_demo/__manifest__.py b/spp_grm_demo/__manifest__.py
index d3f5e62ac..afa0ccb6a 100644
--- a/spp_grm_demo/__manifest__.py
+++ b/spp_grm_demo/__manifest__.py
@@ -3,13 +3,13 @@
{
"name": "OpenSPP GRM Demo Data",
- "version": "19.0.2.0.0",
+ "version": "19.0.2.0.1",
"category": "OpenSPP/Monitoring",
- "summary": "Demo data generator for Grievance Redress Mechanism",
+ "summary": "DEMO ONLY — do not install in production. Demo data generator for Grievance Redress Mechanism.",
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
"license": "LGPL-3",
- "development_status": "Production/Stable",
+ "development_status": "Alpha",
"maintainers": ["jeremi", "gonzalesedwin1123", "emjay0921"],
"depends": [
"spp_demo", # Consolidated demo module
@@ -27,6 +27,7 @@
],
"demo": [],
"images": [],
+ "post_init_hook": "post_init_hook",
"application": False,
"installable": True,
"auto_install": False,
diff --git a/spp_grm_demo/data/demo_users.xml b/spp_grm_demo/data/demo_users.xml
index 0fd103df1..d218607a1 100644
--- a/spp_grm_demo/data/demo_users.xml
+++ b/spp_grm_demo/data/demo_users.xml
@@ -1,5 +1,13 @@
+
OpenSPP GRM Demo Data
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:force_regen
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

Demo data generator for the Grievance Redress Mechanism. Creates both
story-based tickets linked to specific personas (Juan Dela Cruz, Ibrahim
Hassan, Fatima Al-Rahman, Ahmed Said, David Martinez, Maria Santos, Rosa
@@ -482,6 +482,11 @@
Integration
Dependencies
spp_demo, spp_grm, spp_grm_registry, spp_grm_programs,
spp_security, faker (Python)
+
+
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.0.1
+
+- fix(security): archive the default-credential GRM demo users
+(demo_grm_manager, demo_grm_officer, shared password demo)
+on a production install via a post_init_hook; they stay active
+only when demo data is enabled. Also drop Production/Stable (this
+is a demo-only bundle).
+
+
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_grm_demo/tests/__init__.py b/spp_grm_demo/tests/__init__.py
index 6d721cd78..16414b46f 100644
--- a/spp_grm_demo/tests/__init__.py
+++ b/spp_grm_demo/tests/__init__.py
@@ -2,3 +2,4 @@
from . import test_grm_demo_wizard
from . import test_story_tickets
+from . import test_demo_user_safety
diff --git a/spp_grm_demo/tests/test_demo_user_safety.py b/spp_grm_demo/tests/test_demo_user_safety.py
new file mode 100644
index 000000000..727a66fbb
--- /dev/null
+++ b/spp_grm_demo/tests/test_demo_user_safety.py
@@ -0,0 +1,64 @@
+# 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 GRM demo users with the shared password
+``demo``. 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_grm_demo import (
+ DEFAULT_DEMO_USER_XMLIDS,
+ deactivate_default_demo_users,
+ demo_data_enabled,
+)
+
+
+@tagged("post_install", "-at_install")
+class TestGrmDemoUserSafety(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, "GRM 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 GRM 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")),
+ "GRM demo users must remain active when demo data is enabled",
+ )
+
+ def test_grm_demo_not_marked_production_stable(self):
+ self.assertNotEqual(
+ get_manifest("spp_grm_demo").get("development_status"),
+ "Production/Stable",
+ )
+
+ def test_demo_data_enabled_matches_module_flag(self):
+ module = self.env["ir.module.module"].search([("name", "=", "spp_grm_demo")], limit=1)
+ self.assertEqual(demo_data_enabled(self.env, "spp_grm_demo"), bool(module.demo))
+
+ def test_manifest_wires_post_init_hook(self):
+ self.assertEqual(get_manifest("spp_grm_demo").get("post_init_hook"), "post_init_hook")
diff --git a/spp_mis_demo_v2/README.rst b/spp_mis_demo_v2/README.rst
index d75e0054b..d41724b82 100644
--- a/spp_mis_demo_v2/README.rst
+++ b/spp_mis_demo_v2/README.rst
@@ -10,9 +10,9 @@ OpenSPP MIS Demo V2
!! 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
@@ -153,6 +153,10 @@ Dependencies
``spp_starter_sp_mis``, ``spp_cr_types_advanced``, ``spp_demo``,
``spp_gis_report``, ``spp_claim_169``
+.. 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::
@@ -161,6 +165,18 @@ Dependencies
Changelog
=========
+19.0.2.1.4
+~~~~~~~~~~
+
+- fix(security): archive the seven default-credential MIS demo users
+ (``demo_local_registrar``, ``demo_global_registrar``,
+ ``demo_cr_local_validator``, ``demo_cr_hq_validator``,
+ ``demo_program_manager``, ``demo_program_validator``,
+ ``demo_cycle_approver``, shared password ``demo``) on a production
+ install; the ``post_init_hook`` now composes demo-variable activation
+ with archiving, and the users stay active only when demo data is
+ enabled. Also drop ``Production/Stable`` (this is a demo-only bundle).
+
19.0.2.1.3
~~~~~~~~~~
diff --git a/spp_mis_demo_v2/__init__.py b/spp_mis_demo_v2/__init__.py
index 6f903c3a8..628f8650c 100644
--- a/spp_mis_demo_v2/__init__.py
+++ b/spp_mis_demo_v2/__init__.py
@@ -1,5 +1,69 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
+import logging
from . import models
from . import wizard
-from .models.indicator_providers import post_init_hook
+from .models.indicator_providers import post_init_hook as _activate_demo_variables
+
+_logger = logging.getLogger(__name__)
+
+# res.users records created with the shared, documented password "demo" by
+# data/demo_users.xml. 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. (The base.user_admin
+# and spp_demo.* records in that file are re-roles of existing users, handled by
+# spp_demo's own hook.) The archiving helper is kept self-contained here so this
+# module carries no dependency on another module's security helper.
+DEFAULT_DEMO_USER_XMLIDS = [
+ "spp_mis_demo_v2.demo_user_local_registrar",
+ "spp_mis_demo_v2.demo_user_global_registrar",
+ "spp_mis_demo_v2.demo_user_cr_local_validator",
+ "spp_mis_demo_v2.demo_user_cr_hq_validator",
+ "spp_mis_demo_v2.demo_user_program_manager",
+ "spp_mis_demo_v2.demo_user_program_validator",
+ "spp_mis_demo_v2.demo_user_cycle_approver",
+]
+
+
+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 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 MIS 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):
+ """Activate demo registry variables, then neutralize default-credential users.
+
+ Wraps the variable-activation hook so the manifest's single ``post_init_hook``
+ entry both prepares the MIS demo variables and archives the well-known-password
+ demo users on a production install (they stay active when demo data is enabled).
+ """
+ _activate_demo_variables(env)
+ deactivate_default_demo_users(env, DEFAULT_DEMO_USER_XMLIDS, demo_data_enabled(env, "spp_mis_demo_v2"))
diff --git a/spp_mis_demo_v2/__manifest__.py b/spp_mis_demo_v2/__manifest__.py
index 42a61b397..3ec79a234 100644
--- a/spp_mis_demo_v2/__manifest__.py
+++ b/spp_mis_demo_v2/__manifest__.py
@@ -2,14 +2,14 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
{
"name": "OpenSPP MIS Demo V2",
- "summary": "Demo Generator V2 for SP-MIS programs with fixed stories and volume generation",
+ "summary": "DEMO ONLY — do not install in production. Demo Generator V2 for SP-MIS programs with fixed stories and volume generation.",
"category": "OpenSPP",
- "version": "19.0.2.1.3",
+ "version": "19.0.2.1.4",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
"license": "LGPL-3",
- "development_status": "Production/Stable",
+ "development_status": "Alpha",
"maintainers": ["jeremi", "gonzalesedwin1123"],
"depends": [
# SP-MIS Starter Bundle (includes registry, programs, API, DCI, CR, CEL, etc.)
diff --git a/spp_mis_demo_v2/data/demo_users.xml b/spp_mis_demo_v2/data/demo_users.xml
index 959fcefef..0f1918add 100644
--- a/spp_mis_demo_v2/data/demo_users.xml
+++ b/spp_mis_demo_v2/data/demo_users.xml
@@ -5,10 +5,14 @@
These are demo operator/staff accounts with different roles for testing
access control, approval workflows, and multi-user scenarios.
- IMPORTANT: For security reasons, these demo users should ONLY be loaded in
+ IMPORTANT: For security reasons, these demo users should ONLY be active in
demo/testing environments. Never use in production.
- All demo users share the same password: "demo" (hashed in the system)
+ All demo users share the same password: "demo" (hashed in the system).
+ They load via the manifest `data` section, so they exist after any install.
+ On a production database (installed without demo data) this module's
+ post_init_hook (see __init__.py) archives them so the well-known password
+ cannot be used to log in; on a demo/evaluation database they stay active.
-->
-

+

Demo data generator for SP-MIS programs. Creates 7 social protection
programs with CEL eligibility expressions, enrolls 8 demo personas with
payment histories, and optionally generates ~730 deterministic
@@ -520,6 +520,11 @@
Extension Points
Dependencies
spp_starter_sp_mis, spp_cr_types_advanced, spp_demo,
spp_gis_report, spp_claim_169
+
+
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.4
+
+- fix(security): archive the seven default-credential MIS demo users
+(demo_local_registrar, demo_global_registrar,
+demo_cr_local_validator, demo_cr_hq_validator,
+demo_program_manager, demo_program_validator,
+demo_cycle_approver, shared password demo) on a production
+install; the post_init_hook now composes demo-variable activation
+with archiving, and the users stay active only when demo data is
+enabled. Also drop Production/Stable (this is a demo-only bundle).
+
+
+
19.0.2.1.3
- fix: PHL story registrants map to the curated PSGC p-code area
@@ -539,7 +557,7 @@
19.0.2.1.3
dropping area assignments).
-
+
19.0.2.1.2
- fix: demo GIS reports use dimension_ids + member_expansion
@@ -548,7 +566,7 @@
19.0.2.1.2
spp_gis_report dimension change).
-
+
19.0.2.1.1
- feat(demo): adapt the change-request demo generator to the redesigned
@@ -556,7 +574,7 @@
19.0.2.1.1
Head of Household uses per-member role lines (#873) (#242)
-
+
19.0.2.1.0
- feat(demo): seed country-appropriate CR document types (≥5 per country
@@ -565,7 +583,7 @@
19.0.2.1.0
files to a change request without defining them manually (#1102)
-
+
19.0.2.0.0
- Initial migration to OpenSPP2
diff --git a/spp_mis_demo_v2/tests/__init__.py b/spp_mis_demo_v2/tests/__init__.py
index 566a572f8..f772fdefb 100644
--- a/spp_mis_demo_v2/tests/__init__.py
+++ b/spp_mis_demo_v2/tests/__init__.py
@@ -12,3 +12,4 @@
from . import test_registry_variables
from . import test_demo_statistics
from . import test_story_area_map
+from . import test_demo_user_safety
diff --git a/spp_mis_demo_v2/tests/test_demo_user_safety.py b/spp_mis_demo_v2/tests/test_demo_user_safety.py
new file mode 100644
index 000000000..50e3e55aa
--- /dev/null
+++ b/spp_mis_demo_v2/tests/test_demo_user_safety.py
@@ -0,0 +1,82 @@
+# 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 MIS demo users with the shared password
+``demo``. On a production database (installed without demo data) they must be
+deactivated; on a demo/evaluation database they must stay active. The module's
+``post_init_hook`` composes the demo-variable activation with this archiving.
+"""
+
+from odoo.modules.module import get_manifest
+from odoo.tests import TransactionCase, tagged
+
+from odoo.addons.spp_mis_demo_v2 import (
+ DEFAULT_DEMO_USER_XMLIDS,
+ deactivate_default_demo_users,
+ demo_data_enabled,
+ post_init_hook,
+)
+
+
+@tagged("post_install", "-at_install")
+class TestMisDemoUserSafety(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, "MIS 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 MIS 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")),
+ "MIS demo users must remain active when demo data is enabled",
+ )
+
+ def test_composed_hook_archives_users_on_production(self):
+ """The module's own post_init_hook must archive the users when demo is off."""
+ users = self._default_users()
+ users.active = True
+ module = self.env["ir.module.module"].search([("name", "=", "spp_mis_demo_v2")], limit=1)
+
+ if module.demo:
+ self.skipTest("demo data enabled on this database; production path not exercised")
+
+ post_init_hook(self.env)
+
+ self.assertFalse(
+ any(users.mapped("active")),
+ "the composed post_init_hook must archive default-credential users when demo is disabled",
+ )
+
+ def test_mis_demo_not_marked_production_stable(self):
+ self.assertNotEqual(
+ get_manifest("spp_mis_demo_v2").get("development_status"),
+ "Production/Stable",
+ )
+
+ def test_demo_data_enabled_matches_module_flag(self):
+ module = self.env["ir.module.module"].search([("name", "=", "spp_mis_demo_v2")], limit=1)
+ self.assertEqual(demo_data_enabled(self.env, "spp_mis_demo_v2"), bool(module.demo))
+
+ def test_manifest_wires_post_init_hook(self):
+ self.assertEqual(get_manifest("spp_mis_demo_v2").get("post_init_hook"), "post_init_hook")
diff --git a/spp_mis_demo_v2/tests/test_mis_demo_generator.py b/spp_mis_demo_v2/tests/test_mis_demo_generator.py
index 6dc5d09c8..c85e45c28 100644
--- a/spp_mis_demo_v2/tests/test_mis_demo_generator.py
+++ b/spp_mis_demo_v2/tests/test_mis_demo_generator.py
@@ -13,6 +13,19 @@ def setUpClass(cls):
super().setUpClass()
# Create test country for locale
cls.test_country = cls.env.ref("base.us")
+ # The module's post_init_hook archives the default-credential demo users
+ # on a database without demo data (the test harness installs without
+ # demo). The demo CR generator drives a tiered approval workflow that can
+ # only be performed by the designated demo validator users, so reactivate
+ # them here: running the generator is an explicit demo action, exactly the
+ # deliberate reactivation the hook's warning describes.
+ for xmlid in (
+ "spp_mis_demo_v2.demo_user_cr_local_validator",
+ "spp_mis_demo_v2.demo_user_cr_hq_validator",
+ ):
+ user = cls.env.ref(xmlid, raise_if_not_found=False)
+ if user:
+ user.active = True
def test_generator_creation(self):
"""Test that generator can be created with defaults."""