[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:
Sébastien Theys
2022-04-08 15:52:31 +02:00
parent cc23863f09
commit a29a7183a4
5 changed files with 44 additions and 9 deletions
@@ -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)
+31 -3
View File
@@ -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):
+7
View File
@@ -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:
+2 -2
View File
@@ -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'),
'&amp;', ('public', '=', 'private'), ('channel_partner_ids', 'in', [user.partner_id.id]),
'&amp;', ('public', '=', 'private'), ('is_member', '=', True),
'&amp;', ('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'),
'&amp;', ('channel_id.public', '=', 'private'), ('channel_id.channel_partner_ids', 'in', [user.partner_id.id]),
'&amp;', ('channel_id.public', '=', 'private'), ('channel_id.is_member', '=', True),
'&amp;', ('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"/>
+2 -2
View File
@@ -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