[FIX] account_edi*: Fix access rights for account.edi.document.attachment_id field
An attachment has complex access rights. If there is no res_model/res_id, the access rights are admin (not exactly but let's say that).
When an EDI like Facturx/E-FFF generates an attachment not linked to any model, you don't have access to it except if you are admin.
However, here we have a security issue since everyone is able to write any 'id' on the 'attachment_id' field.
If you do that using Facturx, knowing this EDI will embed its attachment inside the invoice PDF report in sudo mode, you have now a way to extract any attachment from the database including the ones you shouldn't have access to.
Furthermore, a different api introduced by OWL makes the form view of account.edi.document popping from the one2many inside the invoice form.
Instead of "options={'no_open': '1'}", the new api is now to put directly "no_open='1'" on the root node.
If you combine both issues above, you currently have a way to extract any 'attachment_id' from the database and odoo is kind enough to give you the form view to do it.
closes odoo/odoo#111210
Solution: "account.edi.document.attachment_id" is now accessible to the admin only.
X-original-commit: 44a4cdb3944a4b722dcfbca5e2947a4372b8501d
Signed-off-by: Olivier Colson (oco) <oco@odoo.com>
This commit is contained in:
@@ -20,7 +20,11 @@ class AccountEdiDocument(models.Model):
|
||||
# == Stored fields ==
|
||||
move_id = fields.Many2one('account.move', required=True, ondelete='cascade')
|
||||
edi_format_id = fields.Many2one('account.edi.format', required=True)
|
||||
attachment_id = fields.Many2one('ir.attachment', help='The file generated by edi_format_id when the invoice is posted (and this document is processed).')
|
||||
attachment_id = fields.Many2one(
|
||||
comodel_name='ir.attachment',
|
||||
groups='base.group_system',
|
||||
help="The file generated by edi_format_id when the invoice is posted (and this document is processed).",
|
||||
)
|
||||
state = fields.Selection([('to_send', 'To Send'), ('sent', 'Sent'), ('to_cancel', 'To Cancel'), ('cancelled', 'Cancelled')])
|
||||
error = fields.Html(help='The text of the last error that happened during Electronic Invoice operation.')
|
||||
blocking_level = fields.Selection(
|
||||
@@ -116,8 +120,8 @@ class AccountEdiDocument(models.Model):
|
||||
move = document.move_id
|
||||
move_result = edi_result.get(move, {})
|
||||
if move_result.get('attachment'):
|
||||
old_attachment = document.attachment_id
|
||||
document.attachment_id = move_result['attachment']
|
||||
old_attachment = document.sudo().attachment_id
|
||||
document.sudo().attachment_id = move_result['attachment']
|
||||
if not old_attachment.res_model or not old_attachment.res_id:
|
||||
attachments_to_unlink |= old_attachment
|
||||
if move_result.get('success') is True:
|
||||
@@ -144,7 +148,7 @@ class AccountEdiDocument(models.Model):
|
||||
move_result = edi_result.get(move, {})
|
||||
if move_result.get('success') is True:
|
||||
old_attachment = document.sudo().attachment_id
|
||||
document.write({
|
||||
document.sudo().write({
|
||||
'state': 'cancelled',
|
||||
'error': False,
|
||||
'attachment_id': False,
|
||||
@@ -218,7 +222,7 @@ class AccountEdiDocument(models.Model):
|
||||
for job in jobs_to_process:
|
||||
documents = job['documents']
|
||||
move_to_lock = documents.move_id
|
||||
attachments_potential_unlink = documents.attachment_id.filtered(lambda a: not a.res_model and not a.res_id)
|
||||
attachments_potential_unlink = documents.sudo().attachment_id.filtered(lambda a: not a.res_model and not a.res_id)
|
||||
try:
|
||||
with self.env.cr.savepoint(flush=False):
|
||||
self._cr.execute('SELECT * FROM account_edi_document WHERE id IN %s FOR UPDATE NOWAIT', [tuple(documents.ids)])
|
||||
|
||||
@@ -36,7 +36,7 @@ class IrActionsReport(models.Model):
|
||||
# The attachements on the edi documents are only system readable
|
||||
# because they don't have res_id and res_model, here we are sure that
|
||||
# the user has access to the invoice and edi document
|
||||
edi_document.edi_format_id._prepare_invoice_report(writer, edi_document.sudo())
|
||||
edi_document.edi_format_id._prepare_invoice_report(writer, edi_document)
|
||||
|
||||
# Replace the current content.
|
||||
pdf_stream.close()
|
||||
|
||||
@@ -102,8 +102,8 @@
|
||||
string="EDI Documents"
|
||||
groups="base.group_no_one"
|
||||
attrs="{'invisible': [('edi_document_ids', '=', [])]}">
|
||||
<field name="edi_document_ids" options="{'no_open': '1'}">
|
||||
<tree create="false" delete="false" edit="false" decoration-danger="error">
|
||||
<field name="edi_document_ids">
|
||||
<tree create="false" delete="false" edit="false" decoration-danger="error" no_open="1">
|
||||
<field name="name"/>
|
||||
<field name="edi_format_name"/>
|
||||
<field name="state"/>
|
||||
|
||||
@@ -151,10 +151,11 @@ class AccountEdiFormat(models.Model):
|
||||
self.ensure_one()
|
||||
if self.code != 'facturx_1_0_05':
|
||||
return super()._prepare_invoice_report(pdf_writer, edi_document)
|
||||
if not edi_document.attachment_id:
|
||||
attachment = edi_document.sudo().attachment_id
|
||||
if not attachment:
|
||||
return
|
||||
|
||||
pdf_writer.embed_odoo_attachment(edi_document.attachment_id, subtype='text/xml')
|
||||
pdf_writer.embed_odoo_attachment(attachment, subtype='text/xml')
|
||||
if not pdf_writer.is_pdfa:
|
||||
try:
|
||||
pdf_writer.convert_to_pdfa()
|
||||
|
||||
@@ -16,7 +16,7 @@ class IrActionsReport(models.Model):
|
||||
|
||||
def _add_pdf_into_invoice_xml(self, invoice, stream_data):
|
||||
format_codes = ['ubl_bis3', 'ubl_de', 'nlcius_1', 'efff_1']
|
||||
edi_attachments = invoice.edi_document_ids.filtered(lambda d: d.edi_format_id.code in format_codes).attachment_id
|
||||
edi_attachments = invoice.edi_document_ids.filtered(lambda d: d.edi_format_id.code in format_codes).sudo().attachment_id
|
||||
for edi_attachment in edi_attachments:
|
||||
old_xml = base64.b64decode(edi_attachment.with_context(bin_size=False).datas, validate=True)
|
||||
tree = etree.fromstring(old_xml)
|
||||
@@ -45,7 +45,7 @@ class IrActionsReport(models.Model):
|
||||
anchor_index = tree.index(anchor_elements[0])
|
||||
tree.insert(anchor_index, etree.fromstring(to_inject))
|
||||
new_xml = etree.tostring(cleanup_xml_node(tree))
|
||||
edi_attachment.write({
|
||||
edi_attachment.sudo().write({
|
||||
'res_model': 'account.move',
|
||||
'res_id': invoice.id,
|
||||
'datas': base64.b64encode(new_xml),
|
||||
|
||||
@@ -817,8 +817,9 @@ class AccountEdiFormat(models.Model):
|
||||
self.ensure_one()
|
||||
if self.code != 'fattura_pa':
|
||||
return super()._prepare_invoice_report(pdf_writer, edi_document)
|
||||
if edi_document.attachment_id:
|
||||
pdf_writer.embed_odoo_attachment(edi_document.attachment_id)
|
||||
attachment = edi_document.sudo().attachment_id
|
||||
if attachment:
|
||||
pdf_writer.embed_odoo_attachment(attachment)
|
||||
|
||||
def _is_compatible_with_journal(self, journal):
|
||||
# OVERRIDE
|
||||
|
||||
Reference in New Issue
Block a user