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 3d86d9bf226..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 @@ -67,6 +71,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/__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/pg_session_store.py b/session_db/pg_session_store.py index ad47eb4fec0..bf63e9d557d 100644 --- a/session_db/pg_session_store.py +++ b/session_db/pg_session_store.py @@ -149,6 +149,48 @@ 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): + continue + 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..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.

@@ -380,13 +385,14 @@

Store sessions in DB

  • Bug Tracker
  • Credits
  • -

    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.

    @@ -394,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 @@ -402,16 +408,24 @@

    Bug Tracker

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

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Odoo SA
    • ACSONE SA/NV
    +
    +

    Contributors

    + +
    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

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

    Maintainers

    +
    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)