[IMP] payment,*: restrict access to payment.transaction records
Since 15.0, it is no longer possible to have partnerless transactions. Nonetheless, migrated databases could contain transactions without a `partner_id` set. This commit cleans existing rules to make those transactions are not accessible to unwanted users. We take the opportunity to clean the security of payment.transaction records globally. Now only admin and accounting users have access to payment.transaction records. The code of different applications has been adapted accordingly. Task - 3102824 closes odoo/odoo#113515 Related: odoo/upgrade#4564 Related: odoo/enterprise#40939 Signed-off-by: Masereel Pierre <pim@odoo.com>
This commit is contained in:
committed by
Horacio Tellez (hote)
parent
d471391dd8
commit
2b1e2abda3
@@ -15,7 +15,8 @@ class AccountMove(models.Model):
|
||||
readonly=True, copy=False)
|
||||
authorized_transaction_ids = fields.Many2many(
|
||||
string="Authorized Transactions", comodel_name='payment.transaction',
|
||||
compute='_compute_authorized_transaction_ids', readonly=True, copy=False)
|
||||
compute='_compute_authorized_transaction_ids', readonly=True, copy=False,
|
||||
compute_sudo=True)
|
||||
amount_paid = fields.Monetary(
|
||||
string="Amount paid",
|
||||
compute='_compute_amount_paid'
|
||||
@@ -64,7 +65,7 @@ class AccountMove(models.Model):
|
||||
|
||||
def get_portal_last_transaction(self):
|
||||
self.ensure_one()
|
||||
return self.with_context(active_test=False).transaction_ids._get_last()
|
||||
return self.with_context(active_test=False).transaction_ids.sudo()._get_last()
|
||||
|
||||
def payment_action_capture(self):
|
||||
""" Capture all transactions linked to this invoice. """
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
payment_link_wizard,payment.link.wizard,payment.model_payment_link_wizard,account.group_account_invoice,1,1,1,0
|
||||
payment_refund_wizard,payment.refund.wizard,model_payment_refund_wizard,account.group_account_invoice,1,1,1,0
|
||||
payment_transaction_user,payment.transaction.user,model_payment_transaction,account.group_account_invoice,1,1,1,0
|
||||
|
||||
|
@@ -1,16 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
|
||||
<!-- Transactions -->
|
||||
|
||||
<record id="payment_transaction_billing_rule" model="ir.rule">
|
||||
<field name="name">Access every transaction</field>
|
||||
<field name="model_id" ref="payment.model_payment_transaction"/>
|
||||
<!-- Reset the domain defined by payment.transaction_user_rule -->
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
<field name="groups" eval="[(4, ref('account.group_account_invoice'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Tokens -->
|
||||
|
||||
<record id="payment_token_billing_rule" model="ir.rule">
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
</td>
|
||||
</xpath>
|
||||
<xpath expr="//t[@name='invoice_status_posted']/span[1]" position="before">
|
||||
<t t-set="last_tx" t-value="invoice.get_portal_last_transaction()"/>
|
||||
<t t-set="tx_sudo" t-value="invoice.get_portal_last_transaction()"/>
|
||||
</xpath>
|
||||
<xpath expr="//span[@name='invoice_status_waiting_for_payment']" position="before">
|
||||
<span t-elif="last_tx.state == 'authorized'"
|
||||
<span t-elif="tx_sudo.state == 'authorized'"
|
||||
class="badge rounded-pill text-bg-primary">
|
||||
<i class="fa fa-fw fa-check"/>
|
||||
<span class="d-none d-md-inline"> Authorized</span>
|
||||
</span>
|
||||
<span t-elif="last_tx.state == 'pending' and last_tx.provider_code not in ('none', 'custom')"
|
||||
<span t-elif="tx_sudo.state == 'pending' and tx_sudo.provider_code not in ('none', 'custom')"
|
||||
class="badge rounded-pill text-bg-warning">
|
||||
<span class="d-none d-md-inline"> Pending</span>
|
||||
</span>
|
||||
<span t-elif="last_tx.state == 'done'" class="badge rounded-pill text-bg-success">
|
||||
<span t-elif="tx_sudo.state == 'done'" class="badge rounded-pill text-bg-success">
|
||||
<i class="fa fa-fw fa-check"></i>
|
||||
<span class="d-none d-md-inline"> Paid</span>
|
||||
</span>
|
||||
@@ -100,9 +100,9 @@
|
||||
</t>
|
||||
<t t-elif="error == 'pay_invoice_tx_fail'">
|
||||
There was an error processing your payment: transaction failed.<br />
|
||||
<t t-set="tx_id" t-value="invoice.get_portal_last_transaction()"/>
|
||||
<t t-if="tx_id and tx_id.state_message">
|
||||
<t t-out="tx_id.state_message"/>
|
||||
<t t-set="tx_sudo" t-value="invoice.get_portal_last_transaction()"/>
|
||||
<t t-if="tx_sudo and tx_sudo.state_message">
|
||||
<t t-out="tx_sudo.state_message"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-elif="error == 'pay_invoice_tx_token'">
|
||||
@@ -115,9 +115,9 @@
|
||||
inherit_id="account.portal_invoice_success">
|
||||
<xpath expr="//a[hasclass('close')]" position="after">
|
||||
<t t-if="success == 'pay_invoice'">
|
||||
<t t-set="payment_tx_id" t-value="invoice.get_portal_last_transaction()"/>
|
||||
<span t-if='payment_tx_id.provider_id.sudo().done_msg' t-out="payment_tx_id.provider_id.sudo().done_msg"/>
|
||||
<div t-if="payment_tx_id.provider_id.sudo().pending_msg and payment_tx_id.provider_code == 'custom' and invoice.ref">
|
||||
<t t-set="tx_sudo" t-value="invoice.get_portal_last_transaction()"/>
|
||||
<span t-if="tx_sudo.provider_id.sudo().done_msg" t-out="tx_sudo.provider_id.sudo().done_msg"/>
|
||||
<div t-if="tx_sudo.provider_id.sudo().pending_msg and tx_sudo.provider_code == 'custom' and invoice.ref">
|
||||
<b>Communication: </b><span t-out='invoice.ref'/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -7,6 +7,4 @@ payment_method_all,payment.method.all,model_payment_method,,1,0,0,0
|
||||
payment_method_system,payment.method.system,model_payment_method,base.group_system,1,1,1,1
|
||||
payment_token_all,payment.token.all,model_payment_token,,1,0,0,0
|
||||
payment_token_system,payment.token.system,model_payment_token,base.group_system,1,1,1,1
|
||||
payment_transaction_all,payment.transaction.all,model_payment_transaction,,1,0,0,0
|
||||
payment_transaction_system,payment.transaction.system,model_payment_transaction,base.group_system,1,1,1,1
|
||||
payment_transaction_user,payment.transaction.user,model_payment_transaction,base.group_user,1,1,1,0
|
||||
|
||||
|
@@ -11,13 +11,6 @@
|
||||
|
||||
<!-- Transactions -->
|
||||
|
||||
<record id="payment_transaction_user_rule" model="ir.rule">
|
||||
<field name="name">Access own transactions only</field>
|
||||
<field name="model_id" ref="payment.model_payment_transaction"/>
|
||||
<field name="domain_force">['|', ('partner_id', '=', False), ('partner_id', '=', user.partner_id.id)]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_user')), (4, ref('base.group_portal'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="transaction_company_rule" model="ir.rule">
|
||||
<field name="name">Access transactions in own companies only</field>
|
||||
<field name="model_id" ref="payment.model_payment_transaction"/>
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
<p style="margin: 0px; padding: 0px; font-size: 12px;">
|
||||
Hello,
|
||||
<br/><br/>
|
||||
<t t-set="transaction" t-value="object.get_portal_last_transaction()"/>
|
||||
<t t-set="tx_sudo" t-value="object.get_portal_last_transaction()"/>
|
||||
Your order <span style="font-weight:bold;" t-out="object.name or ''">S00049</span> amounting in <span style="font-weight:bold;" t-out="format_amount(object.amount_total, object.currency_id) or ''">$ 10.00</span>
|
||||
<t t-if="object.state == 'sale' or (transaction and transaction.state in ('done', 'authorized'))">
|
||||
<t t-if="object.state == 'sale' or (tx_sudo and tx_sudo.state in ('done', 'authorized'))">
|
||||
has been confirmed.<br/>
|
||||
Thank you for your trust!
|
||||
</t>
|
||||
<t t-elif="transaction and transaction.state == 'pending'">
|
||||
<t t-elif="tx_sudo and tx_sudo.state == 'pending'">
|
||||
is pending. It will be confirmed when the payment is received.
|
||||
<t t-if="object.reference">
|
||||
Your payment reference is <span style="font-weight:bold;" t-out="object.reference or ''"></span>.
|
||||
@@ -187,13 +187,13 @@
|
||||
<tr>
|
||||
<td>
|
||||
<span style="font-weight:bold;">Payment Method:</span>
|
||||
<t t-if="transaction.token_id">
|
||||
<t t-out="transaction.token_id.display_name or ''"></t>
|
||||
<t t-if="tx_sudo.token_id">
|
||||
<t t-out="tx_sudo.token_id.display_name or ''"></t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-out="transaction.provider_id.sudo().name or ''"></t>
|
||||
<t t-out="tx_sudo.provider_id.sudo().name or ''"></t>
|
||||
</t>
|
||||
(<t t-out="format_amount(transaction.amount, object.currency_id) or ''">$ 10.00</t>)
|
||||
(<t t-out="format_amount(tx_sudo.amount, object.currency_id) or ''">$ 10.00</t>)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -244,7 +244,8 @@ class SaleOrder(models.Model):
|
||||
comodel_name='payment.transaction',
|
||||
string="Authorized Transactions",
|
||||
compute='_compute_authorized_transaction_ids',
|
||||
copy=False)
|
||||
copy=False,
|
||||
compute_sudo=True)
|
||||
|
||||
# UTMs - enforcing the fact that we want to 'set null' when relation is unlinked
|
||||
campaign_id = fields.Many2one(ondelete='set null')
|
||||
@@ -1405,7 +1406,7 @@ class SaleOrder(models.Model):
|
||||
|
||||
def get_portal_last_transaction(self):
|
||||
self.ensure_one()
|
||||
return self.transaction_ids._get_last()
|
||||
return self.transaction_ids.sudo()._get_last()
|
||||
|
||||
def _get_order_lines_to_report(self):
|
||||
down_payment_lines = self.order_line.filtered(lambda line:
|
||||
|
||||
@@ -437,7 +437,7 @@
|
||||
<ul class="list-group mb-4">
|
||||
<t t-foreach="invoices" t-as="i">
|
||||
<t t-set="report_url" t-value="i.get_portal_url(report_type='pdf', download=True)"/>
|
||||
<t t-set="authorized_tx_ids" t-value="i.transaction_ids.filtered(lambda tx: tx.state == 'authorized')"/>
|
||||
<t t-set="authorized_tx_ids" t-value="i.authorized_transaction_ids"/>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between">
|
||||
<div>
|
||||
<a t-att-href="report_url">
|
||||
|
||||
@@ -18,7 +18,7 @@ class WebsiteSaleDelivery(WebsiteSale):
|
||||
order.access_point_address = {}
|
||||
carrier_id = int(post['carrier_id'])
|
||||
if order and carrier_id != order.carrier_id.id:
|
||||
if any(tx.state not in ('cancel', 'error', 'draft') for tx in order.transaction_ids):
|
||||
if any(tx.sudo().state not in ('cancel', 'error', 'draft') for tx in order.transaction_ids):
|
||||
raise UserError(_('It seems that there is already a transaction for your order, you can not change the delivery method anymore'))
|
||||
order._check_carrier_quotation(force_carrier_id=carrier_id)
|
||||
return self._update_website_sale_delivery_return(order, **post)
|
||||
|
||||
@@ -1606,21 +1606,21 @@ class WebsiteSale(http.Controller):
|
||||
order = request.env['sale.order'].sudo().browse(sale_order_id)
|
||||
assert order.id == request.session.get('sale_last_order_id')
|
||||
|
||||
tx = order.get_portal_last_transaction() if order else order.env['payment.transaction']
|
||||
tx_sudo = order.get_portal_last_transaction() if order else order.env['payment.transaction']
|
||||
|
||||
if not order or (order.amount_total and not tx):
|
||||
if not order or (order.amount_total and not tx_sudo):
|
||||
return request.redirect('/shop')
|
||||
|
||||
if order and not order.amount_total and not tx:
|
||||
if order and not order.amount_total and not tx_sudo:
|
||||
order.with_context(send_email=True).action_confirm()
|
||||
return request.redirect(order.get_portal_url())
|
||||
|
||||
# clean context and session, then redirect to the confirmation page
|
||||
request.website.sale_reset()
|
||||
if tx and tx.state == 'draft':
|
||||
if tx_sudo and tx_sudo.state == 'draft':
|
||||
return request.redirect('/shop')
|
||||
|
||||
PaymentPostProcessing.remove_transactions(tx)
|
||||
PaymentPostProcessing.remove_transactions(tx_sudo)
|
||||
return request.redirect('/shop/confirmation')
|
||||
|
||||
@http.route(['/shop/confirmation'], type='http', auth="public", website=True, sitemap=False)
|
||||
|
||||
@@ -538,7 +538,7 @@ class SaleOrder(models.Model):
|
||||
return self.filtered(
|
||||
lambda abandoned_sale_order:
|
||||
abandoned_sale_order.partner_id.email
|
||||
and not any(transaction.state == 'error' for transaction in abandoned_sale_order.transaction_ids)
|
||||
and not any(transaction.sudo().state == 'error' for transaction in abandoned_sale_order.transaction_ids)
|
||||
and any(not float_is_zero(line.price_unit, precision_rounding=line.currency_id.rounding) for line in abandoned_sale_order.order_line)
|
||||
and not has_later_sale_order.get(abandoned_sale_order.partner_id, False)
|
||||
)
|
||||
|
||||
@@ -2155,8 +2155,8 @@
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl">
|
||||
<div class="oe_cart">
|
||||
<t t-set="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
|
||||
<t t-if="payment_tx_id.state == 'done'">
|
||||
<t t-set="tx_sudo" t-value="order.get_portal_last_transaction()"/>
|
||||
<t t-if="tx_sudo.state == 'done'">
|
||||
<div class="thanks_msg">
|
||||
<h2>Thank you for your order.
|
||||
<a role="button" class="btn btn-primary d-none d-md-inline-block" href="/shop/print" target="_blank" aria-label="Print" title="Print"><i class="fa fa-print"></i> Print</a>
|
||||
@@ -2173,18 +2173,18 @@
|
||||
<h3 class="text-start mt-3">
|
||||
<strong>Payment Information:</strong>
|
||||
</h3>
|
||||
<t t-set="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
|
||||
<t t-set="tx_sudo" t-value="order.get_portal_last_transaction()"/>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<t t-esc="payment_tx_id.provider_id.display_as or payment_tx_id.provider_id.name" />
|
||||
<t t-esc="tx_sudo.provider_id.display_as or tx_sudo.provider_id.name" />
|
||||
</td>
|
||||
<td class="text-end" width="100">
|
||||
<strong>Total:</strong>
|
||||
</td>
|
||||
<td class="text-end" width="100">
|
||||
<strong t-field="payment_tx_id.amount" t-options="{'widget': 'monetary', 'display_currency': order.currency_id}" />
|
||||
<strong t-field="tx_sudo.amount" t-options="{'widget': 'monetary', 'display_currency': order.currency_id}" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -2265,45 +2265,45 @@
|
||||
|
||||
<template id="payment_confirmation_status">
|
||||
<div class="oe_website_sale_tx_status mt-3" t-att-data-order-id="order.id" t-att-data-order-tracking-info="json.dumps(order_tracking_info)">
|
||||
<t t-set="payment_tx_id" t-value="order.get_portal_last_transaction()"/>
|
||||
<t t-set="tx_sudo" t-value="order.get_portal_last_transaction()"/>
|
||||
<div t-attf-class="card #{
|
||||
(payment_tx_id.state == 'pending' and 'bg-info') or
|
||||
(payment_tx_id.state == 'done' and order.amount_total == payment_tx_id.amount and 'alert-success') or
|
||||
(payment_tx_id.state == 'done' and order.amount_total != payment_tx_id.amount and 'bg-warning') or
|
||||
(payment_tx_id.state == 'authorized' and 'alert-success') or
|
||||
(tx_sudo.state == 'pending' and 'bg-info') or
|
||||
(tx_sudo.state == 'done' and order.amount_total == tx_sudo.amount and 'alert-success') or
|
||||
(tx_sudo.state == 'done' and order.amount_total != tx_sudo.amount and 'bg-warning') or
|
||||
(tx_sudo.state == 'authorized' and 'alert-success') or
|
||||
'bg-danger'}">
|
||||
<div class="card-header">
|
||||
<a role="button" groups="base.group_system" class="btn btn-sm btn-link text-white float-end" target="_blank" aria-label="Edit" title="Edit"
|
||||
t-att-href="'/web#model=%s&id=%s&action=%s&view_type=form' % ('payment.provider', payment_tx_id.provider_id.id, 'payment.action_payment_provider')">
|
||||
t-att-href="'/web#model=%s&id=%s&action=%s&view_type=form' % ('payment.provider', tx_sudo.provider_id.id, 'payment.action_payment_provider')">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<t t-if="payment_tx_id.state == 'pending'">
|
||||
<t t-out="payment_tx_id.provider_id.sudo().pending_msg"/>
|
||||
<t t-if="tx_sudo.state == 'pending'">
|
||||
<t t-out="tx_sudo.provider_id.sudo().pending_msg"/>
|
||||
</t>
|
||||
<t t-if="payment_tx_id.state == 'done'">
|
||||
<span t-if='payment_tx_id.provider_id.sudo().done_msg' t-out="payment_tx_id.provider_id.sudo().done_msg"/>
|
||||
<t t-if="tx_sudo.state == 'done'">
|
||||
<span t-if='tx_sudo.provider_id.sudo().done_msg' t-out="tx_sudo.provider_id.sudo().done_msg"/>
|
||||
</t>
|
||||
<t t-if="payment_tx_id.state == 'done' and order.amount_total != payment_tx_id.amount">
|
||||
<t t-if="tx_sudo.state == 'done' and order.amount_total != tx_sudo.amount">
|
||||
<span>Unfortunately your order can not be confirmed as the amount of your payment does not match the amount of your cart.
|
||||
Please contact the responsible of the shop for more information.</span>
|
||||
</t>
|
||||
<t t-if="payment_tx_id.state == 'cancel'">
|
||||
<t t-out="payment_tx_id.provider_id.sudo().cancel_msg"/>
|
||||
<t t-if="tx_sudo.state == 'cancel'">
|
||||
<t t-out="tx_sudo.provider_id.sudo().cancel_msg"/>
|
||||
</t>
|
||||
<t t-if="payment_tx_id.state == 'authorized'">
|
||||
<t t-if="payment_tx_id.provider_id.sudo().auth_msg" t-out="payment_tx_id.provider_id.sudo().auth_msg"/>
|
||||
<t t-if="tx_sudo.state == 'authorized'">
|
||||
<t t-if="tx_sudo.provider_id.sudo().auth_msg" t-out="tx_sudo.provider_id.sudo().auth_msg"/>
|
||||
<span t-else="">Your payment has been authorized.</span>
|
||||
</t>
|
||||
<t t-if="payment_tx_id.state == 'error'">
|
||||
<span t-esc="payment_tx_id.state_message"/>
|
||||
<t t-if="tx_sudo.state == 'error'">
|
||||
<span t-esc="tx_sudo.state_message"/>
|
||||
</t>
|
||||
</div>
|
||||
<t t-if="payment_tx_id.provider_code == 'custom'">
|
||||
<t t-if="tx_sudo.provider_code == 'custom'">
|
||||
<div t-if="order.reference" class="card-body">
|
||||
<b>Communication: </b><span t-esc='order.reference'/>
|
||||
</div>
|
||||
<div t-if="payment_tx_id.provider_id.sudo().qr_code">
|
||||
<t t-set="qr_code" t-value="payment_tx_id.company_id.partner_id.bank_ids[:1].build_qr_code_base64(order.amount_total,payment_tx_id.reference, None, payment_tx_id.currency_id, payment_tx_id.partner_id)"/>
|
||||
<div t-if="tx_sudo.provider_id.sudo().qr_code">
|
||||
<t t-set="qr_code" t-value="tx_sudo.company_id.partner_id.bank_ids[:1].build_qr_code_base64(order.amount_total,tx_sudo.reference, None, tx_sudo.currency_id, tx_sudo.partner_id)"/>
|
||||
<div class="card-body" t-if="qr_code">
|
||||
<h3>Or scan me with your banking app.</h3>
|
||||
<img class="border border-dark rounded" t-att-src="qr_code"/>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<template id="payment_confirmation_status" inherit_id="website_sale.payment_confirmation_status">
|
||||
<xpath expr="(//div[hasclass('card-body')])[1]" position="replace">
|
||||
<t t-if="payment_tx_id.provider_id.custom_mode == 'onsite'">
|
||||
<t t-if="tx_sudo.provider_id.custom_mode == 'onsite'">
|
||||
<div class="card-body">
|
||||
<div class="o_header_carrier_message">
|
||||
<b t-out="order.carrier_id.name"/><span class="text-muted"> (On site picking)</span>
|
||||
|
||||
Reference in New Issue
Block a user