diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index b2f9b268dea..e3996348043 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -869,7 +869,18 @@ class Home(http.Controller): return response def _login_redirect(self, uid, redirect=None): - return redirect if redirect else '/web' + if request.session.uid: # fully logged + return redirect or '/web' + + # partial session (MFA) + url = request.env(user=uid)['res.users'].browse(uid)._mfa_url() + if not redirect: + return url + + parsed = werkzeug.urls.url_parse(url) + qs = parsed.decode_query() + qs['redirect'] = redirect + return parsed.replace(query=werkzeug.urls.url_encode(qs)).to_url() @http.route('/web/login', type='http', auth="none") def web_login(self, redirect=None, **kw): diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index 336be363e36..11d0f0980ae 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -998,6 +998,9 @@ class Users(models.Model): return 'base/static/img/user-slash.png' return super()._get_placeholder_filename(field=field) + def _mfa_url(self): + """ If an MFA method is enabled, returns the URL for its second step. """ + return # # Implied groups # diff --git a/odoo/http.py b/odoo/http.py index 40da5a5b131..d025e7fac31 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -48,7 +48,6 @@ except ImportError: psutil = None import odoo -from odoo import fields from .service.server import memory_info from .service import security, model as service_model from .sql_db import flush_env @@ -992,18 +991,29 @@ class OpenERPSession(sessions.Session): REMOTE_ADDR=wsgienv['REMOTE_ADDR'], ) uid = odoo.registry(db)['res.users'].authenticate(db, login, password, env) + self.pre_uid = uid + + user = request.env(user=uid)['res.users'].browse(uid) + if not user._mfa_url(): + self.finalize() self.rotate = True self.db = db - self.uid = uid self.login = login - self.session_token = security.compute_session_token(self, request.env) - request.uid = uid request.disable_db = False - self.get_context() return uid + def finalize(self): + """ Finalizes a partial session, should be called on MFA validation to + convert a partial / pre-session into a full-fledged "logged-in" one + """ + self.rotate = True + request.uid = self.uid = self.pop('pre_uid') + user = request.env(user=self.uid)['res.users'].browse(self.uid) + self.session_token = user._compute_session_token(self.sid) + self.get_context() + def check_security(self): """ Check the current authentication parameters to know if those are still