From 37c100a87dc0a5f0cb68928d44f2b6b2b6a16ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 2 Mar 2023 16:41:37 +0000 Subject: [PATCH] [FIX] mail: delegate document attachment check to message when posting When creating a with attachments a manual check is done on given attachemnts to ensure user have rights to read them. Indeed otherwise user could simply give attachments from a protected document when posting on a document he can read and try to gain information about those. However sometimes user can post and create messages without having read access on the document, e.g. when being notified and answering in a multi company environment. In this commit we consider that creating a message with attachments linked to the same document does not require an additional check for attachments. The check is performed at message level (see its custom 'check_access_rule') and is considered sufficient. However attachments linked to other documents using model and res_id are still checked. This fix helps greening the 'test_post_wo_access' test recently introduced. Task-3213982 (Mail: fix 'multi-company' post support) X-original-commit: odoo/odoo@bab05499043809682d016d8785c4f32480810da7 Part-of: odoo/odoo#114511 --- addons/mail/models/mail_message.py | 39 ++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index ede8f076056..4f59dff99c5 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -612,20 +612,43 @@ class Message(models.Model): messages = super(Message, self).create(values_list) - check_attachment_access = [] - if all(isinstance(command, int) or command[0] in (4, 6) for values in values_list for command in values['attachment_ids']): + # link back attachments to records, to filter out attachments linked to + # the same records as the message (considered as ok if message is ok) + # and check rights on other documents + attachments_tocheck = self.env['ir.attachment'] + doc_to_attachment_ids = defaultdict(set) + if all(isinstance(command, int) or command[0] in (4, 6) + for values in values_list + for command in values['attachment_ids']): for values in values_list: + message_attachment_ids = set() for command in values['attachment_ids']: if isinstance(command, int): - check_attachment_access += [command] + message_attachment_ids.add(command) elif command[0] == 6: - check_attachment_access += command[2] + message_attachment_ids |= set(command[2]) else: # command[0] == 4: - check_attachment_access += [command[1]] + message_attachment_ids.add(command[1]) + if message_attachment_ids: + key = (values.get('model'), values.get('res_id')) + doc_to_attachment_ids[key] |= message_attachment_ids + + attachment_ids_all = { + attachment_id + for doc_attachment_ids in doc_to_attachment_ids + for attachment_id in doc_attachment_ids + } + AttachmentSudo = self.env['ir.attachment'].sudo().with_prefetch(list(attachment_ids_all)) + for (model, res_id), doc_attachment_ids in doc_to_attachment_ids.items(): + # check only attachments belonging to another model, access already + # checked on message for other attachments + attachments_tocheck += AttachmentSudo.browse(doc_attachment_ids).filtered( + lambda att: att.res_model != model or att.res_id != res_id + ).sudo(False) else: - check_attachment_access = messages.mapped('attachment_ids').ids # fallback on read if any unknow command - if check_attachment_access: - self.env['ir.attachment'].browse(check_attachment_access).check(mode='read') + attachments_tocheck = messages.attachment_ids # fallback on read if any unknown command + if attachments_tocheck: + attachments_tocheck.check('read') for message, values, tracking_values_cmd in zip(messages, values_list, tracking_values_list): if tracking_values_cmd: