Skip to content

Commit

Permalink
NEXT-19820 - Improve session invalidation on customer updates
Browse files Browse the repository at this point in the history
  • Loading branch information
shyim authored and pweyck committed Feb 4, 2022
1 parent 310e186 commit 47b4b09
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 40 deletions.
@@ -0,0 +1,6 @@
---
title: Improve session invalidate
issue: NEXT-19820
---
# Core
* Changed session invalidation for customer updates
98 changes: 60 additions & 38 deletions src/Core/Checkout/Customer/Subscriber/CustomerTokenSubscriber.php
Expand Up @@ -3,6 +3,7 @@
namespace Shopware\Core\Checkout\Customer\Subscriber;

use Shopware\Core\Checkout\Customer\CustomerEvents;
use Shopware\Core\Framework\DataAbstractionLayer\EntityWriteResult;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityDeletedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
use Shopware\Core\PlatformRequest;
Expand Down Expand Up @@ -37,64 +38,85 @@ public static function getSubscribedEvents()
}

public function onCustomerWritten(EntityWrittenEvent $event): void
{
foreach ($event->getWriteResults() as $writeResult) {
if ($writeResult->getOperation() !== EntityWriteResult::OPERATION_UPDATE) {
continue;
}

$payload = $writeResult->getPayload();
if (!$this->customerCredentialsChanged($payload)) {
continue;
}

$customerId = $payload['id'];
$newToken = $this->invalidateUsingSession($customerId);

if ($newToken) {
$this->contextPersister->revokeAllCustomerTokens($customerId, $newToken);
} else {
$this->contextPersister->revokeAllCustomerTokens($customerId);
}
}
}

public function onCustomerDeleted(EntityDeletedEvent $event): void
{
foreach ($event->getIds() as $customerId) {
$this->contextPersister->revokeAllCustomerTokens($customerId);
}
}

private function customerCredentialsChanged(array $payload): bool
{
return isset($payload['password']);
}

private function invalidateUsingSession(string $customerId): ?string
{
$master = $this->requestStack->getMainRequest();

if (!$master) {
return;
return null;
}

// Is not a storefront request
if (!$master->attributes->has(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT)) {
return;
return null;
}

/** @var SalesChannelContext $context */
$context = $master->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
$token = $context->getToken();

$payloads = $event->getPayloads();

foreach ($payloads as $payload) {
if ($this->customerCredentialsChanged($payload)) {
$newToken = $this->contextPersister->replace($token, $context);

$context->assign([
'token' => $newToken,
]);

if (!$master->hasSession()) {
return;
}
// Not loggedin skip
if ($context->getCustomer() === null) {
return null;
}

$session = $master->getSession();
$session->migrate();
$session->set('sessionId', $session->getId());
// The written customer is not the same as logged-in. We don't modify the user session
if ($context->getCustomer()->getId() !== $customerId) {
return null;
}

$session->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $newToken);
$master->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $newToken);
$token = $context->getToken();

return;
}
}
}
$newToken = $this->contextPersister->replace($token, $context);

public function onCustomerDeleted(EntityDeletedEvent $event): void
{
$master = $this->requestStack->getMainRequest();
$context->assign([
'token' => $newToken,
]);

if (!$master) {
return;
if (!$master->hasSession()) {
return null;
}

$customerIds = $event->getIds();
$session = $master->getSession();
$session->migrate();
$session->set('sessionId', $session->getId());

foreach ($customerIds as $customerId) {
$this->contextPersister->revokeAllCustomerTokens($customerId);
}
}
$session->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $newToken);
$master->headers->set(PlatformRequest::HEADER_CONTEXT_TOKEN, $newToken);

private function customerCredentialsChanged(array $payload): bool
{
return isset($payload['password']);
return $newToken;
}
}
Expand Up @@ -39,6 +39,35 @@ public function setUp(): void
$this->customerRepository = $this->getContainer()->get('customer.repository');
}

public function testCustomerTokenSubscriber(): void
{
$customerId = $this->createCustomer();

$this->connection->insert('sales_channel_api_context', [
'customer_id' => Uuid::fromHexToBytes($customerId),
'token' => 'test',
'sales_channel_id' => Uuid::fromHexToBytes(TestDefaults::SALES_CHANNEL),
'updated_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
'payload' => '{"customerId": "1234"}',
]);

$this->customerRepository->update([
[
'id' => $customerId,
'password' => 'fooo',
],
], Context::createDefaultContext());

static::assertSame(
[
'customerId' => null,
'billingAddressId' => null,
'shippingAddressId' => null,
],
json_decode($this->connection->fetchOne('SELECT payload FROM sales_channel_api_context WHERE token = "test"'), true)
);
}

public function testCustomerTokenSubscriberStorefrontShouldStillBeLoggedIn(): void
{
$customerId = $this->createCustomer();
Expand Down
8 changes: 6 additions & 2 deletions src/Core/Checkout/Test/Order/SalesChannel/OrderRouteTest.php
Expand Up @@ -712,11 +712,15 @@ public function testOrderSalesChannelRestriction(): void
],
],
]);
$testOrder = $this->createOrder($this->customerId, $this->email, $this->password);

$orderId = Uuid::randomHex();
$orderData = $this->getOrderData($orderId, $this->customerId, $this->email, $this->password, $this->context);
unset($orderData[0]['orderCustomer']['customer']['password']);
$this->orderRepository->create($orderData, $this->context);

$this->orderRepository->update([
[
'id' => $testOrder,
'id' => $orderId,
'salesChannelId' => $testChannel['id'],
],
], $this->context);
Expand Down
Expand Up @@ -172,7 +172,9 @@ public function revokeAllCustomerTokens(string $customerId, string ...$preserveT
->update('sales_channel_api_context')
->set('payload', ':payload')
->set('customer_id', 'NULL')
->set('updated_at', ':updatedAt')
->where('customer_id = :customerId')
->setParameter('updatedAt', (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT))
->setParameter(':payload', json_encode($revokeParams))
->setParameter(':customerId', Uuid::fromHexToBytes($customerId));

Expand Down

0 comments on commit 47b4b09

Please sign in to comment.