Skip to content

Commit 9d8d22c

Browse files
Merge remote-tracking branch 'magento-l3/ACP2E-1454' into PR-L3-21-02-2023
2 parents 4a66488 + 4ffde30 commit 9d8d22c

File tree

5 files changed

+356
-9
lines changed

5 files changed

+356
-9
lines changed

app/code/Magento/AwsS3/Driver/AwsS3Factory.php

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,28 +54,37 @@ class AwsS3Factory implements DriverFactoryInterface
5454
*/
5555
private $cachePrefix;
5656

57+
/**
58+
* @var CachedCredentialsProvider
59+
*/
60+
private $cachedCredentialsProvider;
61+
5762
/**
5863
* @param ObjectManagerInterface $objectManager
5964
* @param Config $config
6065
* @param MetadataProviderInterfaceFactory $metadataProviderFactory
6166
* @param CacheInterfaceFactory $cacheInterfaceFactory
6267
* @param CachedAdapterInterfaceFactory $cachedAdapterInterfaceFactory
6368
* @param string|null $cachePrefix
69+
* @param CachedCredentialsProvider|null $cachedCredentialsProvider
6470
*/
6571
public function __construct(
6672
ObjectManagerInterface $objectManager,
6773
Config $config,
6874
MetadataProviderInterfaceFactory $metadataProviderFactory,
6975
CacheInterfaceFactory $cacheInterfaceFactory,
7076
CachedAdapterInterfaceFactory $cachedAdapterInterfaceFactory,
71-
string $cachePrefix = null
77+
string $cachePrefix = null,
78+
?CachedCredentialsProvider $cachedCredentialsProvider = null,
7279
) {
7380
$this->objectManager = $objectManager;
7481
$this->config = $config;
7582
$this->metadataProviderFactory = $metadataProviderFactory;
7683
$this->cacheInterfaceFactory = $cacheInterfaceFactory;
7784
$this->cachedAdapterInterfaceFactory = $cachedAdapterInterfaceFactory;
7885
$this->cachePrefix = $cachePrefix;
86+
$this->cachedCredentialsProvider = $cachedCredentialsProvider ??
87+
$this->objectManager->get(CachedCredentialsProvider::class);
7988
}
8089

