Problem/Motivation

PaymentAccessControlHandler::checkAccess() throws a TypeError when checking access for the capture or refund operations:

TypeError: Drupal\Core\Access\AccessResult::allowedIf(): Argument #1 ($condition)
must be of type bool, Drupal\Core\Access\AccessResultForbidden given, called in
.../payment/src/Entity/Payment/PaymentAccessControlHandler.php on line 35
in Drupal\Core\Access\AccessResult::allowedIf() (line 82 of
core/lib/Drupal/Core/Access/AccessResult.php).

In PaymentAccessControlHandler::checkAccess(), the capture and refund branches wrap the results of PaymentMethodInterface::capturePaymentAccess() and PaymentMethodInterface::refundPaymentAccess() in AccessResult::allowedIf().

For example:

return AccessResult::allowedIf($payment_method instanceof PaymentMethodCapturePaymentInterface)
  ->andIf(AccessResult::allowedIf($payment_method->capturePaymentAccess($account)))
  ->andIf($this->checkAccessPermission($payment, $operation, $account));

However, according to the interfaces' docblocks (PaymentMethodCapturePaymentInterface::capturePaymentAccess() and PaymentMethodRefundPaymentInterface::refundPaymentAccess()) and the default implementations in PaymentMethodBase, both methods return an AccessResultInterface object rather than a bool.

AccessResult::allowedIf() requires a boolean $condition. Passing an AccessResultInterface to it therefore results in a TypeError.

This affects sites using a payment method that implements PaymentMethodCapturePaymentInterface or PaymentMethodRefundPaymentInterface when a capture or refund access check is performed, for example:

$payment->access('capture');

Steps to reproduce

  1. Enable a payment method plugin implementing PaymentMethodCapturePaymentInterface or PaymentMethodRefundPaymentInterface.
  2. Create or load a payment entity using that payment method.
  3. Call $payment->access('capture') or $payment->access('refund').
  4. Observe the TypeError caused by passing an AccessResultInterface object to AccessResult::allowedIf().

Proposed resolution

Since capturePaymentAccess() and refundPaymentAccess() already return an AccessResultInterface, pass their results directly to andIf() instead of wrapping them in AccessResult::allowedIf().

Remaining tasks

  • Review and test the proposed patch.
  • Add automated test coverage for capture access checks.
  • Add automated test coverage for refund access checks.
  • Verify the fix against the supported Drupal core versions.
  • Verify whether the issue also exists in 2.x-dev.

User interface changes

None.

API changes

None. The existing PaymentMethodCapturePaymentInterface::capturePaymentAccess() and PaymentMethodRefundPaymentInterface::refundPaymentAccess() return types and behavior are preserved.

Data model changes

None.

Comments

carolpettirossi created an issue. See original summary.

carolpettirossi’s picture

Issue summary: View changes
carolpettirossi’s picture

Issue summary: View changes