Skip to content

Commit 1aa0a31

Browse files
authored
RATESWSX-307: add support for login as modules (#68)
1 parent 86086bd commit 1aa0a31

34 files changed

+314
-125
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## WIP
44

5-
* RATESWX-306: installment: prevent fatal error message in checkout on unreachable gateway
5+
* RATESWSX-306: installment: prevent fatal error message in checkout on unreachable gateway
6+
* RATESWSX-307: add support for login as (third-party modules and Shopware standard since 6.6.5.x)
67

78
## Version 7.0.1 - Released on 2024-06-07
89

src/Components/Account/Subscriber/AccountSubscriber.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public function onPaymentOrderRouteRequest(SetPaymentOrderRouteRequestEvent $eve
129129
$requestData = new DataBag($event->getStorefrontRequest()->request->all());
130130
$ratepayData = RequestHelper::getRatepayData($requestData);
131131

132-
$validationDefinitions = $paymentHandler->getValidationDefinitions($requestData, $orderEntity);
132+
$validationDefinitions = $paymentHandler->getValidationDefinitions($requestData, $event->getSalesChannelContext(), $orderEntity);
133133
$definition = new DataValidationDefinition();
134134
$definition->addSub(RequestHelper::RATEPAY_DATA_KEY, DataValidationHelper::addSubConstraints(new DataValidationDefinition(), $validationDefinitions));
135135
try {

src/Components/AdminOrders/DependencyInjection/services.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<service id="Ratepay\RpayPayments\Components\AdminOrders\Service\DfpService"
1414
decorates="Ratepay\RpayPayments\Components\DeviceFingerprint\DfpService">
1515
<argument key="$decorated" type="service" id=".inner"/>
16+
</service>
17+
18+
<service id="Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService">
1619
<argument key="$sessionKey">%ratepay.admin.storefront-login.token%</argument>
1720
</service>
1821
</services>

src/Components/AdminOrders/DependencyInjection/subscriber.xml

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,9 @@
99
<tag name="kernel.event_subscriber"/>
1010
</defaults>
1111

12-
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\ProfileConfigSubscriber">
13-
<argument key="$sessionKey">%ratepay.admin.storefront-login.token%</argument>
14-
</service>
15-
16-
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\PageSubscriber">
17-
<argument key="$sessionKey">%ratepay.admin.storefront-login.token%</argument>
18-
<tag name="kernel.event_subscriber"/>
19-
</service>
12+
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\ProfileConfigSubscriber"/>
13+
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\PageSubscriber"/>
14+
<service id="Ratepay\RpayPayments\Components\AdminOrders\Subscriber\LoginSubscriber"/>
2015

2116
</services>
2217

src/Components/AdminOrders/Service/DfpService.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,32 @@ class DfpService implements DfpServiceInterface
2121
public function __construct(
2222
private readonly DfpServiceInterface $decorated,
2323
private readonly RequestStack $requestStack,
24-
private readonly string $sessionKey
24+
private readonly SessionService $sessionService
2525
) {
2626
}
2727

28-
public function generatedDfpId(Request $request, OrderEntity|SalesChannelContext $baseData): ?string
28+
public function generatedDfpId(Request $request, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): ?string
2929
{
30-
return $this->isDfpRequired($baseData) ? $this->decorated->generatedDfpId($request, $baseData) : null;
30+
return $this->isDfpRequired($salesChannelContext, $orderEntity) ? $this->decorated->generatedDfpId($request, $salesChannelContext, $orderEntity) : null;
3131
}
3232

33-
public function getDfpSnippet(Request $request, OrderEntity|SalesChannelContext $baseData): ?string
33+
public function getDfpSnippet(Request $request, SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): ?string
3434
{
35-
return $this->isDfpRequired($baseData) ? $this->decorated->getDfpSnippet($request, $baseData) : null;
35+
return $this->isDfpRequired($salesChannelContext, $orderEntity) ? $this->decorated->getDfpSnippet($request, $salesChannelContext, $orderEntity) : null;
3636
}
3737

38-
public function isDfpIdValid(OrderEntity|SalesChannelContext $baseData, string $dfpId = null): bool
38+
public function isDfpIdValid(SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null, string $dfpId = null): bool
3939
{
40-
return !$this->isDfpRequired($baseData) || $this->decorated->isDfpIdValid($baseData, $dfpId);
40+
return !$this->isDfpRequired($salesChannelContext, $orderEntity) || $this->decorated->isDfpIdValid($salesChannelContext, $orderEntity, $dfpId);
4141
}
4242

43-
public function isDfpRequired(OrderEntity|SalesChannelContext $object): bool
43+
public function isDfpRequired(SalesChannelContext $salesChannelContext, OrderEntity $orderEntity = null): bool
4444
{
4545
$session = $this->requestStack->getMainRequest()->getSession();
46-
if ($session->get($this->sessionKey)) {
46+
if ($this->sessionService->isAdminSession($salesChannelContext, $session)) {
4747
return false;
4848
}
4949

50-
return $this->decorated->isDfpRequired($object);
50+
return $this->decorated->isDfpRequired($salesChannelContext, $orderEntity);
5151
}
5252
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/*
5+
* Copyright (c) Ratepay GmbH
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace Ratepay\RpayPayments\Components\AdminOrders\Service;
12+
13+
use Shopware\Core\System\SalesChannel\SalesChannelContext;
14+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
15+
16+
class SessionService
17+
{
18+
public function __construct(
19+
private readonly string $sessionKey
20+
) {
21+
}
22+
23+
public function isAdminSession(SalesChannelContext $context, SessionInterface $session): bool
24+
{
25+
return $this->isRatepayAdminSession($session) || $this->isLoggedInAsCustomer($context, $session);
26+
}
27+
28+
public function canLogout(SalesChannelContext $context, SessionInterface $session): bool
29+
{
30+
// allow only session logout if the session has been created by ratepay
31+
return $this->isRatepayAdminSession($session) && !$this->isLoggedInAsCustomer($context, $session);
32+
}
33+
34+
public function isLoggedInAsCustomer(SalesChannelContext $context, SessionInterface $session): bool
35+
{
36+
// supported since SW 6.6.5.x - TODO remove this check if compatibility has been change to Shopware >= 6.6.5
37+
if (method_exists($context, 'getImitatingUserId') && $context->getImitatingUserId() !== null) {
38+
return true;
39+
}
40+
41+
if ($context->getCustomerId() === null) {
42+
return false;
43+
}
44+
45+
foreach ($this->getThirdPartyLoginAsSessionKeys() as $key) {
46+
if ($session->has($key)) {
47+
return true;
48+
}
49+
}
50+
51+
return false;
52+
}
53+
54+
public function destroy(SessionInterface $session): void
55+
{
56+
$session->remove($this->sessionKey);
57+
58+
// make sure that the third-party modules did not left any data, which we will check
59+
foreach ($this->getThirdPartyLoginAsSessionKeys() as $key) {
60+
$session->remove($key);
61+
}
62+
}
63+
64+
private function isRatepayAdminSession(SessionInterface $session): bool
65+
{
66+
return $session->get($this->sessionKey) === true;
67+
}
68+
69+
private function getThirdPartyLoginAsSessionKeys(): array
70+
{
71+
$keys = [];
72+
73+
// login as (module: https://store.shopware.com/de/jlau706451421896/als-kunde-einloggen.html)
74+
if (defined('Jlau\LoginAsCustomer\Controller\LoginAsCustomer::SESSION_NAME')) {
75+
$keys[] = constant('Jlau\LoginAsCustomer\Controller\LoginAsCustomer::SESSION_NAME');
76+
}
77+
78+
// login as (module: https://store.shopware.com/de/swpa452746080451m/als-kunde-einloggen.html)
79+
if (defined('Swpa\SwpaLoginAsCustomer\Service\LoginService::ADMIN_CUSTOMER_CONTEXT_EXTENSION')) {
80+
$keys[] = constant('Swpa\SwpaLoginAsCustomer\Service\LoginService::ADMIN_CUSTOMER_CONTEXT_EXTENSION');
81+
}
82+
83+
return $keys;
84+
}
85+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/*
5+
* Copyright (c) Ratepay GmbH
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace Ratepay\RpayPayments\Components\AdminOrders\Subscriber;
12+
13+
use Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService;
14+
use Shopware\Core\Checkout\Customer\Event\CustomerLogoutEvent;
15+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16+
use Symfony\Component\HttpFoundation\RequestStack;
17+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
18+
19+
class LoginSubscriber implements EventSubscriberInterface
20+
{
21+
public function __construct(
22+
private readonly SessionService $sessionService,
23+
private readonly RequestStack $requestStack
24+
) {
25+
}
26+
27+
public static function getSubscribedEvents(): array
28+
{
29+
return [
30+
CustomerLogoutEvent::class => ['onLogout', -3000], // as late as possible to prioritize thirdparty modules
31+
];
32+
}
33+
34+
public function onLogout(CustomerLogoutEvent $event): void
35+
{
36+
$session = $this->requestStack->getMainRequest()?->getSession();
37+
if (!$session instanceof SessionInterface) {
38+
return;
39+
}
40+
41+
$this->sessionService->destroy($session);
42+
}
43+
}

src/Components/AdminOrders/Subscriber/PageSubscriber.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111

1212
namespace Ratepay\RpayPayments\Components\AdminOrders\Subscriber;
1313

14+
use Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService;
1415
use Shopware\Storefront\Event\StorefrontRenderEvent;
1516
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1617

1718
class PageSubscriber implements EventSubscriberInterface
1819
{
1920
public function __construct(
20-
private readonly string $sessionKey
21+
private readonly SessionService $sessionService
2122
) {
2223
}
2324

@@ -31,6 +32,10 @@ public static function getSubscribedEvents(): array
3132
public function onPage(StorefrontRenderEvent $event): void
3233
{
3334
$session = $event->getRequest()->getSession();
34-
$event->setParameter('ratepayAdminOrderSession', $session->get($this->sessionKey) === true);
35+
$event->setParameter('ratepayAdminOrderSession', [
36+
'active' => $this->sessionService->isAdminSession($event->getSalesChannelContext(), $session),
37+
'canLogout' => $this->sessionService->canLogout($event->getSalesChannelContext(), $session),
38+
'isLoggedInAsCustomer' => $this->sessionService->isLoggedInAsCustomer($event->getSalesChannelContext(), $session),
39+
]);
3540
}
3641
}

src/Components/AdminOrders/Subscriber/ProfileConfigSubscriber.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Ratepay\RpayPayments\Components\AdminOrders\Subscriber;
1313

14+
use Ratepay\RpayPayments\Components\AdminOrders\Service\SessionService;
1415
use Ratepay\RpayPayments\Components\ProfileConfig\Event\CreateProfileConfigCriteriaEvent;
1516
use Ratepay\RpayPayments\Components\ProfileConfig\Model\ProfileConfigEntity;
1617
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
@@ -21,7 +22,7 @@ class ProfileConfigSubscriber implements EventSubscriberInterface
2122
{
2223
public function __construct(
2324
private readonly RequestStack $requestStack,
24-
private readonly string $sessionKey
25+
private readonly SessionService $sessionService
2526
) {
2627
}
2728

@@ -34,9 +35,7 @@ public static function getSubscribedEvents(): array
3435

3536
public function onLoadConfig(CreateProfileConfigCriteriaEvent $event): void
3637
{
37-
$session = $this->requestStack->getMainRequest()->getSession();
38-
39-
if ($session->get($this->sessionKey) === true) {
38+
if ($this->sessionService->isAdminSession($event->getSalesChannelContext(), $this->requestStack->getMainRequest()->getSession())) {
4039
$event->getCriteria()->addFilter(new EqualsFilter(ProfileConfigEntity::FIELD_ONLY_ADMIN_ORDERS, true));
4140
} else {
4241
$event->getCriteria()->addFilter(new EqualsFilter(ProfileConfigEntity::FIELD_ONLY_ADMIN_ORDERS, false));

src/Components/Checkout/SalesChannel/HandlePaymentMethodRoute.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function load(Request $request, SalesChannelContext $context): HandlePaym
4848
if ($request->request->getBoolean('updatePayment')) {
4949
$orderId = $request->request->get('orderId');
5050

51+
/** @var OrderEntity|null $order */
5152
$order = $this->orderRepository->search(CriteriaHelper::getCriteriaForOrder($orderId), $context->getContext())->first();
5253
if ($order instanceof OrderEntity && ($transaction = $order->getTransactions()->last()) instanceof OrderTransactionEntity) {
5354
$paymentHandlerIdentifier = $transaction->getPaymentMethod()->getHandlerIdentifier();
@@ -57,7 +58,7 @@ public function load(Request $request, SalesChannelContext $context): HandlePaym
5758
}
5859

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

6364
return $this->innerService->load($request, $context);

0 commit comments

Comments
 (0)