8190
/**
@@ -94,18 +103,19 @@ public function create(): RemoteDriverInterface
94103
}
95104

96105
/**
97-
* @inheritDoc
106+
* Prepare config for S3Client
107+
*
108+
* @param array $config
109+
* @return array
110+
* @throws DriverException
98111
*/
99-
public function createConfigured(
100-
array $config,
101-
string $prefix,
102-
string $cacheAdapter = '',
103-
array $cacheConfig = []
104-
): RemoteDriverInterface {
112+
private function prepareConfig(array $config)
113+
{
105114
$config['version'] = 'latest';
106115

107116
if (empty($config['credentials']['key']) || empty($config['credentials']['secret'])) {
108-
unset($config['credentials']);
117+
//Access keys were not provided; request token from AWS config (local or EC2) and cache result
118+
$config['credentials'] = $this->cachedCredentialsProvider->get();
109119
}
110120

111121
if (empty($config['bucket']) || empty($config['region'])) {
@@ -120,6 +130,19 @@ public function createConfigured(
120130
$config['use_path_style_endpoint'] = boolval($config['path_style']);
121131
}
122132

133+
return $config;
134+
}
135+
136+
/**
137+
* @inheritDoc
138+
*/
139+
public function createConfigured(
140+
array $config,
141+
string $prefix,
142+
string $cacheAdapter = '',
143+
array $cacheConfig = []
144+
): RemoteDriverInterface {
145+
$config = $this->prepareConfig($config);
123146
$client = new S3Client($config);
124147
$adapter = new AwsS3V3Adapter($client, $config['bucket'], $prefix);
125148
$cache = $this->cacheInterfaceFactory->create(
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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\AwsS3\Driver;
9+
10+
use Aws\Credentials\CredentialProvider;
11+
12+
class CachedCredentialsProvider
13+
{
14+
/**
15+
* @var CredentialsCache
16+
*/
17+
private $magentoCacheAdapter;
18+
19+
/**
20+
* @param CredentialsCache $magentoCacheAdapter
21+
*/
22+
public function __construct(CredentialsCache $magentoCacheAdapter)
23+
{
24+
$this->magentoCacheAdapter = $magentoCacheAdapter;
25+
}
26+
27+
/**
28+
* Provides cache mechanism to retrieve and store AWS credentials
29+
*
30+
* @return callable
31+
*/
32+
public function get()
33+
{
34+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
35+
return call_user_func(
36+
[CredentialProvider::class, 'cache'],
37+
//phpcs:ignore Magento2.Functions.DiscouragedFunction
38+
call_user_func([CredentialProvider::class, 'defaultProvider']),
39+
$this->magentoCacheAdapter
40+
);
41+
}
42+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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\AwsS3\Driver;
9+
10+
use Aws\CacheInterface;
11+
use Aws\Credentials\CredentialsFactory;
12+
use Magento\Framework\App\CacheInterface as MagentoCacheInterface;
13+
use Magento\Framework\Serialize\Serializer\Json;
14+
15+
/** Cache Adapter for AWS credentials */
16+
class CredentialsCache implements CacheInterface
17+
{
18+
/**
19+
* @var MagentoCacheInterface
20+
*/
21+
private $magentoCache;
22+
23+
/**
24+
* @var Json
25+
*/
26+
private $json;
27+
28+
/**
29+
* @var CredentialsFactory
30+
*/
31+
private $credentialsFactory;
32+
33+
/**
34+
* @param MagentoCacheInterface $magentoCache
35+
* @param CredentialsFactory $credentialsFactory
36+
* @param Json $json
37+
*/
38+
public function __construct(MagentoCacheInterface $magentoCache, CredentialsFactory $credentialsFactory, Json $json)
39+
{
40+
$this->magentoCache = $magentoCache;
41+
$this->credentialsFactory = $credentialsFactory;
42+
$this->json = $json;
43+
}
44+
45+
/**
46+
* @inheritdoc
47+
*/
48+
public function get($key)
49+
{
50+
$value = $this->magentoCache->load($key);
51+
52+
if (!is_string($value)) {
53+
return null;
54+
}
55+
56+
$result = $this->json->unserialize($value);
57+
try {
58+
return $this->credentialsFactory->create($result);
59+
} catch (\Exception $e) {
60+
return $result;
61+
}
62+
}
63+
64+
/**
65+
* @inheritdoc
66+
*/
67+
public function set($key, $value, $ttl = 0)
68+
{
69+
if (method_exists($value, 'toArray')) {
70+
$value = $value->toArray();
71+
}
72+
$this->magentoCache->save($this->json->serialize($value), $key, [], $ttl);
73+
}
74+
75+
/**
76+
* @inheritdoc
77+
*/
78+
public function remove($key)
79+
{
80+
$this->magentoCache->remove($key);
81+
}
82+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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\AwsS3\Test\Unit\Driver;
9+
10+
use Magento\AwsS3\Driver\AwsS3Factory;
11+
use Magento\AwsS3\Driver\CachedCredentialsProvider;
12+
use Magento\Framework\ObjectManagerInterface;
13+
use Magento\RemoteStorage\Driver\Adapter\Cache\CacheInterfaceFactory;
14+
use Magento\RemoteStorage\Driver\Adapter\CachedAdapterInterfaceFactory;
15+
use Magento\RemoteStorage\Driver\Adapter\MetadataProviderInterfaceFactory;
16+
use Magento\RemoteStorage\Model\Config;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
class AwsS3FactoryTest extends TestCase
21+
{
22+
/**
23+
* @var AwsS3Factory
24+
*/
25+
private $factory;
26+
27+
/**
28+
* @var ObjectManagerInterface|MockObject
29+
*/
30+
private $objectManagerMock;
31+
32+
/**
33+
* @var Config|MockObject
34+
*/
35+
private $remoteStorageConfigMock;
36+
37+
/**
38+
* @var MetadataProviderInterfaceFactory|MockObject
39+
*/
40+
private $metadataFactoryMock;
41+
42+
/**
43+
* @var CacheInterfaceFactory|MockObject
44+
*/
45+
private $remoteStorageCacheMock;
46+
47+
/**
48+
* @var CachedAdapterInterfaceFactory|MockObject
49+
*/
50+
private $remoteCacheAdapterMock;
51+
52+
/**
53+
* @var string|null
54+
*/
55+
private $cachePrefix = 'testPrefix';
56+
57+
/**
58+
* @var CachedCredentialsProvider|MockObject
59+
*/
60+
private $cachedCredsProviderMock;
61+
62+
/**
63+
* @inheritDoc
64+
*/
65+
protected function setUp(): void
66+
{
67+
$this->objectManagerMock = $this->getMockForAbstractClass(ObjectManagerInterface::class);
68+
$this->remoteStorageConfigMock = $this->createMock(Config::class);
69+
$this->metadataFactoryMock = $this->createMock(MetadataProviderInterfaceFactory::class);
70+
$this->remoteStorageCacheMock = $this->createMock(CacheInterfaceFactory::class);
71+
$this->remoteCacheAdapterMock = $this->createMock(CachedAdapterInterfaceFactory::class);
72+
$this->cachedCredsProviderMock = $this->createMock(CachedCredentialsProvider::class);
73+
74+
$this->factory = new AwsS3Factory(
75+
$this->objectManagerMock,
76+
$this->remoteStorageConfigMock,
77+
$this->metadataFactoryMock,
78+
$this->remoteStorageCacheMock,
79+
$this->remoteCacheAdapterMock,
80+
$this->cachePrefix,
81+
$this->cachedCredsProviderMock
82+
);
83+
}
84+
85+
/**
86+
* If no credentials in magento config, credentials retrieved from AWS should be cached
87+
*
88+
* @return void
89+
*/
90+
public function testPrepareConfigUseCache()
91+
{
92+
$config = [
93+
'region' => 'us-west-1',
94+
'bucket' => 'someName',
95+
'credentials' => []
96+
];
97+
$this->cachedCredsProviderMock->expects($this->once())->method('get');
98+
$this->invokePrepareConfig($config);
99+
}
100+
101+
public function testPrepareConfigMissingRequired()
102+
{
103+
$config = [
104+
'credentials' => [
105+
'key' => 'someKey',
106+
'secret' => 'verySecretKey'
107+
]
108+
];
109+
110+
$this->expectException('\Magento\RemoteStorage\Driver\DriverException');
111+
$this->invokePrepareConfig($config);
112+
}
113+
114+
/**
115+
* Invoke private method via reflection
116+
*
117+
* @param array $config
118+
* @return array
119+
*/
120+
private function invokePrepareConfig(array $config): array
121+
{
122+
$method = new \ReflectionMethod(
123+
AwsS3Factory::class,
124+
'prepareConfig'
125+
);
126+
$method->setAccessible(true);
127+
128+
return $method->invokeArgs($this->factory, [$config]);
129+
}
130+
}

0 commit comments

Comments
 (0)