Skip to content

Commit 92b1703

Browse files
authored
Merge branch 'magento-commerce:2.4-develop' into AC-229
2 parents 257126d + 6063639 commit 92b1703

File tree

8 files changed

+853
-25
lines changed

8 files changed

+853
-25
lines changed

app/code/Magento/GraphQlCache/Model/Plugin/Integration/Api/UserTokenIssuer.php renamed to app/code/Magento/GraphQlCache/Model/Plugin/Auth/TokenIssuer.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
*/
66
declare(strict_types=1);
77

8-
namespace Magento\GraphQlCache\Model\Plugin\Integration\Api;
8+
namespace Magento\GraphQlCache\Model\Plugin\Auth;
99

1010
use Magento\Authorization\Model\UserContextInterface;
11-
use Magento\Framework\App\ResponseInterface;
1211
use Magento\GraphQl\Model\Query\ContextFactoryInterface;
1312
use Magento\Integration\Api\UserTokenIssuerInterface;
1413

1514
/**
16-
* Load the shared UserContext with data for the user used to generate the token
15+
* Load the shared UserContext with data for the new user after a token is generated
1716
*/
18-
class UserTokenIssuer
17+
class TokenIssuer
1918
{
2019
/**
2120
* @var ContextFactoryInterface
@@ -36,7 +35,7 @@ public function __construct(ContextFactoryInterface $contextFactory)
3635
* @param UserTokenIssuerInterface $issuer
3736
* @param string $result
3837
* @param UserContextInterface $userContext
39-
* @return ResponseInterface
38+
* @return string
4039
*
4140
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
4241
*/
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQlCache\Model\Plugin\Auth;
9+
10+
use Magento\Authorization\Model\UserContextInterface;
11+
use Magento\GraphQl\Model\Query\ContextFactoryInterface;
12+
use Magento\Integration\Api\UserTokenRevokerInterface;
13+
use Magento\Integration\Model\CustomUserContext;
14+
15+
/**
16+
* Load the shared UserContext with data for guest after a token is revoked
17+
*/
18+
class TokenRevoker
19+
{
20+
/**
21+
* @var ContextFactoryInterface
22+
*/
23+
private $contextFactory;
24+
25+
/**
26+
* @param ContextFactoryInterface $contextFactory
27+
*/
28+
public function __construct(ContextFactoryInterface $contextFactory)
29+
{
30+
$this->contextFactory = $contextFactory;
31+
}
32+
33+
/**
34+
* Reset the shared user context to guest after a token is revoked
35+
*
36+
* @param UserTokenRevokerInterface $revoker
37+
* @return void
38+
*
39+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
40+
*/
41+
public function afterRevokeFor(UserTokenRevokerInterface $revoker): void
42+
{
43+
$this->contextFactory->create(new CustomUserContext(0, UserContextInterface::USER_TYPE_GUEST));
44+
}
45+
}

app/code/Magento/GraphQlCache/etc/graphql/di.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
<plugin name="result-varnish-cache" type="Magento\PageCache\Model\Controller\Result\VarnishPlugin"/>
2525
</type>
2626
<type name="Magento\Integration\Api\UserTokenIssuerInterface">
27-
<plugin name="set-context-after-token" type="Magento\GraphQlCache\Model\Plugin\Integration\Api\UserTokenIssuer"/>
27+
<plugin name="set-context-after-token" type="Magento\GraphQlCache\Model\Plugin\Auth\TokenIssuer"/>
28+
</type>
29+
<type name="Magento\Integration\Api\UserTokenRevokerInterface">
30+
<plugin name="set-guest-after-revoke" type="Magento\GraphQlCache\Model\Plugin\Auth\TokenRevoker"/>
2831
</type>
2932
</config>

app/code/Magento/RemoteStorage/Setup/ConfigOptionsList.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ public function createConfig(array $options, DeploymentConfig $deploymentConfig)
141141
*/
142142
public function validate(array $options, DeploymentConfig $deploymentConfig): array
143143
{
144+
// deployment configuration existence determines readiness of object manager to resolve remote storage drivers
145+
$isDeploymentConfigExists = (bool) $deploymentConfig->getConfigData();
146+
147+
if (!$isDeploymentConfigExists) {
148+
return [];
149+
}
150+
144151
$driver = $options[self::OPTION_REMOTE_STORAGE_DRIVER] ?? DriverPool::FILE;
145152

146153
if ($driver === DriverPool::FILE) {
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\RemoteStorage\Test\Unit\Setup;
8+
9+
use Magento\Framework\App\DeploymentConfig;
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\RemoteStorage\Driver\DriverFactoryPool;
12+
use Magento\RemoteStorage\Driver\RemoteDriverInterface;
13+
use Magento\RemoteStorage\Setup\ConfigOptionsList;
14+
use Magento\RemoteStorage\Driver\DriverFactoryInterface;
15+
use PHPUnit\Framework\MockObject\MockObject;
16+
use PHPUnit\Framework\TestCase;
17+
use Psr\Log\LoggerInterface;
18+
19+
class ConfigOptionsListTest extends TestCase
20+
{
21+
/**
22+
* @var DriverFactoryPool|MockObject
23+
*/
24+
private $driverFactoryPoolMock;
25+
26+
/**
27+
* @var LoggerInterface|MockObject
28+
*/
29+
private $loggerMock;
30+
31+
/**
32+
* @var ConfigOptionsList
33+
*/
34+
private $configOptionsList;
35+
36+
/**
37+
* @return void
38+
* @throws \Magento\Framework\Exception\FileSystemException
39+
*/
40+
protected function setUp(): void
41+
{
42+
$this->driverFactoryPoolMock = $this->getMockBuilder(DriverFactoryPool::class)
43+
->disableOriginalConstructor()
44+
->getMock();
45+
46+
$this->loggerMock = $this->getMockBuilder(LoggerInterface::class)
47+
->disableOriginalConstructor()
48+
->getMock();
49+
50+
$this->configOptionsList = new ConfigOptionsList(
51+
$this->driverFactoryPoolMock,
52+
$this->loggerMock
53+
);
54+
}
55+
56+
/**
57+
* @param array $input
58+
* @param bool $isDeploymentConfigExists
59+
* @param array $expectedOutput
60+
* @dataProvider validateDataProvider
61+
*/
62+
public function testValidate(array $input, bool $isDeploymentConfigExists, array $expectedOutput)
63+
{
64+
$deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class)
65+
->disableOriginalConstructor()
66+
->getMock();
67+
68+
$deploymentConfigMock
69+
->expects(static::once())
70+
->method('getConfigData')
71+
->willReturn($isDeploymentConfigExists);
72+
73+
$isConnectionToBeTested = $isDeploymentConfigExists && isset(
74+
$input['remote-storage-region'],
75+
$input['remote-storage-bucket']
76+
);
77+
78+
if ($isConnectionToBeTested) {
79+
$driverFactoryMock = $this->getMockBuilder(DriverFactoryInterface::class)
80+
->disableOriginalConstructor()
81+
->getMock();
82+
83+
$this->driverFactoryPoolMock
84+
->expects(static::once())
85+
->method('get')
86+
->with($input['remote-storage-driver'])
87+
->willReturn($driverFactoryMock);
88+
89+
$remoteDriverMock = $this->getMockBuilder(RemoteDriverInterface::class)
90+
->disableOriginalConstructor()
91+
->getMock();
92+
93+
$driverFactoryMock
94+
->expects(static::once())
95+
->method('createConfigured')
96+
->willReturn($remoteDriverMock);
97+
98+
$testMethodExpectation = $remoteDriverMock->expects(static::once())->method('test');
99+
100+
$isExceptionExpectedToBeCaught = (bool) count($expectedOutput);
101+
102+
if ($isExceptionExpectedToBeCaught) {
103+
$adapterErrorMessage = str_replace('Adapter error: ', '', $expectedOutput[0]);
104+
$exception = new LocalizedException(__($adapterErrorMessage));
105+
$testMethodExpectation->willThrowException($exception);
106+
$this->loggerMock->expects(static::once())->method('critical')->with($exception->getMessage());
107+
}
108+
}
109+
110+
$this->assertEquals(
111+
$expectedOutput,
112+
$this->configOptionsList->validate($input, $deploymentConfigMock)
113+
);
114+
}
115+
116+
/**
117+
* @return array
118+
*/
119+
public function validateDataProvider()
120+
{
121+
return [
122+
'Local File Storage Before Deployment Config Exists' => [
123+
[], false, [],
124+
],
125+
'Local File Storage After Deployment Config Exists' => [
126+
[], true, [],
127+
],
128+
'Remote Storage Before Deployment Config Exists' => [
129+
[
130+
'remote-storage-driver' => 'aws-s3',
131+
'remote-storage-region' => 'us-east-1',
132+
'remote-storage-bucket' => 'bucket1',
133+
],
134+
false,
135+
[],
136+
],
137+
'Remote Storage Missing Region' => [
138+
[
139+
'remote-storage-driver' => 'aws-s3',
140+
'remote-storage-bucket' => 'bucket1',
141+
],
142+
true,
143+
[
144+
'Region is required',
145+
],
146+
],
147+
'Remote Storage Missing Bucket' => [
148+
[
149+
'remote-storage-driver' => 'aws-s3',
150+
'remote-storage-region' => 'us-east-1',
151+
],
152+
true,
153+
[
154+
'Bucket is required',
155+
],
156+
],
157+
'Remote Storage Missing Region and Bucket' => [
158+
[
159+
'remote-storage-driver' => 'aws-s3',
160+
],
161+
true,
162+
[
163+
'Region is required',
164+
'Bucket is required',
165+
],
166+
],
167+
'Valid Remote Storage Config with Successful Test Connection' => [
168+
[
169+
'remote-storage-driver' => 'aws-s3',
170+
'remote-storage-region' => 'us-east-1',
171+
'remote-storage-bucket' => 'bucket1',
172+
'remote-storage-prefix' => '',
173+
],
174+
true,
175+
[],
176+
],
177+
'Valid Remote Storage With Unsuccessful Test Connection' => [
178+
[
179+
'remote-storage-driver' => 'aws-s3',
180+
'remote-storage-region' => 'us-east-1',
181+
'remote-storage-bucket' => 'bucket1',
182+
'remote-storage-prefix' => '',
183+
],
184+
true,
185+
[
186+
'Adapter error: [Message from LocalizedException]',
187+
]
188+
],
189+
];
190+
}
191+
}

dev/tests/api-functional/testsuite/Magento/GraphQl/GraphQlCache/CacheIdFactorProviders/Customer/IsLoggedInProviderTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,44 @@ public function testCacheIdHeaderWithIsLoggedIn()
9393
$addProductToCustomerCartResponse['headers'][CacheIdCalculator::CACHE_ID_HEADER]
9494
);
9595
}
96+
97+
/**
98+
* Tests that cache id header resets to the one for guest when a customer token is revoked
99+
*
100+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
101+
* @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
102+
*/
103+
public function testCacheIdHeaderAfterRevokeToken()
104+
{
105+
// Get the guest cache id
106+
$guestCartResponse = $this->graphQlMutationWithResponseHeaders('mutation{createEmptyCart}');
107+
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $guestCartResponse['headers']);
108+
$guestCacheId = $guestCartResponse['headers'][CacheIdCalculator::CACHE_ID_HEADER];
109+
110+
// Get the customer cache id and token to send to the revoke mutation
111+
$generateToken = <<<MUTATION
112+
mutation{
113+
generateCustomerToken(email:"customer@example.com", password:"password")
114+
{token}
115+
}
116+
MUTATION;
117+
$tokenResponse = $this->graphQlMutationWithResponseHeaders($generateToken);
118+
$this->assertArrayHasKey('generateCustomerToken', $tokenResponse['body']);
119+
$customerToken = $tokenResponse['body']['generateCustomerToken']['token'];
120+
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $tokenResponse['headers']);
121+
$customerCacheId = $tokenResponse['headers'][CacheIdCalculator::CACHE_ID_HEADER];
122+
$this->assertNotEquals($customerCacheId, $guestCacheId);
123+
124+
// Revoke the token and check that it returns the guest cache id
125+
$revokeCustomerToken = "mutation{revokeCustomerToken{result}}";
126+
$revokeResponse = $this->graphQlMutationWithResponseHeaders(
127+
$revokeCustomerToken,
128+
[],
129+
'',
130+
['Authorization' => 'Bearer ' . $customerToken]
131+
);
132+
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $revokeResponse['headers']);
133+
$revokeCacheId = $revokeResponse['headers'][CacheIdCalculator::CACHE_ID_HEADER];
134+
$this->assertEquals($guestCacheId, $revokeCacheId);
135+
}
96136
}

0 commit comments

Comments
 (0)