From 0b178c1d6f5bc979de676c4c308c8e5b5e465df7 Mon Sep 17 00:00:00 2001 From: tarteo Date: Wed, 24 Jun 2026 12:12:48 +0200 Subject: [PATCH 1/3] [FIX] session_db: revoking and cleanup of user devices (backport from 19.0) [IMP] Add tests to test res.device with PGSessionStore --- session_db/README.rst | 7 ++++ session_db/pg_session_store.py | 45 +++++++++++++++++++++++ session_db/readme/CONTRIBUTORS.md | 3 ++ session_db/static/description/index.html | 13 ++++++- session_db/tests/test_pg_session_store.py | 45 +++++++++++++++++++++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 session_db/readme/CONTRIBUTORS.md diff --git a/session_db/README.rst b/session_db/README.rst index 3d86d9bf226..857b99ed7d3 100644 --- a/session_db/README.rst +++ b/session_db/README.rst @@ -67,6 +67,13 @@ Authors * Odoo SA * ACSONE SA/NV +Contributors +------------ + +- Nicolas Seinlet +- Stéphane Bidoul (https://acsone.eu) +- Dennis Sluijk (https://onestein.nl) + Maintainers ----------- diff --git a/session_db/pg_session_store.py b/session_db/pg_session_store.py index ad47eb4fec0..6178fbf47cb 100644 --- a/session_db/pg_session_store.py +++ b/session_db/pg_session_store.py @@ -149,6 +149,51 @@ def vacuum(self, max_lifetime=http.SESSION_LIFETIME): (f"{max_lifetime} seconds",), ) + @with_lock + @with_cursor + def get_missing_session_identifiers(self, identifiers: list[str]) -> set[str]: + """ + :param identifiers: session identifiers whose file existence must be checked + identifiers are a part session sid (first 42 chars) + :type identifiers: iterable + :return: the identifiers which are not present on the filesystem + :rtype: set + + Note 1: + Working with identifiers 42 characters long means that + we don't have to work with the entire sid session, + while maintaining sufficient entropy to avoid collisions. + See details in ``generate_key``. + + Note 2: + Scans the session store for inactive (GC'd) sessions. + Performance is acceptable for an infrequent background job. + """ + missing_identifiers = set() + for identifier in identifiers: + self._cr.execute( + "SELECT sid FROM http_sessions WHERE sid LIKE %s||'%%' LIMIT 1", + (identifier,), + ) + if self._cr.rowcount == 0: + missing_identifiers.add(identifier) + return missing_identifiers + + @with_lock + @with_cursor + def delete_from_identifiers(self, identifiers: list[str]) -> None: + for identifier in identifiers: + if not http._session_identifier_re.match( + identifier + ) and not sessions._sha1_re.match(identifier): + raise ValueError( + "Identifier format incorrect, " + "did you pass in a string instead of a list?" + ) + self._cr.execute( + "DELETE FROM http_sessions WHERE sid LIKE %s||'%%'", (identifier,) + ) + _original_session_store = http.root.__class__.session_store diff --git a/session_db/readme/CONTRIBUTORS.md b/session_db/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..e738f8c0b8a --- /dev/null +++ b/session_db/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- Nicolas Seinlet +- Stéphane Bidoul \<\> () +- Dennis Sluijk \<\> () diff --git a/session_db/static/description/index.html b/session_db/static/description/index.html index 429a004e730..f31405738f4 100644 --- a/session_db/static/description/index.html +++ b/session_db/static/description/index.html @@ -380,7 +380,8 @@

Store sessions in DB

  • Bug Tracker
  • Credits
  • @@ -410,8 +411,16 @@

    Authors

  • ACSONE SA/NV
  • +
    +

    Contributors

    + +
    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association diff --git a/session_db/tests/test_pg_session_store.py b/session_db/tests/test_pg_session_store.py index 970fb275975..9d0c1023c04 100644 --- a/session_db/tests/test_pg_session_store.py +++ b/session_db/tests/test_pg_session_store.py @@ -100,3 +100,48 @@ def test_make_postgres_uri(self): assert "postgres://test:PASSWORD@localhost:5432/test" == _make_postgres_uri( **connection_info ) + + def test_missing_session_identifiers(self): + session = self.session_store.new() + self.session_store.save(session) + missing_identifiers = self.session_store.get_missing_session_identifiers( + [session.sid] + ) + self.assertEqual(missing_identifiers, set()) + self.session_store.delete_from_identifiers([session.sid]) + missing_identifiers = self.session_store.get_missing_session_identifiers( + [session.sid] + ) + self.assertEqual(missing_identifiers, {session.sid}) + + def test_revoke_res_device_log(self): + # Truncate the session table to ensure that the session store is empty before + # starting the test + self.session_store._cr.execute("TRUNCATE TABLE http_sessions") + + # Create a session and save it to the session store + session = self.session_store.new() + self.session_store.save(session) + + # Patch odoo.http.root.session_store to use the test session store + with mock.patch("odoo.http.root.session_store", self.session_store): + # Create a res.device.log entry for the session + log = self.env["res.device"].create( + { + "session_identifier": session.sid, + "user_id": self.ref("base.user_demo"), + "first_activity": "2020-01-01 00:00:00", + "last_activity": "2020-01-01 00:00:00", + } + ) + log._revoke() + + # The session shouldn't exist anymore in the session store + missing_identifiers = self.session_store.get_missing_session_identifiers( + [session.sid] + ) + self.assertEqual(missing_identifiers, {session.sid}) + + # This will return a new session if it can't fetch one + session_in_store = self.session_store.get(session.sid) + self.assertNotEqual(session_in_store.sid, session.sid) From bd99848f9fde91278691c78614a9bb19ce903de2 Mon Sep 17 00:00:00 2001 From: tarteo Date: Wed, 1 Jul 2026 10:30:50 +0200 Subject: [PATCH 2/3] [IMP] session_db: Remove raising value error, ignore invalid session identifiers instead --- session_db/pg_session_store.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/session_db/pg_session_store.py b/session_db/pg_session_store.py index 6178fbf47cb..bf63e9d557d 100644 --- a/session_db/pg_session_store.py +++ b/session_db/pg_session_store.py @@ -186,10 +186,7 @@ def delete_from_identifiers(self, identifiers: list[str]) -> None: if not http._session_identifier_re.match( identifier ) and not sessions._sha1_re.match(identifier): - raise ValueError( - "Identifier format incorrect, " - "did you pass in a string instead of a list?" - ) + continue self._cr.execute( "DELETE FROM http_sessions WHERE sid LIKE %s||'%%'", (identifier,) ) From 54ec915071bb62a3717a5b4ece799c2110d6d23d Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Wed, 1 Jul 2026 08:59:45 +0000 Subject: [PATCH 3/3] [BOT] post-merge updates --- README.md | 2 +- session_db/README.rst | 8 +++++-- session_db/__manifest__.py | 2 +- session_db/static/description/index.html | 28 ++++++++++++++---------- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4c743f71491..3c82eaafd65 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ addon | version | maintainers | summary [scheduler_error_mailer](scheduler_error_mailer/) | 18.0.1.0.0 | | Scheduler Error Mailer [sentry](sentry/) | 18.0.1.0.4 | barsi naglis versada moylop260 fernandahf | Report Odoo errors to Sentry [sequence_python](sequence_python/) | 18.0.1.0.0 | | Calculate a sequence number from a Python expression -[session_db](session_db/) | 18.0.1.0.1 | sbidoul | Store sessions in DB +[session_db](session_db/) | 18.0.1.0.2 | sbidoul | Store sessions in DB [test_auditlog](test_auditlog/) | 18.0.1.0.3 | | Additional unit tests for Audit Log based on accounting models [test_base_time_window](test_base_time_window/) | 18.0.1.0.0 | | Test Base model to handle time windows [tracking_manager](tracking_manager/) | 18.0.1.1.0 | Kev-Roche sebastienbeau | This module tracks all fields of a model, including one2many and many2many ones. diff --git a/session_db/README.rst b/session_db/README.rst index 857b99ed7d3..d6f3a2a7245 100644 --- a/session_db/README.rst +++ b/session_db/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ==================== Store sessions in DB ==================== @@ -7,13 +11,13 @@ Store sessions in DB !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:7558240596b423e92065ebf70859ac382972bca78db62368b1d0e7f06cc2a266 + !! source digest: sha256:b732da96d2a87ff5a4503e576f75338a858701e8c6b32619d8eba6fcd263332d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png +.. |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 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github diff --git a/session_db/__manifest__.py b/session_db/__manifest__.py index ac5b38c2610..0627c9e692a 100644 --- a/session_db/__manifest__.py +++ b/session_db/__manifest__.py @@ -1,6 +1,6 @@ { "name": "Store sessions in DB", - "version": "18.0.1.0.1", + "version": "18.0.1.0.2", "author": "Odoo SA,ACSONE SA/NV,Odoo Community Association (OCA)", "license": "LGPL-3", "website": "https://github.com/OCA/server-tools", diff --git a/session_db/static/description/index.html b/session_db/static/description/index.html index f31405738f4..ed5f9cd1e7a 100644 --- a/session_db/static/description/index.html +++ b/session_db/static/description/index.html @@ -3,7 +3,7 @@ -Store sessions in DB +README.rst -
    -

    Store sessions in DB

    +
    + + +Odoo Community Association + +
    +

    Store sessions in DB

    -

    Beta License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

    +

    Beta License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runboat

    Store sessions in a database instead of the filesystem. This simplifies the configuration of horizontally scalable deployments, by avoiding the need for a distributed filesystem to store the Odoo sessions.

    @@ -387,7 +392,7 @@

    Store sessions in DB

    -

    Usage

    +

    Usage

    Set this module in the server wide modules.

    Set a SESSION_DB_URI environment variable as a full postgresql connection string, like postgres://user:passwd@server/db or db.

    @@ -395,7 +400,7 @@

    Usage

    possibly a dedicated postgres user for additional security.

    -

    Bug Tracker

    +

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -403,16 +408,16 @@

    Bug Tracker

    Do not contact contributors directly about support or help with technical issues.

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Odoo SA
    • ACSONE SA/NV
    -

    Contributors

    +

    Contributors

    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -435,5 +440,6 @@

    Maintainers

    +