From 7f6f6a545c3e913ca4f5c70401e187a12bb980e6 Mon Sep 17 00:00:00 2001 From: Valentin Chevalier Date: Fri, 29 Apr 2022 15:53:06 +0000 Subject: [PATCH] [FIX] payment,sale: fix access error when using the capture or void buttons Before this commit, users may saw the capture and void buttons but couldn't use them. As capture or void actions need to access acquirer-related fields, these actions are now sudoed to allow any users that see the button to use it. The access rules and rights are also checked before executing the action to avoid abusive RPC calls. task-2785144 closes odoo/odoo#91923 X-original-commit: 2e351d6ff9b7f72b486e83c827ff6e15a05c3331 Signed-off-by: Valentin Chevalier Signed-off-by: Antoine Vandevenne (anv) --- addons/payment/models/account_move.py | 12 ++++++++++-- addons/payment/models/payment_transaction.py | 8 ++++++-- addons/payment/utils.py | 13 +++++++++++++ addons/sale/models/sale_order.py | 12 ++++++++++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/addons/payment/models/account_move.py b/addons/payment/models/account_move.py index 85c46e9f181..4809263217d 100644 --- a/addons/payment/models/account_move.py +++ b/addons/payment/models/account_move.py @@ -2,6 +2,8 @@ from odoo import api, fields, models +from odoo.addons.payment import utils as payment_utils + class AccountMove(models.Model): _inherit = 'account.move' @@ -26,10 +28,16 @@ class AccountMove(models.Model): return self.with_context(active_test=False).transaction_ids._get_last() def payment_action_capture(self): - self.authorized_transaction_ids.action_capture() + """ Capture all transactions linked to this invoice. """ + payment_utils.check_rights_on_recordset(self) + # In sudo mode because we need to be able to read on acquirer fields. + self.authorized_transaction_ids.sudo().action_capture() def payment_action_void(self): - self.authorized_transaction_ids.action_void() + """ Void all transactions linked to this invoice. """ + payment_utils.check_rights_on_recordset(self) + # In sudo mode because we need to be able to read on acquirer fields. + self.authorized_transaction_ids.sudo().action_void() def action_view_payment_transactions(self): action = self.env['ir.actions.act_window']._for_xml_id('payment.action_payment_transaction') diff --git a/addons/payment/models/payment_transaction.py b/addons/payment/models/payment_transaction.py index 593352a59b9..44d96686073 100644 --- a/addons/payment/models/payment_transaction.py +++ b/addons/payment/models/payment_transaction.py @@ -298,16 +298,20 @@ class PaymentTransaction(models.Model): if any(tx.state != 'authorized' for tx in self): raise ValidationError(_("Only authorized transactions can be captured.")) + payment_utils.check_rights_on_recordset(self) for tx in self: - tx._send_capture_request() + # In sudo mode because we need to be able to read on acquirer fields. + tx.sudo()._send_capture_request() def action_void(self): """ Check the state of the transaction and request to have them voided. """ if any(tx.state != 'authorized' for tx in self): raise ValidationError(_("Only authorized transactions can be voided.")) + payment_utils.check_rights_on_recordset(self) for tx in self: - tx._send_void_request() + # In sudo mode because we need to be able to read on acquirer fields. + tx.sudo()._send_void_request() def action_refund(self, amount_to_refund=None): """ Check the state of the transactions and request their refund. diff --git a/addons/payment/utils.py b/addons/payment/utils.py index 2dcb6392597..a812ca4c7f1 100644 --- a/addons/payment/utils.py +++ b/addons/payment/utils.py @@ -156,3 +156,16 @@ def split_partner_name(partner_name): def get_customer_ip_address(): return request and request.httprequest.remote_addr or '' + + +def check_rights_on_recordset(recordset): + """ Ensure that the user has the rights to write on the record. + + Call this method to check the access rules and rights before doing any operation that is + callable by RPC and that requires to be executed in sudo mode. + + :param recordset: The recordset for which the rights should be checked. + :return: None + """ + recordset.check_access_rights('write') + recordset.check_access_rule('write') diff --git a/addons/sale/models/sale_order.py b/addons/sale/models/sale_order.py index 96e24eae6a0..736acdb9464 100644 --- a/addons/sale/models/sale_order.py +++ b/addons/sale/models/sale_order.py @@ -12,6 +12,8 @@ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.osv import expression from odoo.tools import float_is_zero, format_amount, format_date, html_keep_url, is_html_empty +from odoo.addons.payment import utils as payment_utils + READONLY_FIELD_STATES = { state: [('readonly', True)] for state in {'sale', 'done', 'cancel'} @@ -1200,10 +1202,16 @@ class SaleOrder(models.Model): line.qty_to_invoice = 0 def payment_action_capture(self): - self.authorized_transaction_ids.action_capture() + """ Capture all transactions linked to this sale order. """ + payment_utils.check_rights_on_recordset(self) + # In sudo mode because we need to be able to read on acquirer fields. + self.authorized_transaction_ids.sudo().action_capture() def payment_action_void(self): - self.authorized_transaction_ids.action_void() + """ Void all transactions linked to this sale order. """ + payment_utils.check_rights_on_recordset(self) + # In sudo mode because we need to be able to read on acquirer fields. + self.authorized_transaction_ids.sudo().action_void() def get_portal_last_transaction(self): self.ensure_one()