Since it's first implementation at [1], a new cursor is created to then build a new environment after the website's `authenticate` overide with the new `uid` of the logged in user. Since the new visitor SQL upsert refactoring done at [2], it seems to be introducing a SQL deadlock sometimes. It has been detected on odoo.com while monitoring the logs. Despite being quite rare, it still happens too often due to our heavy trafic. Step to reproduce (among others): - Install website_livechat - Login as admin on 127.0.0.1 - On 127.0.0.2, load the website - Open the livechat - Type something in the discussion - Directly try to login as admin - It will load for a certain time then crash on a 502 timeout due to the SQL deadlock - You may need to repeat the process to actually face the bug [1]: https://github.com/odoo/odoo/commit/6bec0e4d29e6b33b74962b2893a7a405667ef58c [2]: https://github.com/odoo/odoo/commit/d348bed1ad9d3d16b295f013f015706be6c07820 closes odoo/odoo#109830 X-original-commit: b241cf7de9329af1410b9dd45b161aa41926effb Signed-off-by: Olivier Dony (odo) <odo@odoo.com> Signed-off-by: Romain Derie (rde) <rde@odoo.com>
90 lines
3.8 KiB
Python
90 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import logging
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.http import request
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ResUsers(models.Model):
|
|
_inherit = 'res.users'
|
|
|
|
website_id = fields.Many2one('website', related='partner_id.website_id', store=True, related_sudo=False, readonly=False)
|
|
|
|
_sql_constraints = [
|
|
# Partial constraint, complemented by a python constraint (see below).
|
|
('login_key', 'unique (login, website_id)', 'You can not have two users with the same login!'),
|
|
]
|
|
|
|
@api.constrains('login', 'website_id')
|
|
def _check_login(self):
|
|
""" Do not allow two users with the same login without website """
|
|
self.flush_model(['login', 'website_id'])
|
|
self.env.cr.execute(
|
|
"""SELECT login
|
|
FROM res_users
|
|
WHERE login IN (SELECT login FROM res_users WHERE id IN %s AND website_id IS NULL)
|
|
AND website_id IS NULL
|
|
GROUP BY login
|
|
HAVING COUNT(*) > 1
|
|
""",
|
|
(tuple(self.ids),)
|
|
)
|
|
if self.env.cr.rowcount:
|
|
raise ValidationError(_('You can not have two users with the same login!'))
|
|
|
|
@api.model
|
|
def _get_login_domain(self, login):
|
|
website = self.env['website'].get_current_website()
|
|
return super(ResUsers, self)._get_login_domain(login) + website.website_domain()
|
|
|
|
@api.model
|
|
def _get_login_order(self):
|
|
return 'website_id, ' + super(ResUsers, self)._get_login_order()
|
|
|
|
@api.model
|
|
def _signup_create_user(self, values):
|
|
current_website = self.env['website'].get_current_website()
|
|
if request and current_website.specific_user_account:
|
|
values['company_id'] = current_website.company_id.id
|
|
values['company_ids'] = [(4, current_website.company_id.id)]
|
|
values['website_id'] = current_website.id
|
|
new_user = super(ResUsers, self)._signup_create_user(values)
|
|
return new_user
|
|
|
|
@api.model
|
|
def _get_signup_invitation_scope(self):
|
|
current_website = self.env['website'].get_current_website()
|
|
return current_website.auth_signup_uninvited or super(ResUsers, self)._get_signup_invitation_scope()
|
|
|
|
@classmethod
|
|
def authenticate(cls, db, login, password, user_agent_env):
|
|
""" Override to link the logged in user's res.partner to website.visitor.
|
|
If a visitor already exists for that user, assign it data from the
|
|
current anonymous visitor (if exists).
|
|
Purpose is to try to aggregate as much sub-records (tracked pages,
|
|
leads, ...) as possible. """
|
|
visitor_pre_authenticate_sudo = None
|
|
if request and request.env:
|
|
visitor_pre_authenticate_sudo = request.env['website.visitor']._get_visitor_from_request()
|
|
uid = super(ResUsers, cls).authenticate(db, login, password, user_agent_env)
|
|
if uid and visitor_pre_authenticate_sudo:
|
|
env = api.Environment(request.env.cr, uid, {})
|
|
user_partner = env.user.partner_id
|
|
visitor_current_user_sudo = env['website.visitor'].sudo().search([
|
|
('partner_id', '=', user_partner.id)
|
|
], limit=1)
|
|
if visitor_current_user_sudo:
|
|
# A visitor exists for the logged in user, link public
|
|
# visitor records to it.
|
|
if visitor_pre_authenticate_sudo != visitor_current_user_sudo:
|
|
visitor_pre_authenticate_sudo._merge_visitor(visitor_current_user_sudo)
|
|
visitor_current_user_sudo._update_visitor_last_visit()
|
|
else:
|
|
visitor_pre_authenticate_sudo.access_token = user_partner.id
|
|
visitor_pre_authenticate_sudo._update_visitor_last_visit()
|
|
return uid
|