diff --git a/Api/Config/System/ConnectionInterface.php b/Api/Config/System/ConnectionInterface.php index a464ee6..fa17b1b 100644 --- a/Api/Config/System/ConnectionInterface.php +++ b/Api/Config/System/ConnectionInterface.php @@ -26,6 +26,7 @@ interface ConnectionInterface extends DebugInterface public const XML_PATH_PRODUCTION_CLIENT_SECRET = 'payment/truelayer/production_client_secret'; public const XML_PATH_PRODUCTION_PRIVATE_KEY = 'payment/truelayer/production_private_key'; public const XML_PATH_PRODUCTION_KEY_ID = 'payment/truelayer/production_key_id'; + public const XML_PATH_CACHE_ENCRYPTION_KEY = 'payment/truelayer/cache_encryption_key'; public const XML_PATH_PRODUCTION_RELEASE_CHANNEL = 'payment/truelayer/production_release_channel'; /** diff --git a/Controller/Adminhtml/Credentials/Check.php b/Controller/Adminhtml/Credentials/Check.php index 2104045..f7cd6bc 100644 --- a/Controller/Adminhtml/Credentials/Check.php +++ b/Controller/Adminhtml/Credentials/Check.php @@ -171,7 +171,8 @@ private function getCredentials(): array 'client_id' => $clientId, 'client_secret' => $clientSecret, 'private_key' => $this->getPrivateKeyPath($configCredentials), - 'key_id' => $keyId + 'key_id' => $keyId, + 'cache_encryption_key' => $configCredentials['cache_encryption_key'] ] ]; } diff --git a/Model/Cache/CacheType.php b/Model/Cache/CacheType.php new file mode 100644 index 0000000..13b46a0 --- /dev/null +++ b/Model/Cache/CacheType.php @@ -0,0 +1,29 @@ +get(self::TYPE_IDENTIFIER), + self::CACHE_TAG + ); + } +} diff --git a/Model/Config/System/ConnectionRepository.php b/Model/Config/System/ConnectionRepository.php index da47f51..c385ab0 100644 --- a/Model/Config/System/ConnectionRepository.php +++ b/Model/Config/System/ConnectionRepository.php @@ -43,7 +43,8 @@ public function getCredentials(?int $storeId = null, ?bool $forceSandbox = null) "client_id" => $this->getClientId($storeId, $isSandBox), "client_secret" => $this->getClientSecret($storeId, $isSandBox), "private_key" => $this->getPathToPrivateKey($storeId, $isSandBox), - "key_id" => $this->getKeyId($storeId, $isSandBox) + "key_id" => $this->getKeyId($storeId, $isSandBox), + "cache_encryption_key" => $this->getCacheEncryptionKey($storeId) ]; } @@ -114,4 +115,23 @@ private function getClientSecret(?int $storeId = null, bool $isSandBox = false): return ''; } + + /** + * @param int|null $storeId + * + * @return string + */ + private function getCacheEncryptionKey(?int $storeId = null): ?string + { + $path = self::XML_PATH_CACHE_ENCRYPTION_KEY; + $value = $this->getStoreValue($path, $storeId); + if (is_string($value)) { + if ($value) { + return $this->encryptor->decrypt($value); + } + return ''; + } + + return null; + } } diff --git a/Service/Cache/InvalidArgumentException.php b/Service/Cache/InvalidArgumentException.php new file mode 100644 index 0000000..c25f12f --- /dev/null +++ b/Service/Cache/InvalidArgumentException.php @@ -0,0 +1,12 @@ +cacheFrontend = $cacheFrontend; + } + + /** + * {@inheritdoc} + */ + public function get(string $key, mixed $default = null): mixed + { + $item = $this->cacheFrontend->load($key); + + if ($item !== false) { + $item = unserialize($item); + } else { + $item = $default; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function set(string $key, $value, $ttl = null): bool + { + $value = serialize($value); + return $this->cacheFrontend->save( + $value, + $key, + [CacheType::CACHE_TAG], + $ttl + ); + } + + /** + * {@inheritdoc} + */ + public function delete($key): bool + { + return $this->cacheFrontend->remove($key); + } + + /** + * {@inheritdoc} + */ + public function clear(): bool + { + return $this->cacheFrontend->clean(); + } + + /** + * {@inheritdoc} + */ + public function getMultiple(iterable $keys, mixed $default = null): iterable + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } + $values = []; + foreach ($keys as $key) { + $values[$key] = $this->get($key, $default); + } + return $values; + } + + /** + * {@inheritdoc} + * @param iterable $values + */ + public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool + { + $stringKeyedValues = []; + foreach ($values as $key => $value) { + if (is_int($key)) { + $key = (string) $key; + } + + if (!is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', gettype($key))); + } + + $stringKeyedValues[$key] = $value; + } + $success = true; + foreach ($stringKeyedValues as $key => $value) { + $success = $this->set($key, $value, $ttl) && $success; + } + + return $success; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple(iterable $keys): bool + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } + $success = true; + foreach ($keys as $key) { + $success = $this->delete($key) && $success; + } + return $success; + } + + /** + * {@inheritdoc} + */ + public function has(string $key): bool + { + return $this->cacheFrontend->test($key); + } +} diff --git a/Service/Client/ClientFactory.php b/Service/Client/ClientFactory.php index 049a696..ed97224 100644 --- a/Service/Client/ClientFactory.php +++ b/Service/Client/ClientFactory.php @@ -12,6 +12,8 @@ use TrueLayer\Client; use TrueLayer\Connect\Api\Config\RepositoryInterface as ConfigRepository; use TrueLayer\Connect\Api\Log\LogServiceInterface; +use TrueLayer\Connect\Service\Cache\Psr16CacheAdapter; +use TrueLayer\Exceptions\InvalidArgumentException; use TrueLayer\Exceptions\SignerException; use TrueLayer\Interfaces\Client\ClientInterface; use TrueLayer\Settings; @@ -20,6 +22,7 @@ class ClientFactory { private ConfigRepository $configProvider; private LogServiceInterface $logger; + private Psr16CacheAdapter $cacheAdapter; /** * @param ConfigRepository $configProvider @@ -27,10 +30,12 @@ class ClientFactory */ public function __construct( ConfigRepository $configProvider, - LogServiceInterface $logger + LogServiceInterface $logger, + Psr16CacheAdapter $cacheAdapter, ) { $this->configProvider = $configProvider; $this->logger = $logger; + $this->cacheAdapter = $cacheAdapter; } /** @@ -54,18 +59,27 @@ public function create(int $storeId = 0, ?array $data = []): ?ClientInterface /** * @param array $credentials + * @param bool|null $forceSandbox * @return ClientInterface|null + * @throws InvalidArgumentException * @throws SignerException */ private function createClient(array $credentials, ?bool $forceSandbox = null): ?ClientInterface { + $cacheEncryptionKey = $credentials['cache_encryption_key']; Settings::tlAgent('truelayer-magento/' . $this->configProvider->getExtensionVersion()); - return Client::configure() - ->clientId($credentials['client_id']) + + $clientFactory = Client::configure(); + $clientFactory->clientId($credentials['client_id']) ->clientSecret($credentials['client_secret']) ->keyId($credentials['key_id']) ->pemFile($credentials['private_key']) - ->useProduction(is_null($forceSandbox) ? !$this->configProvider->isSandbox() : !$forceSandbox) - ->create(); + ->useProduction(is_null($forceSandbox) ? !$this->configProvider->isSandbox() : !$forceSandbox); + + if ($cacheEncryptionKey) { + $clientFactory->cache($this->cacheAdapter, $cacheEncryptionKey); + } + + return $clientFactory->create(); } } diff --git a/Setup/Recurring.php b/Setup/Recurring.php new file mode 100644 index 0000000..ed546d4 --- /dev/null +++ b/Setup/Recurring.php @@ -0,0 +1,44 @@ +startSetup(); + + $credentials = $this->configRepository->getCredentials(); + $cacheEncryptionKey = $credentials['cache_encryption_key'] ?? null; + if ($cacheEncryptionKey) { + try { + $binEncryptionKey = \hex2bin($cacheEncryptionKey); + if (strlen($binEncryptionKey) !== 32) { + $cacheEncryptionKey = null; + } + } catch (\Exception $e) { + $cacheEncryptionKey = null; + } + } + if (is_null($cacheEncryptionKey)) { + $path = ConnectionInterface::XML_PATH_CACHE_ENCRYPTION_KEY; + $value = bin2hex(openssl_random_pseudo_bytes(32)); + $value = $this->encryptor->encrypt($value); + $this->resourceConfig->saveConfig($path, $value, 'default', 0); + } + + $setup->endSetup(); + } +} \ No newline at end of file diff --git a/etc/cache.xml b/etc/cache.xml new file mode 100644 index 0000000..0237bc0 --- /dev/null +++ b/etc/cache.xml @@ -0,0 +1,7 @@ + + + + + Cache used by TrueLayer Plugin + +