diff --git a/auth_session_timeout/README.rst b/auth_session_timeout/README.rst index 63325502c..8b0f2c282 100644 --- a/auth_session_timeout/README.rst +++ b/auth_session_timeout/README.rst @@ -1,6 +1,8 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :alt: License: AGPL-3 + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +========================= Inactive Sessions Timeout ========================= @@ -13,16 +15,32 @@ Configuration Two system parameters are available: -* inactive_session_time_out_delay: validity of a session in seconds (default = 2 Hours) -* inactive_session_time_out_ignored_url: technical urls where the check does not occur +* ``inactive_session_time_out_delay``: validity of a session in seconds + (default = 2 Hours) +* ``inactive_session_time_out_ignored_url``: technical urls where the check + does not occur + +Usage +===== + +Setup the session parameters as described above. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/149/9.0 + +Known issues / Roadmap +====================== + 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 smashing it by providing a detailed and welcomed feedback -`here `_. +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 smashing it by providing a detailed and welcomed feedback. + Credits ======= @@ -32,16 +50,19 @@ Contributors * Cédric Pigeon * Dhinesh D +* Dave Lasley Maintainer ---------- -.. image:: http://odoo-community.org/logo.png +.. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association - :target: http://odoo-community.org + :target: https://odoo-community.org This module is maintained by the OCA. -OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. -To contribute to this module, please visit http://odoo-community.org. +To contribute to this module, please visit https://odoo-community.org. diff --git a/auth_session_timeout/__init__.py b/auth_session_timeout/__init__.py index 9b1fb35b0..852bfeac2 100644 --- a/auth_session_timeout/__init__.py +++ b/auth_session_timeout/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # (c) 2015 ACSONE SA/NV, Dhinesh D - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import models diff --git a/auth_session_timeout/__manifest__.py b/auth_session_timeout/__manifest__.py index 5b1452402..837ae425b 100644 --- a/auth_session_timeout/__manifest__.py +++ b/auth_session_timeout/__manifest__.py @@ -1,28 +1,22 @@ # -*- coding: utf-8 -*- # (c) 2015 ACSONE SA/NV, Dhinesh D - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { 'name': "Inactive Sessions Timeout", - 'summary': """ This module disable all inactive sessions since a given delay""", - - 'author': "ACSONE SA/NV, Dhinesh D, Odoo Community Association (OCA)", + 'author': "ACSONE SA/NV, " + "Dhinesh D, " + "LasLabs, " + "Odoo Community Association (OCA)", 'maintainer': 'Odoo Community Association (OCA)', 'website': "http://acsone.eu", - 'category': 'Tools', - 'version': '9.0.1.0.0', + 'version': '10.0.1.0.0', 'license': 'AGPL-3', - - 'depends': [ - 'base', - ], - 'data': [ 'data/ir_config_parameter_data.xml' ], - 'installable': False, + 'installable': True, } diff --git a/auth_session_timeout/data/ir_config_parameter_data.xml b/auth_session_timeout/data/ir_config_parameter_data.xml index 96b0194bf..fb0b1709e 100644 --- a/auth_session_timeout/data/ir_config_parameter_data.xml +++ b/auth_session_timeout/data/ir_config_parameter_data.xml @@ -4,18 +4,13 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> - - - - inactive_session_time_out_delay - 7200 - - - - - - inactive_session_time_out_ignored_url - /calendar/notify,/longpolling/poll - - + + + inactive_session_time_out_delay + 7200 + + + inactive_session_time_out_ignored_url + /calendar/notify,/longpolling/poll + diff --git a/auth_session_timeout/models/__init__.py b/auth_session_timeout/models/__init__.py index 0c6063031..4c14e36fd 100644 --- a/auth_session_timeout/models/__init__.py +++ b/auth_session_timeout/models/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # (c) 2015 ACSONE SA/NV, Dhinesh D - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import res_users diff --git a/auth_session_timeout/models/ir_config_parameter.py b/auth_session_timeout/models/ir_config_parameter.py index 69a7003f0..8e2e87e36 100644 --- a/auth_session_timeout/models/ir_config_parameter.py +++ b/auth_session_timeout/models/ir_config_parameter.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- # (c) 2015 ACSONE SA/NV, Dhinesh D - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, api, tools, SUPERUSER_ID +from odoo import models, api, tools DELAY_KEY = 'inactive_session_time_out_delay' @@ -13,24 +12,18 @@ IGNORED_PATH_KEY = 'inactive_session_time_out_ignored_url' class IrConfigParameter(models.Model): _inherit = 'ir.config_parameter' - @tools.ormcache(skiparg=0) - def get_session_parameters(self, db): - param_model = self.pool['ir.config_parameter'] - cr = self.pool.cursor() - delay = False - urls = [] - try: - delay = int(param_model.get_param( - cr, SUPERUSER_ID, DELAY_KEY, 7200)) - urls = param_model.get_param( - cr, SUPERUSER_ID, IGNORED_PATH_KEY, '').split(',') - finally: - cr.close() - return delay, urls + @api.model + @tools.ormcache('self.env.cr.dbname') + def get_session_parameters(self): + ConfigParam = self.env['ir.config_parameter'] + delay = ConfigParam.get_param(DELAY_KEY, 7200) + urls = ConfigParam.get_param(IGNORED_PATH_KEY, '').split(',') + return int(delay), urls @api.multi - def write(self, vals, context=None): + def write(self, vals): res = super(IrConfigParameter, self).write(vals) - if self.key in [DELAY_KEY, IGNORED_PATH_KEY]: - self.get_session_parameters.clear_cache(self) + for rec_id in self: + if rec_id.key in (DELAY_KEY, IGNORED_PATH_KEY): + self.get_session_parameters.clear_cache(self) return res diff --git a/auth_session_timeout/models/res_users.py b/auth_session_timeout/models/res_users.py index 95a137df9..db9b5dc6e 100644 --- a/auth_session_timeout/models/res_users.py +++ b/auth_session_timeout/models/res_users.py @@ -1,29 +1,25 @@ # -*- coding: utf-8 -*- # (c) 2015 ACSONE SA/NV, Dhinesh D - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models -from openerp import http - -from openerp.http import root -from openerp.http import request - from os import utime from os.path import getmtime from time import time +from odoo import models, http + class ResUsers(models.Model): _inherit = 'res.users' - def _check_session_validity(self, db, uid, passwd): - if not request: + @classmethod + def _check_session_validity(cls, db, uid, passwd): + if not http.request: return - session = request.session - session_store = root.session_store - param_obj = self.pool['ir.config_parameter'] - delay, urls = param_obj.get_session_parameters(db) + session = http.request.session + session_store = http.root.session_store + ConfigParam = http.request.env['ir.config_parameter'] + delay, urls = ConfigParam.get_session_parameters() deadline = time() - delay path = session_store.get_session_filename(session.sid) try: @@ -38,7 +34,8 @@ class ResUsers(models.Model): pass return - def check(self, db, uid, passwd): - res = super(ResUsers, self).check(db, uid, passwd) - self._check_session_validity(db, uid, passwd) + @classmethod + def check(cls, db, uid, passwd): + res = super(ResUsers, cls).check(db, uid, passwd) + cls._check_session_validity(db, uid, passwd) return res diff --git a/auth_session_timeout/tests/__init__.py b/auth_session_timeout/tests/__init__.py index 7ae3d0692..a4a711662 100644 --- a/auth_session_timeout/tests/__init__.py +++ b/auth_session_timeout/tests/__init__.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -# (c) 2015 ACSONE SA/NV, Dhinesh D - -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import test_ir_config_parameter +from . import test_res_users diff --git a/auth_session_timeout/tests/test_ir_config_parameter.py b/auth_session_timeout/tests/test_ir_config_parameter.py index 6a7249f8e..b5cc8d2de 100644 --- a/auth_session_timeout/tests/test_ir_config_parameter.py +++ b/auth_session_timeout/tests/test_ir_config_parameter.py @@ -1,28 +1,32 @@ # -*- coding: utf-8 -*- # (c) 2015 ACSONE SA/NV, Dhinesh D - +# Copyright 2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -import threading - -from openerp.tests import common -import openerp +from odoo.tests.common import TransactionCase -class TestIrConfigParameter(common.TransactionCase): +class TestIrConfigParameter(TransactionCase): def setUp(self): super(TestIrConfigParameter, self).setUp() - self.db = openerp.tools.config['db_name'] - if not self.db and hasattr(threading.current_thread(), 'dbname'): - self.db = threading.current_thread().dbname self.param_obj = self.env['ir.config_parameter'] self.data_obj = self.env['ir.model.data'] self.delay = self.env.ref( - 'auth_session_timeout.inactive_session_time_out_delay') + 'auth_session_timeout.inactive_session_time_out_delay' + ) + self.url = self.env.ref( + 'auth_session_timeout.inactive_session_time_out_ignored_url' + ) + self.urls = ['url1', 'url2'] + self.url.value = ','.join(self.urls) - def test_check_delay(self): - delay, urls = self.param_obj.get_session_parameters(self.db) + def test_get_session_parameters_delay(self): + """ It should return the proper delay """ + delay, _ = self.param_obj.get_session_parameters() self.assertEqual(delay, int(self.delay.value)) - self.assertIsInstance(delay, int) - self.assertIsInstance(urls, list) + + def test_get_session_parameters_url(self): + """ It should return URIs split by comma """ + _, urls = self.param_obj.get_session_parameters() + self.assertEqual(urls, self.urls) diff --git a/auth_session_timeout/tests/test_res_users.py b/auth_session_timeout/tests/test_res_users.py new file mode 100644 index 000000000..eab8c802d --- /dev/null +++ b/auth_session_timeout/tests/test_res_users.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock + +from contextlib import contextmanager + +from odoo.tests.common import TransactionCase + + +class EndTestException(Exception): + """ It stops tests from continuing """ + pass + + +class TestResUsers(TransactionCase): + + def setUp(self): + super(TestResUsers, self).setUp() + self.ResUsers = self.env['res.users'] + + @contextmanager + def _mock_assets(self, assets=None): + """ It provides mocked imports from res_users.py + :param assets: (list) Name of imports to mock. Mocks `http` if None + :return: (dict) Dictionary of mocks, keyed by module name + """ + if assets is None: + assets = ['http'] + patches = {name: mock.DEFAULT for name in assets} + with mock.patch.multiple( + 'odoo.addons.auth_session_timeout.models.res_users', **patches + ) as mocks: + yield mocks + + def _check_session_validity(self): + """ It wraps ``_check_session_validity`` for easier calling """ + self.db = mock.MagicMock() + self.uid = mock.MagicMock() + self.passwd = mock.MagicMock() + return self.ResUsers._check_session_validity( + self.db, self.uid, self.passwd, + ) + + def test_session_validity_no_request(self): + """ It should return immediately if no request """ + with self._mock_assets() as assets: + assets['http'].request = False + res = self._check_session_validity() + self.assertFalse(res) + + def test_session_validity_gets_params(self): + """ It should call ``get_session_parameters`` with db """ + with self._mock_assets() as assets: + get_params = assets['http'].request.env[''].get_session_parameters + get_params.side_effect = EndTestException + with self.assertRaises(EndTestException): + self._check_session_validity() + get_params.assert_called_once_with() + + def test_session_validity_gets_session_file(self): + """ It should call get the session file for the session id """ + with self._mock_assets() as assets: + get_params = assets['http'].request.env[''].get_session_parameters + get_params.return_value = 0, [] + store = assets['http'].root.session_store + store.get_session_filename.side_effect = EndTestException + with self.assertRaises(EndTestException): + self._check_session_validity() + store.get_session_filename.assert_called_once_with( + assets['http'].request.session.sid, + ) + + def test_session_validity_logout(self): + """ It should log out of session if past deadline """ + with self._mock_assets(['http', 'getmtime', 'utime']) as assets: + get_params = assets['http'].request.env[''].get_session_parameters + get_params.return_value = -9999, [] + assets['getmtime'].return_value = 0 + self._check_session_validity() + assets['http'].request.session.logout.assert_called_once_with( + keep_db=True, + ) + + def test_session_validity_updates_utime(self): + """ It should update utime of session file if not expired """ + with self._mock_assets(['http', 'getmtime', 'utime']) as assets: + get_params = assets['http'].request.env[''].get_session_parameters + get_params.return_value = 9999, [] + self._check_session_validity() + assets['utime'].assert_called_once_with( + assets['http'].root.session_store.get_session_filename(), + None, + ) + + def test_session_validity_os_error_guard(self): + """ It should properly guard from OSError & return """ + with self._mock_assets(['http', 'utime', 'getmtime']) as assets: + get_params = assets['http'].request.env[''].get_session_parameters + get_params.return_value = 0, [] + assets['getmtime'].side_effect = OSError + res = self._check_session_validity() + self.assertFalse(res)