[IMP] mail, im_livechat: fix performance of channel access rule
Current access rule requires fetching all members of each channel, which does not scale. Use is_member field instead, and make sure itself does not fetch all members. task-2818759 closes odoo/odoo#88221 Related: odoo/upgrade#3422 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
@@ -129,10 +129,10 @@ class TestGetMailChannel(TransactionCase):
|
||||
'email': 'michel@example.com',
|
||||
'livechat_username': 'Michel at your service',
|
||||
})
|
||||
channel_info = self.livechat_channel.with_user(public_user)._open_livechat_mail_channel(anonymous_name='whatever')
|
||||
channel_info = self.livechat_channel.with_user(public_user).sudo()._open_livechat_mail_channel(anonymous_name='whatever')
|
||||
channel = self.env['mail.channel'].browse(channel_info['id'])
|
||||
channel.with_user(operator).message_post(body='Hello', message_type='comment', subtype_xmlid='mail.mt_comment')
|
||||
message_formats = channel.with_user(public_user)._channel_fetch_message()
|
||||
message_formats = channel.with_user(public_user).sudo()._channel_fetch_message()
|
||||
self.assertEqual(len(message_formats), 1)
|
||||
self.assertEqual(message_formats[0]['author_id'][0], operator.partner_id.id)
|
||||
self.assertEqual(message_formats[0]['author_id'][1], operator.livechat_username)
|
||||
|
||||
@@ -73,7 +73,7 @@ class Channel(models.Model):
|
||||
'mail.channel.partner', 'channel_id', string='Last Seen',
|
||||
groups='base.group_user')
|
||||
rtc_session_ids = fields.One2many('mail.channel.rtc.session', 'channel_id', groups="base.group_system")
|
||||
is_member = fields.Boolean('Is Member', compute='_compute_is_member', compute_sudo=True)
|
||||
is_member = fields.Boolean('Is Member', compute='_compute_is_member', search='_search_is_member')
|
||||
member_count = fields.Integer(string="Member Count", compute='_compute_member_count', compute_sudo=True, help="Excluding guests from count.")
|
||||
group_ids = fields.Many2many(
|
||||
'res.groups', string='Auto Subscription',
|
||||
@@ -166,10 +166,38 @@ class Channel(models.Model):
|
||||
])
|
||||
)]
|
||||
|
||||
@api.depends('channel_partner_ids')
|
||||
@api.depends_context('uid', 'guest')
|
||||
@api.depends('channel_last_seen_partner_ids')
|
||||
def _compute_is_member(self):
|
||||
if not self:
|
||||
return
|
||||
if self.env.user._is_public():
|
||||
guest = self.env['mail.guest']._get_guest_from_context()
|
||||
if not guest:
|
||||
self.is_member = False
|
||||
return
|
||||
user_domain = [('guest_id', '=', guest.id)]
|
||||
else:
|
||||
user_domain = [('partner_id', '=', self.env.user.partner_id.id)]
|
||||
members = self.env['mail.channel.partner'].sudo().search(expression.AND([[('channel_id', 'in', self.ids)], user_domain]))
|
||||
is_member_channels = {member.channel_id for member in members}
|
||||
for channel in self:
|
||||
channel.is_member = self.env.user.partner_id in channel.channel_partner_ids
|
||||
channel.is_member = channel in is_member_channels
|
||||
|
||||
def _search_is_member(self, operator, operand):
|
||||
is_in = (operator == '=' and operand) or (operator == '!=' and not operand)
|
||||
if self.env.user._is_public():
|
||||
guest = self.env['mail.guest']._get_guest_from_context()
|
||||
if not guest:
|
||||
return expression.FALSE_DOMAIN if is_in else expression.TRUE_DOMAIN
|
||||
user_domain = [('guest_id', '=', guest.id)]
|
||||
else:
|
||||
user_domain = [('partner_id', '=', self.env.user.partner_id.id)]
|
||||
return [(
|
||||
'channel_last_seen_partner_ids',
|
||||
'in' if is_in else 'not in',
|
||||
self.env['mail.channel.partner'].sudo()._search(user_domain)
|
||||
)]
|
||||
|
||||
@api.depends('channel_partner_ids')
|
||||
def _compute_member_count(self):
|
||||
|
||||
@@ -29,6 +29,13 @@ class MailGuest(models.Model):
|
||||
timezone = fields.Selection(string="Timezone", selection=_tz_get)
|
||||
channel_ids = fields.Many2many(string="Channels", comodel_name='mail.channel', relation='mail_channel_partner', column1='guest_id', column2='channel_id', copy=False)
|
||||
|
||||
def _get_guest_from_context(self):
|
||||
"""Returns the current guest record from the context, if applicable."""
|
||||
guest = self.env.context.get('guest')
|
||||
if isinstance(guest, self.pool['mail.guest']):
|
||||
return guest
|
||||
return self.env['mail.guest']
|
||||
|
||||
def _get_guest_from_request(self, request):
|
||||
parts = request.httprequest.cookies.get(self._cookie_name, '').split(self._cookie_separator)
|
||||
if len(parts) != 2:
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="groups" eval="[Command.link(ref('base.group_user')), Command.link(ref('base.group_portal')), Command.link(ref('base.group_public'))]"/>
|
||||
<field name="domain_force">['|', '|',
|
||||
('public', '=', 'public'),
|
||||
'&', ('public', '=', 'private'), ('channel_partner_ids', 'in', [user.partner_id.id]),
|
||||
'&', ('public', '=', 'private'), ('is_member', '=', True),
|
||||
'&', ('public', '=', 'groups'), ('group_public_id', 'in', [g.id for g in user.groups_id])]</field>
|
||||
<field name="perm_create" eval="False"/>
|
||||
</record>
|
||||
@@ -19,7 +19,7 @@
|
||||
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
|
||||
<field name="domain_force">['|', '|',
|
||||
('channel_id.public', '=', 'public'),
|
||||
'&', ('channel_id.public', '=', 'private'), ('channel_id.channel_partner_ids', 'in', [user.partner_id.id]),
|
||||
'&', ('channel_id.public', '=', 'private'), ('channel_id.is_member', '=', True),
|
||||
'&', ('channel_id.public', '=', 'groups'), ('channel_id.group_public_id', 'in', [g.id for g in user.groups_id])]</field>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
|
||||
@@ -43,7 +43,7 @@ class TestChannelAccessRights(MailCommon):
|
||||
'public': 'private'})
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_rule', 'odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
@users('user_public')
|
||||
@users('user_portal')
|
||||
def test_access_public(self):
|
||||
# Read public group -> ok
|
||||
self.env['mail.channel'].browse(self.group_public.id).read()
|
||||
@@ -56,7 +56,7 @@ class TestChannelAccessRights(MailCommon):
|
||||
self.env['mail.channel'].browse(self.group_private.id).read()
|
||||
|
||||
# Read a private group when being a member: ok
|
||||
self.group_private.write({'channel_partner_ids': [(4, self.user_public.partner_id.id)]})
|
||||
self.group_private.write({'channel_partner_ids': [(4, self.user_portal.partner_id.id)]})
|
||||
self.env['mail.channel'].browse(self.group_private.id).read()
|
||||
|
||||
# Create group: ko, no access rights
|
||||
|
||||
Reference in New Issue
Block a user