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
102 lines
4.2 KiB
Python
102 lines
4.2 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import json
|
|
import logging
|
|
import operator
|
|
|
|
from werkzeug.urls import url_encode
|
|
|
|
import odoo
|
|
import odoo.modules.registry
|
|
from odoo import http
|
|
from odoo.modules import module
|
|
from odoo.exceptions import AccessError, UserError, AccessDenied
|
|
from odoo.http import request
|
|
from odoo.service import dispatch_rpc
|
|
from odoo.tools.translate import _
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Session(http.Controller):
|
|
|
|
@http.route('/web/session/get_session_info', type='json', auth="user")
|
|
def get_session_info(self):
|
|
# Crapy workaround for unupdatable Odoo Mobile App iOS (Thanks Apple :@)
|
|
request.session.touch()
|
|
return request.env['ir.http'].session_info()
|
|
|
|
@http.route('/web/session/authenticate', type='json', auth="none")
|
|
def authenticate(self, db, login, password, base_location=None):
|
|
if not http.db_filter([db]):
|
|
raise AccessError("Database not found.")
|
|
pre_uid = request.session.authenticate(db, login, password)
|
|
if pre_uid != request.session.uid:
|
|
# Crapy workaround for unupdatable Odoo Mobile App iOS (Thanks Apple :@) and Android
|
|
# Correct behavior should be to raise AccessError("Renewing an expired session for user that has multi-factor-authentication is not supported. Please use /web/login instead.")
|
|
return {'uid': None}
|
|
|
|
request.session.db = db
|
|
registry = odoo.modules.registry.Registry(db)
|
|
with registry.cursor() as cr:
|
|
env = odoo.api.Environment(cr, request.session.uid, request.session.context)
|
|
return env['ir.http'].session_info()
|
|
|
|
@http.route('/web/session/change_password', type='json', auth="user")
|
|
def change_password(self, fields):
|
|
old_password, new_password, confirm_password = operator.itemgetter('old_pwd', 'new_password', 'confirm_pwd')(
|
|
{f['name']: f['value'] for f in fields})
|
|
if not (old_password.strip() and new_password.strip() and confirm_password.strip()):
|
|
return {'error': _('You cannot leave any password empty.')}
|
|
if new_password != confirm_password:
|
|
return {'error': _('The new password and its confirmation must be identical.')}
|
|
|
|
msg = _("Error, password not changed !")
|
|
try:
|
|
if request.env['res.users'].change_password(old_password, new_password):
|
|
return {'new_password': new_password}
|
|
except AccessDenied as e:
|
|
msg = e.args[0]
|
|
if msg == AccessDenied().args[0]:
|
|
msg = _('The old password you provided is incorrect, your password was not changed.')
|
|
except UserError as e:
|
|
msg = e.args[0]
|
|
return {'error': msg}
|
|
|
|
@http.route('/web/session/get_lang_list', type='json', auth="none")
|
|
def get_lang_list(self):
|
|
try:
|
|
return dispatch_rpc('db', 'list_lang', []) or []
|
|
except Exception as e:
|
|
return {"error": e, "title": _("Languages")}
|
|
|
|
@http.route('/web/session/modules', type='json', auth="user")
|
|
def modules(self):
|
|
# return all installed modules. Web client is smart enough to not load a module twice
|
|
return list(request.env.registry._init_modules.union([module.current_test] if module.current_test else []))
|
|
|
|
@http.route('/web/session/check', type='json', auth="user")
|
|
def check(self):
|
|
return # ir.http@_authenticate does the job
|
|
|
|
@http.route('/web/session/account', type='json', auth="user")
|
|
def account(self):
|
|
ICP = request.env['ir.config_parameter'].sudo()
|
|
params = {
|
|
'response_type': 'token',
|
|
'client_id': ICP.get_param('database.uuid') or '',
|
|
'state': json.dumps({'d': request.db, 'u': ICP.get_param('web.base.url')}),
|
|
'scope': 'userinfo',
|
|
}
|
|
return 'https://accounts.odoo.com/oauth2/auth?' + url_encode(params)
|
|
|
|
@http.route('/web/session/destroy', type='json', auth="user")
|
|
def destroy(self):
|
|
request.session.logout()
|
|
|
|
@http.route('/web/session/logout', type='http', auth="none")
|
|
def logout(self, redirect='/web'):
|
|
request.session.logout(keep_db=True)
|
|
return request.redirect(redirect, 303)
|