[FIX] core: session logout after 16.0 migration

Create a 15.0 database with website, access the home page via your
browser. Stop the server and migrate the database to 16.0. Restart the
server with a `--dbfilter` that rejects the database you created and
refresh your browser. 500 Internal server error, attribute error:
the `request` object as no `session`.

An error could occurs after a migration to 16.0 due to the presence of
the `geoip` key in the session. `request.session.geoip` has been made a
deprecated alias to `request.geoip` between 15.0 and 16.0, see 04e9726.

Because the session was created before 16.0, the session dict does
contain a `geoip` key. Upon logging the session out, the session dict
is cleared. The default implementation of `clear()`[^1] inside of
`collections.abc.MutableMapping` can be summarized for our usecase to:

    for key in self:
        value = self[key]
        del self[key]

There is an extra `__getitem__` call due to `value = self[key]`, in the
case of the `geoip` key, it would access the alias. It is not possible
to accessing that alias inside of the `_get_dbname_and_session` method
of request as the session has not been set on `self` (the request) yet.

Yet inside of that method, we do `session.logout()` which `clear()` the
session which (wrongly) access the alias because `geoip` exists in the
internal dict (`'geoip' in self.keys()  # True`).

The solution has been to implement the `clear()` function ourself
instead of using the mixin of `MutableMapping`.

[^1]: https://github.com/python/cpython/blob/b43496c01a554cf41ae654a0379efae18609ad39/Lib/_collections_abc.py#L925-L931

closes odoo/odoo#105763

X-original-commit: b66e1ffa8e348eedf2de735babbc398290a8bffb
Signed-off-by: Julien Castiaux <juc@odoo.com>
This commit is contained in:
Julien Castiaux
2022-11-15 18:25:40 +01:00
parent 48ae46cb22
commit ec4826ef5b
2 changed files with 21 additions and 0 deletions
@@ -1,5 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from urllib.parse import urlparse
from unittest.mock import patch
import odoo
@@ -74,3 +75,19 @@ class TestHttpSession(TestHttpBase):
res.raise_for_status()
self.assertEqual(res.text, str(GEOIP_ODOO_FARM_2))
mock_resolve.assert_called_once()
def test_session3_logout_15_0_geoip(self):
session = self.authenticate(None, None)
session['db'] = 'idontexist'
session['geoip'] = {} # Until saas-15.2 geoip was directly stored in the session
odoo.http.root.session_store.save(session)
with self.assertLogs('odoo.http', level='WARNING') as (_, warnings):
res = self.multidb_url_open('/test_http/ensure_db', dblist=['db1', 'db2'])
self.assertEqual(warnings, [
"WARNING:odoo.http:Logged into database 'idontexist', but dbfilter rejects it; logging session out.",
])
self.assertFalse(session['db'])
self.assertEqual(res.status_code, 303)
self.assertEqual(urlparse(res.headers['Location']).path, '/web/database/selector')
+4
View File
@@ -915,6 +915,10 @@ class Session(collections.abc.MutableMapping):
else:
self[key] = val
def clear(self):
self.data.clear()
self.is_dirty = True
#
# Session methods
#