diff --git a/src/Wrapper/GenericAKVWrapper.php b/src/Wrapper/GenericAKVWrapper.php index 576e497..1bbbb1a 100644 --- a/src/Wrapper/GenericAKVWrapper.php +++ b/src/Wrapper/GenericAKVWrapper.php @@ -15,6 +15,8 @@ use Keboola\ObjectEncryptor\EncryptorOptions; use Keboola\ObjectEncryptor\Exception\ApplicationException; use Keboola\ObjectEncryptor\Exception\UserException; +use Keboola\ObjectEncryptor\Temporary\TransClient; +use Keboola\ObjectEncryptor\Temporary\TransClientNotAvailableException; use Psr\Log\NullLogger; use Retry\BackOff\ExponentialBackOffPolicy; use Retry\Policy\SimpleRetryPolicy; @@ -37,6 +39,9 @@ class GenericAKVWrapper implements CryptoWrapperInterface private string $keyVaultURL; private ?Client $client = null; + private TransClient|false|null $transClient = null; + private ?string $encryptorId = null; + public function __construct(EncryptorOptions $encryptorOptions) { // there is no way to pass backOffMaxTries option to the Azure Key Vault client. Yet. @@ -44,6 +49,8 @@ public function __construct(EncryptorOptions $encryptorOptions) if (empty($this->keyVaultURL)) { throw new ApplicationException('Cipher key settings are invalid.'); } + + $this->encryptorId = $encryptorOptions->getEncryptorId(); } public function getClient(): Client @@ -58,6 +65,22 @@ public function getClient(): Client return $this->client; } + public function getTransClient(): ?TransClient + { + if ($this->transClient === null) { + try { + $this->transClient = new TransClient( + new GuzzleClientFactory(new NullLogger()), + $this->encryptorId, + ); + } catch (TransClientNotAvailableException) { + $this->transClient = false; + } + } + + return $this->transClient ?: null; + } + private function getRetryProxy(): RetryProxy { $retryPolicy = new SimpleRetryPolicy(3); diff --git a/tests/Temporary/AKVWrappersWithTransClientTest.php b/tests/Temporary/AKVWrappersWithTransClientTest.php new file mode 100644 index 0000000..af8b372 --- /dev/null +++ b/tests/Temporary/AKVWrappersWithTransClientTest.php @@ -0,0 +1,150 @@ + [ + 'wrapperClass' => $className, + ]; + } + } + + /** + * @dataProvider provideAKVWrappers + * @param class-string $wrapperClass + */ + public function testWrappersDoNotHaveTransClientInitializedWhenTransEnvsMissing( + string $wrapperClass, + ): void { + $encryptorOptions = new EncryptorOptions( + stackId: 'some-stack', + akvUrl: 'some-url', + ); + + $wrapper = new $wrapperClass($encryptorOptions); + + self::assertNull($wrapper->getTransClient()); + } + + /** + * @dataProvider provideAKVWrappers + * @param class-string $wrapperClass + */ + public function testWrappersHaveTransClientInitialized( + string $wrapperClass, + ): void { + putenv('TRANS_AZURE_TENANT_ID=tenant-id'); + putenv('TRANS_AZURE_CLIENT_ID=client-id'); + putenv('TRANS_AZURE_CLIENT_SECRET=client-secret'); + putenv('TRANS_AZURE_KEY_VAULT_URL=https://vault-url'); + + $encryptorOptions = new EncryptorOptions( + stackId: 'some-stack', + akvUrl: 'some-url', + ); + + $wrapper = new $wrapperClass($encryptorOptions); + + $transClient = $wrapper->getTransClient(); + self::assertInstanceOf(TransClient::class, $transClient); + + // ensure getter returns a single instance of the TransClient + self::assertSame($transClient, $wrapper->getTransClient()); + } + + /** + * @dataProvider provideAKVWrappers + * @param class-string $wrapperClass + */ + public function testWrappersHaveTransClientWhenEncryptorIdMatches( + string $wrapperClass, + ): void { + putenv('TRANS_AZURE_TENANT_ID=tenant-id'); + putenv('TRANS_AZURE_CLIENT_ID=client-id'); + putenv('TRANS_AZURE_CLIENT_SECRET=client-secret'); + putenv('TRANS_AZURE_KEY_VAULT_URL='); + putenv('TRANS_AZURE_KEY_VAULT_URL_EXTRA_BRATWURST=https://german-vault-url'); + + $encryptorOptions = new EncryptorOptions( + stackId: 'some-stack', + akvUrl: 'some-url', + encryptorId: 'extra-bratwurst', + ); + + $wrapper = new $wrapperClass($encryptorOptions); + + $transClient = $wrapper->getTransClient(); + self::assertInstanceOf(TransClient::class, $transClient); + + // ensure getter returns a single instance of the TransClient + self::assertSame($transClient, $wrapper->getTransClient()); + } + + /** + * @dataProvider provideAKVWrappers + * @param class-string $wrapperClass + */ + public function testWrappersDoNotHaveTransClientWhenEncryptorIdMismatches( + string $wrapperClass, + ): void { + putenv('TRANS_AZURE_TENANT_ID=tenant-id'); + putenv('TRANS_AZURE_CLIENT_ID=client-id'); + putenv('TRANS_AZURE_CLIENT_SECRET=client-secret'); + putenv('TRANS_AZURE_KEY_VAULT_URL='); + putenv('TRANS_AZURE_KEY_VAULT_URL_EXTRA_BRATWURST=https://german-vault-url'); + + // null encryptorId + $wrapper = new $wrapperClass(new EncryptorOptions( + stackId: 'some-stack', + akvUrl: 'some-url', + encryptorId: null, + )); + self::assertNull($wrapper->getTransClient()); + + // encryptorId does not match env suffix ('extra-sausage' vs. _EXTRA_BRATWURST) + $wrapper = new $wrapperClass(new EncryptorOptions( + stackId: 'some-stack', + akvUrl: 'some-url', + encryptorId: 'extra-sausage', + )); + self::assertNull($wrapper->getTransClient()); + } +}