From af56ccdc9d7d33f84732f7185c92931fde16a9f4 Mon Sep 17 00:00:00 2001 From: Maksim Soldatjonok Date: Fri, 23 Feb 2024 17:09:43 +0200 Subject: [PATCH] Implement EncryptResolver (#73) --- Model/Resolver/EncryptResolver.php | 64 ++++++++++ .../Model/Resolver/EncryptResolverTest.php | 112 ++++++++++++++++++ docs/config-import.md | 12 ++ etc/di.xml | 1 + 4 files changed, 189 insertions(+) create mode 100644 Model/Resolver/EncryptResolver.php create mode 100644 Test/Unit/Model/Resolver/EncryptResolverTest.php diff --git a/Model/Resolver/EncryptResolver.php b/Model/Resolver/EncryptResolver.php new file mode 100644 index 0000000..4477427 --- /dev/null +++ b/Model/Resolver/EncryptResolver.php @@ -0,0 +1,64 @@ +encryptor = $encryptor; + } + + /** + * Resolve the config value if wrapped with '%encrypt(value)%', this method encrypts the value. + * + * @param string|null $value + * @param string|null $configPath + * + * @return string|null + * + * @throws UnresolveableValueException + */ + public function resolve($value, $configPath = null) + { + if ($value === null) { + return null; + } + + $value = (string)$value; + if ($value === '%encrypt()%') { + throw new UnresolveableValueException('Please specify a valid value to encrypt.'); + } + + $valueToEncrypt = preg_replace_callback( + '/\%encrypt\(([^)]+)\)\%/', + function ($matches) { + return $matches[1]; + }, + $value + ); + + return $this->encryptor->encrypt($valueToEncrypt); + } + + /** + * @inheritDoc + */ + public function supports($value, $configPath = null): bool + { + return 0 === strncmp((string)$value, '%encrypt', strlen('%encrypt')); + } +} diff --git a/Test/Unit/Model/Resolver/EncryptResolverTest.php b/Test/Unit/Model/Resolver/EncryptResolverTest.php new file mode 100644 index 0000000..2b75ed0 --- /dev/null +++ b/Test/Unit/Model/Resolver/EncryptResolverTest.php @@ -0,0 +1,112 @@ +input = $this->createMock(InputInterface::class); + $this->output = $this->createMock(OutputInterface::class); + $this->questionHelper = $this->createMock(QuestionHelper::class); + $this->encryptor = $this->createMock(EncryptorInterface::class); + } + + /** + * @test + * + * @dataProvider resolveDataProvider + */ + public function validate($value, $expectedResult): void + { + $this->encryptor->expects($this->any()) + ->method('encrypt') + ->with($expectedResult) + ->willReturn($expectedResult); + + $this->assertEquals($this->getEncryptResolver()->resolve($value), $expectedResult); + } + + public function resolveDataProvider(): Generator + { + yield [ + 'test_without_data_to_encrypt', + 'test_without_data_to_encrypt', + ]; + yield [ + '%encrypt(data_to_encrypt)%', + 'data_to_encrypt', + ]; + yield [ + null, + '', + ]; + yield [ + false, + '', + ]; + yield [ + true, + '1', + ]; + } + + public function testItWillRaiseErrorIfEncryptValueIsEmpty(): void + { + $this->expectException(UnresolveableValueException::class); + + $this->getEncryptResolver()->resolve('%encrypt()%'); + } + + /** + * @return EncryptResolver + */ + private function getEncryptResolver() + { + $resolver = new EncryptResolver($this->encryptor); + $resolver->setInput($this->input); + $resolver->setOutput($this->output); + $resolver->setQuestionHelper($this->questionHelper); + + return $resolver; + } +} diff --git a/docs/config-import.md b/docs/config-import.md index 05bfd53..bc65780 100644 --- a/docs/config-import.md +++ b/docs/config-import.md @@ -87,6 +87,18 @@ vendorx/general/api_key: You can then set the environment variable `VENDORX_API_KEY` in your CI/CD configuration to the secret API key. +### Encryption Value Substitution + +For importing encrypted configuration data, such as passwords and API keys, into fields utilizing Magento's `\Magento\Config\Model\Config\Backend\Encrypted` backend model, use `%encrypt(value)%` (make sure to put quotes around it) placeholder within your configuration files. + +For example, this could be the content of your configuration file: + +``` +payment/provider/secret_key: + default: + 0: '%encrypt(mySecretKey)%' +``` + ### Delete Config Sometimes, it might be helpful to be able to delete certain config values and get back to the default behavior. To do so, your config value has to be a magic-ish string. diff --git a/etc/di.xml b/etc/di.xml index 83d7251..ac6e2e9 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -17,6 +17,7 @@ Semaio\ConfigImportExport\Model\Resolver\EnvironmentVariableResolver Semaio\ConfigImportExport\Model\Resolver\ThemePathResolver + Semaio\ConfigImportExport\Model\Resolver\EncryptResolver