Skip to content

Commit

Permalink
RATESWSX-292: improve request-data key safety
Browse files Browse the repository at this point in the history
  • Loading branch information
rommelfreddy committed Feb 20, 2024
1 parent 3c375c3 commit 9213230
Show file tree
Hide file tree
Showing 15 changed files with 81 additions and 39 deletions.
14 changes: 7 additions & 7 deletions src/Components/Account/Subscriber/AccountSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ public function addRatepayTemplateData(AccountEditOrderPageLoadedEvent $event):

public function onHandlePaymentMethodRouteRequest(HandlePaymentMethodRouteRequestEvent $event): void
{
if ($event->getStorefrontRequest()->request->has('ratepay')) {
if ($event->getStorefrontRequest()->request->has(RequestHelper::RATEPAY_DATA_KEY)) {
$event->getStoreApiRequest()->request->set(
'ratepay',
RequestHelper::getArrayBag($event->getStorefrontRequest(), 'ratepay')->all()
RequestHelper::RATEPAY_DATA_KEY,
RequestHelper::getArrayBag($event->getStorefrontRequest(), RequestHelper::RATEPAY_DATA_KEY)->all()
);
}
}
Expand Down Expand Up @@ -127,14 +127,14 @@ public function onPaymentOrderRouteRequest(SetPaymentOrderRouteRequestEvent $eve

// we must validate the ratepay data on our own. to prevent errors with other extensions, we will only validate ratepay-data.
$requestData = new DataBag($event->getStorefrontRequest()->request->all());
$ratepayData = $requestData->get('ratepay');
$ratepayData = RequestHelper::getRatepayData($requestData);

$validationDefinitions = $paymentHandler->getValidationDefinitions($requestData, $orderEntity);
$definition = new DataValidationDefinition();
$definition->addSub('ratepay', DataValidationHelper::addSubConstraints(new DataValidationDefinition(), $validationDefinitions));
$definition->addSub(RequestHelper::RATEPAY_DATA_KEY, DataValidationHelper::addSubConstraints(new DataValidationDefinition(), $validationDefinitions));
try {
$this->dataValidator->validate([
'ratepay' => $ratepayData->all(),
RequestHelper::RATEPAY_DATA_KEY => $ratepayData->all(),
], $definition);
} catch (ConstraintViolationException $constraintViolationException) {
throw new ForwardException('frontend.account.edit-order.page', [
Expand All @@ -148,7 +148,7 @@ public function onPaymentOrderRouteRequest(SetPaymentOrderRouteRequestEvent $eve
$orderEntity,
$paymentHandler,
new RequestDataBag([
'ratepay' => $ratepayData,
RequestHelper::RATEPAY_DATA_KEY => $ratepayData,
]),
$event->getSalesChannelContext()
));
Expand Down
3 changes: 2 additions & 1 deletion src/Components/Checkout/SalesChannel/CartOrderRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Ratepay\RpayPayments\Components\Checkout\Service\DataValidationService;
use Ratepay\RpayPayments\Components\PaymentHandler\AbstractPaymentHandler;
use Ratepay\RpayPayments\Util\RequestHelper;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\SalesChannel\AbstractCartOrderRoute;
use Shopware\Core\Checkout\Cart\SalesChannel\CartOrderRouteResponse;
Expand All @@ -39,7 +40,7 @@ public function order(Cart $cart, SalesChannelContext $context, RequestDataBag $
// this validation is optional. The validation will be also processed during handling the payment.

$paymentHandler = $context->getPaymentMethod()->getHandlerIdentifier();
if (is_subclass_of($paymentHandler, AbstractPaymentHandler::class) && $data->has('ratepay')) {
if (is_subclass_of($paymentHandler, AbstractPaymentHandler::class) && $data->has(RequestHelper::RATEPAY_DATA_KEY)) {
$this->validatorService->validatePaymentData($data, $context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
use Shopware\Core\Checkout\Payment\SalesChannel\AbstractHandlePaymentMethodRoute;
use Shopware\Core\Checkout\Payment\SalesChannel\HandlePaymentMethodRouteResponse;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;

class HandlePaymentMethodRoute extends AbstractHandlePaymentMethodRoute
Expand Down Expand Up @@ -57,7 +57,7 @@ public function load(Request $request, SalesChannelContext $context): HandlePaym
}

if ($paymentHandlerIdentifier !== null && is_subclass_of($paymentHandlerIdentifier, AbstractPaymentHandler::class)) {
$this->dataValidationService->validatePaymentData(new ParameterBag($request->request->all()), $order ?? $context);
$this->dataValidationService->validatePaymentData(new DataBag($request->request->all()), $order ?? $context);
}

return $this->innerService->load($request, $context);
Expand Down
15 changes: 9 additions & 6 deletions src/Components/Checkout/Service/DataValidationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

use Ratepay\RpayPayments\Components\PaymentHandler\AbstractPaymentHandler;
use Ratepay\RpayPayments\Util\DataValidationHelper;
use Ratepay\RpayPayments\Util\RequestHelper;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PaymentHandlerRegistry;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\Framework\Validation\DataValidationDefinition;
use Shopware\Core\Framework\Validation\DataValidator;
Expand All @@ -28,7 +30,7 @@ public function __construct(
) {
}

public function validatePaymentData(ParameterBag $parameterBag, SalesChannelContext|OrderEntity $validationScope): void
public function validatePaymentData(DataBag $parameterBag, SalesChannelContext|OrderEntity $validationScope): void
{
if ($validationScope instanceof OrderEntity) {
$paymentMethodId = $validationScope->getTransactions()->last()->getPaymentMethodId();
Expand All @@ -42,15 +44,16 @@ public function validatePaymentData(ParameterBag $parameterBag, SalesChannelCont
return;
}

$validationDefinitions = $paymentHandler->getValidationDefinitions(new RequestDataBag($parameterBag->all()), $validationScope);
/** @var DataBag $_parameterBag */
$_parameterBag = $parameterBag->get(RequestHelper::WRAPPER_KEY, $parameterBag); // paymentDetails is using for pwa request
$validationDefinitions = $paymentHandler->getValidationDefinitions(new RequestDataBag($_parameterBag->all()), $validationScope);

$definitions = new DataValidationDefinition();
DataValidationHelper::addSubConstraints($definitions, $validationDefinitions);
$dataValidationDefinition = (new DataValidationDefinition())->addSub('ratepay', $definitions);
$dataValidationDefinition = (new DataValidationDefinition())->addSub(RequestHelper::RATEPAY_DATA_KEY, $definitions);

$isPaymentDetailsWrapper = $parameterBag->has('paymentDetails');
if ($isPaymentDetailsWrapper) {
$dataValidationDefinition = (new DataValidationDefinition())->addSub('paymentDetails', $dataValidationDefinition);
if ($parameterBag->has(RequestHelper::WRAPPER_KEY)) {
$dataValidationDefinition = (new DataValidationDefinition())->addSub(RequestHelper::WRAPPER_KEY, $dataValidationDefinition);
}

$this->dataValidator->validate($parameterBag->all(), $dataValidationDefinition);
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Checkout/Service/ExtensionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public function buildPaymentDataExtension(

if ($httpRequest instanceof Request) {
// add user entered values again, so that the user have not to reenter his values
foreach (RequestHelper::getArray($httpRequest, 'ratepay') ?: [] as $key => $value) {
foreach (RequestHelper::getArray($httpRequest, RequestHelper::RATEPAY_DATA_KEY) ?: [] as $key => $value) {
if ($key === 'birthday' && is_array($value)) {
$value = (new DateTime())->setDate((int) $value['year'], (int) $value['month'], (int) $value['day']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

use Ratepay\RpayPayments\Components\Checkout\Service\DataValidationService;
use Shopware\Core\Framework\Validation\BuildValidationEvent;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\PlatformRequest;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Expand Down Expand Up @@ -45,7 +45,7 @@ public function validateOrderData(BuildValidationEvent $event): void

$salesChannelContext = $this->getSalesContextFromRequest($request);

$this->dataValidationService->validatePaymentData(new RequestDataBag($request->request->all()), $salesChannelContext);
$this->dataValidationService->validatePaymentData(new DataBag($request->request->all()), $salesChannelContext);
}

private function getSalesContextFromRequest(Request $request): SalesChannelContext
Expand Down
3 changes: 2 additions & 1 deletion src/Components/Checkout/Subscriber/UserDataSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use DateTime;
use Ratepay\RpayPayments\Components\PaymentHandler\Event\BeforePaymentEvent;
use Ratepay\RpayPayments\Util\RequestHelper;
use RuntimeException;
use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity;
use Shopware\Core\Checkout\Customer\CustomerEntity;
Expand Down Expand Up @@ -54,7 +55,7 @@ public function saveUserData(BeforePaymentEvent $event): void
throw new RuntimeException('user data can not be saved. Unknown error.');
}

$ratepayData = $dataBag->get('ratepay');
$ratepayData = RequestHelper::getRatepayData($dataBag);
if (!$ratepayData instanceof RequestDataBag) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Ratepay\RpayPayments\Components\RatepayApi\Dto\PaymentRequestData;
use Ratepay\RpayPayments\Components\RatepayApi\Event\BuildEvent;
use Ratepay\RpayPayments\Components\RatepayApi\Service\Request\PaymentRequestService;
use Ratepay\RpayPayments\Util\RequestHelper;
use Shopware\Storefront\Event\RouteRequest\OrderRouteRequestEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
Expand Down Expand Up @@ -56,7 +57,7 @@ public function onPaymentRequest(BuildEvent $buildEvent): void
/** @var Head $head */
$head = $buildEvent->getBuildData();

$ratepayData = $requestData->getRequestDataBag()->get('ratepay');
$ratepayData = RequestHelper::getRatepayData($requestData->getRequestDataBag());

if ($token = $ratepayData->get('deviceIdentToken') ?? null) {
$head->setCustomerDevice((new CustomerDevice())->setDeviceToken($token));
Expand Down Expand Up @@ -92,7 +93,7 @@ public function addValidationDefinition(ValidationDefinitionCollectEvent $event)
public function addDfpTokenToOrder(OrderExtensionDataBuilt $event): void
{
$requestDataBag = $event->getPaymentRequestData()->getRequestDataBag();
$ratepayData = $requestDataBag->get('ratepay');
$ratepayData = RequestHelper::getRatepayData($requestDataBag);

$data = $event->getData();
$data[RatepayOrderDataEntity::FIELD_ADDITIONAL_DATA]['deviceIdentToken'] = $ratepayData->get('deviceIdentToken');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use Ratepay\RpayPayments\Components\RatepayApi\Factory\ShoppingBasketFactory;
use Ratepay\RpayPayments\Util\MethodHelper;
use Ratepay\RpayPayments\Util\PaymentFirstday;
use Ratepay\RpayPayments\Util\RequestHelper;
use RuntimeException;
use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
Expand Down Expand Up @@ -57,8 +58,11 @@ public function buildPayment(BuildEvent $event): void
/** @var Payment $paymentObject */
$paymentObject = $event->getBuildData();

/** @var DataBag $requestDataBag */ // should be never null, because it is already validated
$requestDataBag = RequestHelper::getRatepayData($requestData->getRequestDataBag());

/** @var DataBag $requestedInstallment */
$requestedInstallment = $requestData->getRequestDataBag()->get('ratepay')->get('installment');
$requestedInstallment = $requestDataBag->get('installment');

$calcContext = new InstallmentCalculatorContext(
$requestData->getSalesChannelContext(),
Expand Down
9 changes: 3 additions & 6 deletions src/Components/PaymentHandler/AbstractPaymentHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use Ratepay\RpayPayments\Components\RatepayApi\Service\Request\PaymentRequestService;
use Ratepay\RpayPayments\Exception\RatepayException;
use Ratepay\RpayPayments\Util\CriteriaHelper;
use Ratepay\RpayPayments\Util\RequestHelper;
use RuntimeException;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\SynchronousPaymentHandlerInterface;
Expand Down Expand Up @@ -68,9 +69,7 @@ abstract public static function getRatepayPaymentMethodName(): string;

public function pay(SyncPaymentTransactionStruct $transaction, RequestDataBag $dataBag, SalesChannelContext $salesChannelContext): void
{
$dataBag = $dataBag->get('paymentDetails', $dataBag); // data from pwa
/** @var DataBag $ratepayData */
$ratepayData = $dataBag->get('ratepay', new DataBag([]));
$ratepayData = RequestHelper::getRatepayData($dataBag) ?: new ParameterBag();

$order = $this->getOrderWithAssociations($transaction->getOrder(), $salesChannelContext->getContext());

Expand Down Expand Up @@ -149,10 +148,8 @@ public function getValidationDefinitions(DataBag $requestDataBag, $baseData): ar
{
$validations = [];

/** @var DataBag $_requestDataBag */
$_requestDataBag = $requestDataBag->get('paymentDetails', $requestDataBag); // data from pwa
/** @var DataBag $ratepayData */
$ratepayData = $_requestDataBag->get('ratepay', new ParameterBag());
$ratepayData = RequestHelper::getRatepayData($requestDataBag) ?: new ParameterBag();

if ($baseData instanceof SalesChannelContext) {
$birthday = $baseData->getCustomer()->getBirthday();
Expand Down
2 changes: 1 addition & 1 deletion src/Components/PaymentHandler/DebitPaymentHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function getValidationDefinitions(DataBag $requestDataBag, $baseData): ar
{
return array_merge(
parent::getValidationDefinitions($requestDataBag, $baseData),
$this->getDebitConstraints($requestDataBag, $baseData)
$this->getDebitConstraints($baseData)
);
}

Expand Down
6 changes: 1 addition & 5 deletions src/Components/PaymentHandler/DebitValidationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@
use Ratepay\RpayPayments\Components\PaymentHandler\Constraint\IbanNotBlank;
use Ratepay\RpayPayments\Components\PaymentHandler\Constraint\SepaConfirmNotBlank;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\Framework\Validation\DataValidationDefinition;
use Shopware\Core\System\SalesChannel\SalesChannelContext;

trait DebitValidationTrait
{
/**
* @return array{bankData: DataValidationDefinition}
*/
public function getDebitConstraints(DataBag $requestDataBag, OrderEntity|SalesChannelContext $baseData): array
public function getDebitConstraints(SalesChannelContext|OrderEntity $baseData): array
{
$bankData = new DataValidationDefinition();
$bankData->add('accountHolder', new BankAccountHolderNotBlank(), new BankAccountHolderChoice(
Expand Down
6 changes: 4 additions & 2 deletions src/Components/PaymentHandler/InstallmentPaymentHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Ratepay\RpayPayments\Components\PaymentHandler;

use Ratepay\RpayPayments\Util\RequestHelper;
use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\Framework\Validation\DataValidationDefinition;
use Symfony\Component\Validator\Constraints\Choice;
Expand Down Expand Up @@ -58,10 +59,11 @@ public function getValidationDefinitions(DataBag $requestDataBag, $baseData): ar
);

/** @var DataBag $ratepayData */
$ratepayData = $requestDataBag->get('ratepay');
$ratepayData = RequestHelper::getRatepayData($requestDataBag);

$installmentData = $ratepayData->get('installment');
if ($installmentData->get('paymentType') && $installmentData->get('paymentType') === 'DIRECT-DEBIT') {
$validations = array_merge($validations, $this->getDebitConstraints($requestDataBag, $baseData));
$validations = array_merge($validations, $this->getDebitConstraints($baseData));
}

$validations['installment'] = $installment;
Expand Down
4 changes: 2 additions & 2 deletions src/Components/RatepayApi/Factory/CustomerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Ratepay\RpayPayments\Components\RatepayApi\Dto\AbstractRequestData;
use Ratepay\RpayPayments\Components\RatepayApi\Dto\CheckoutOperationInterface;
use Ratepay\RpayPayments\Components\RatepayApi\Dto\PaymentRequestData;
use Ratepay\RpayPayments\Util\RequestHelper;
use RuntimeException;
use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity;
use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity;
Expand Down Expand Up @@ -59,8 +60,7 @@ protected function _getData(AbstractRequestData $requestData): ?object
{
/** @var CheckoutOperationInterface $requestData */
/** @var DataBag $requestDataBag */
$requestDataBag = $requestData->getRequestDataBag();
$requestDataBag = $requestDataBag->get('ratepay', new DataBag());
$requestDataBag = RequestHelper::getRatepayData($requestData->getRequestDataBag()) ?: new DataBag();

$customerEntity = $requestData->getCustomer();
if ($requestData instanceof PaymentRequestData) {
Expand Down
37 changes: 37 additions & 0 deletions src/Util/RequestHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@

namespace Ratepay\RpayPayments\Util;

use Shopware\Core\Framework\Validation\DataBag\DataBag;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;

class RequestHelper
{
final public const WRAPPER_KEY = 'paymentDetails';

final public const RATEPAY_DATA_KEY = 'ratepay';

public static function getArrayBag(Request $request, string $key, ?array $default = null): ?RequestDataBag
{
$data = self::getArray($request, $key, $default);
Expand All @@ -35,4 +41,35 @@ public static function getArray(Request $request, string $key, ?array $default =

return $data;
}

public static function isRatepayDataWrapped(ParameterBag $parameterBag): bool
{
return $parameterBag->has(self::WRAPPER_KEY)
&& ($pd = $parameterBag->get(self::WRAPPER_KEY)) instanceof ParameterBag
&& $pd->has(self::RATEPAY_DATA_KEY);
}

public static function getRatepayData(ParameterBag $parameterBag): ?DataBag
{
if (self::isRatepayDataWrapped($parameterBag)) {
$wrapper = $parameterBag->get(self::WRAPPER_KEY);
if ($wrapper instanceof ParameterBag) {
$ratepayData = $wrapper->get(self::RATEPAY_DATA_KEY);
} elseif (is_array($wrapper)) {
$ratepayData = $wrapper[self::RATEPAY_DATA_KEY] ?? null;
}
} else {
$ratepayData = $parameterBag->get(self::RATEPAY_DATA_KEY);
}

if (!isset($ratepayData)) {
return null;
}

if ($ratepayData instanceof DataBag) {
return $ratepayData;
}

return new DataBag(is_array($ratepayData) ? $ratepayData : $ratepayData->all());
}
}

0 comments on commit 9213230

Please sign in to comment.