Files
odoo_source/addons/auth_totp/controllers/home.py
T
Julien Castiaux 04e972660b [IMP] core: don't save visitor default session
Every request comes with a session, a dictionary that is persisted on
the filesystem and that saves various information such as the user
cart on the ecommerce.

When a user simply visits the website, a default session is created and
saved on disk, this bloats the filestore with many sessions. Creating
the session on-the-fly is cheaper than loading it from the filesystem.
With this work the default session is not saved on disk anymore unless
explicitly asked via `session.touch()`.

An exception to the statement "creating the session on-the-fly is
cheaper" is geoip, the ip geolocalization is not cheap. In this work,
geoip have been moved from http_routing/request.session.geoip to a
lazy property core/request.geoip. When requested the info is persisted
on the session. Like other keys from the default session, geoip will not
be persisted unless there is non-default stuff in the session.

Because the CSRF-TOKEN is based on the session-id, it is important the
session-id stays the same across multiples requests even when the
session is not persisted on disk. Even when a session is not persisted
on disk, the session-id cookie is still set so that the next session
created on-the-fly uses the same session-id.

Technical note regarding the session, it has been decided to drop the
session-snapshot protocol and to reintroduce a "modified" flag. It has
been decided not to use werkzeug's session (which natively comes with a
"modified" flag) and to keep our own session object. We decided to
extend MutableMapping instead of dict; using MutableMapping we only
have to override __setitem__ and __detitem__; using dict we would had to
override update()/pop()/... too.

Task: 2789035
Part-of: odoo/odoo#86015
2022-04-05 14:13:54 +02:00

79 lines
3.3 KiB
Python

# -*- coding: utf-8 -*-
import re
from odoo import http, _
from odoo.exceptions import AccessDenied
from odoo.http import request
from odoo.addons.web.controllers import home as web_home
TRUSTED_DEVICE_COOKIE = 'td_id'
TRUSTED_DEVICE_AGE = 90*86400 # 90 days expiration
class Home(web_home.Home):
@http.route(
'/web/login/totp',
type='http', auth='public', methods=['GET', 'POST'], sitemap=False,
website=True, multilang=False # website breaks the login layout...
)
def web_totp(self, redirect=None, **kwargs):
if request.session.uid:
return request.redirect(self._login_redirect(request.session.uid, redirect=redirect))
if not request.session.pre_uid:
return request.redirect('/web/login')
error = None
user = request.env['res.users'].browse(request.session.pre_uid)
if user and request.httprequest.method == 'GET':
cookies = request.httprequest.cookies
key = cookies.get(TRUSTED_DEVICE_COOKIE)
if key:
checked_credentials = request.env['auth_totp.device']._check_credentials(scope="browser", key=key)
if checked_credentials == user.id:
request.session.finalize(request.env)
return request.redirect(self._login_redirect(request.session.uid, redirect=redirect))
elif user and request.httprequest.method == 'POST' and kwargs.get('totp_token'):
try:
with user._assert_can_auth():
user._totp_check(int(re.sub(r'\s', '', kwargs['totp_token'])))
except AccessDenied as e:
error = str(e)
except ValueError:
error = _("Invalid authentication code format.")
else:
request.session.finalize(request.env)
request.update_env(user=request.session.uid)
request.update_context(**request.session.context)
response = request.redirect(self._login_redirect(request.session.uid, redirect=redirect))
if kwargs.get('remember'):
name = _("%(browser)s on %(platform)s",
browser=request.httprequest.user_agent.browser.capitalize(),
platform=request.httprequest.user_agent.platform.capitalize(),
)
geoip = request.geoip
if geoip:
name += " (%s, %s)" % (geoip['city'], geoip['country_name'])
key = request.env['auth_totp.device']._generate("browser", name)
response.set_cookie(
key=TRUSTED_DEVICE_COOKIE,
value=key,
max_age=TRUSTED_DEVICE_AGE,
httponly=True,
samesite='Lax'
)
# Crapy workaround for unupdatable Odoo Mobile App iOS (Thanks Apple :@)
request.session.touch()
return response
# Crapy workaround for unupdatable Odoo Mobile App iOS (Thanks Apple :@)
request.session.touch()
return request.render('auth_totp.auth_totp_form', {
'user': user,
'error': error,
'redirect': redirect,
})