From 89bb842246ffcac032844e2efafbaf82c3802737 Mon Sep 17 00:00:00 2001 From: Horacio Tellez Date: Fri, 12 Nov 2021 09:35:52 +0000 Subject: [PATCH] [FIX] payment: avoid a mismatch between partner's and acquirer's company In a multi-company environment, a partner of company A should not be able to make payments for company B. With this commit, if we detect a mismatch between the companies, a UserError is raised. Task - 2627751 closes odoo/odoo#91189 X-original-commit: 6064cf2e98f16b42f516dcd1b0793a62436bab33 Related: odoo/enterprise#27245 Signed-off-by: Antoine Vandevenne (anv) Signed-off-by: Horacio Tellez Perez (hote) --- addons/account_payment/controllers/portal.py | 16 ++++++++------ addons/payment/controllers/portal.py | 22 ++++++++++++++++++++ addons/payment/i18n/payment.pot | 6 ++++++ addons/sale/controllers/portal.py | 6 ++++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/addons/account_payment/controllers/portal.py b/addons/account_payment/controllers/portal.py index 3d2ad43042f..df0aae049e1 100644 --- a/addons/account_payment/controllers/portal.py +++ b/addons/account_payment/controllers/portal.py @@ -4,6 +4,7 @@ from odoo.http import request from odoo.addons.account.controllers import portal +from odoo.addons.payment.controllers.portal import PaymentPortal from odoo.addons.portal.controllers.portal import _build_url_w_params @@ -15,14 +16,17 @@ class PortalAccount(portal.PortalAccount): # We set partner_id to the partner id of the current user if logged in, otherwise we set it # to the invoice partner id. We do this to ensure that payment tokens are assigned to the # correct partner and to avoid linking tokens to the public user. - partner_id = request.env.user.partner_id.id if logged_in else invoice.partner_id.id + partner = request.env.user.partner_id if logged_in else invoice.partner_id + + # Make sure that the partner's company matches the invoice's company. + invoice_company = invoice.company_id or request.env.company + PaymentPortal.ensure_matching_companies(partner, invoice_company) + acquirers_sudo = request.env['payment.acquirer'].sudo()._get_compatible_acquirers( - invoice.company_id.id or request.env.company.id, - partner_id, - currency_id=invoice.currency_id.id, + invoice_company.id, partner.id, currency_id=invoice.currency_id.id ) # In sudo mode to read the fields of acquirers and partner (if not logged in) tokens = request.env['payment.token'].search( - [('acquirer_id', 'in', acquirers_sudo.ids), ('partner_id', '=', partner_id)] + [('acquirer_id', 'in', acquirers_sudo.ids), ('partner_id', '=', partner.id)] ) # Tokens are cleared at the end if the user is not logged in fees_by_acquirer = { acq_sudo: acq_sudo._compute_fees( @@ -36,7 +40,7 @@ class PortalAccount(portal.PortalAccount): 'show_tokenize_input': logged_in, # Prevent public partner from saving payment methods 'amount': invoice.amount_residual, 'currency': invoice.currency_id, - 'partner_id': partner_id, + 'partner_id': partner.id, 'access_token': access_token, 'transaction_route': f'/invoice/transaction/{invoice.id}/', 'landing_route': _build_url_w_params(invoice.access_url, {'access_token': access_token}) diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py index 91e7f0af70d..c2b156414a9 100644 --- a/addons/payment/controllers/portal.py +++ b/addons/payment/controllers/portal.py @@ -103,6 +103,9 @@ class PaymentPortal(portal.CustomerPortal): company = request.env['res.company'].sudo().browse(company_id) currency_id = currency_id or company.currency_id.id + # Make sure that the company passed as parameter matches the partner's company. + PaymentPortal.ensure_matching_companies(partner_sudo, company) + # Make sure that the currency exists and is active currency = request.env['res.currency'].browse(currency_id).exists() if not currency or not currency.active: @@ -429,3 +432,22 @@ class PaymentPortal(portal.CustomerPortal): return float(str_value) except (TypeError, ValueError, OverflowError): return None + + @staticmethod + def ensure_matching_companies(partner, document_company): + """ Check that the partner's company is the same as the document's company. + + If the partner company is not set, the check passes. If the companies don't match, a + `UserError` is raised. + + :param recordset partner: The partner on behalf on which the payment is made, as a + `res.partner` record. + :param recordset document_company: The company of the document being paid, as a + `res.company` record. + :return: None + :raise UserError: If the companies don't match. + """ + if partner.company_id and partner.company_id != document_company: + raise UserError( + _("Please switch to company '%s' to make this payment.", document_company.name) + ) diff --git a/addons/payment/i18n/payment.pot b/addons/payment/i18n/payment.pot index 9f3a12fbfd0..a53f2790398 100644 --- a/addons/payment/i18n/payment.pot +++ b/addons/payment/i18n/payment.pot @@ -1399,6 +1399,12 @@ msgstr "" msgid "Please set an amount smaller than %s." msgstr "" +#. module: payment +#: code:addons/payment/controllers/portal.py:0 +#, python-format +msgid "Please switch to company '%s' to make this payment." +msgstr "" + #. module: payment #: model_terms:payment.acquirer,pending_msg:payment.payment_acquirer_transfer msgid "Please use the following transfer details" diff --git a/addons/sale/controllers/portal.py b/addons/sale/controllers/portal.py index 6a778744237..92b38d3067f 100644 --- a/addons/sale/controllers/portal.py +++ b/addons/sale/controllers/portal.py @@ -152,6 +152,12 @@ class CustomerPortal(portal.CustomerPortal): # Payment values if order_sudo.has_to_be_paid(): logged_in = not request.env.user._is_public() + + # Make sure that the partner's company matches the sales order's company. + payment_portal.PaymentPortal.ensure_matching_companies( + order_sudo.partner_id, order_sudo.company_id + ) + acquirers_sudo = request.env['payment.acquirer'].sudo()._get_compatible_acquirers( order_sudo.company_id.id, order_sudo.partner_id.id,