From 6a385c7879eaa61e47769586013680d97df4de70 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Tue, 7 Jan 2025 15:14:17 +0100 Subject: [PATCH 01/13] Added HashiCorp vault key provider --- composer.json | 3 +- os2web_key.services.yml | 25 ++- src/Exception/Exception.php | 10 + src/Exception/FileException.php | 10 + src/KeyHelper.php | 19 +- src/Plugin/KeyProvider/VaultKeyProvider.php | 236 ++++++++++++++++++++ src/Plugin/KeyType/CertificateKeyType.php | 2 +- src/Services/Psr16CacheAdapter.php | 108 +++++++++ src/Services/VaultCertificateHelper.php | 70 ++++++ 9 files changed, 473 insertions(+), 10 deletions(-) create mode 100644 src/Exception/Exception.php create mode 100644 src/Exception/FileException.php create mode 100644 src/Plugin/KeyProvider/VaultKeyProvider.php create mode 100644 src/Services/Psr16CacheAdapter.php create mode 100644 src/Services/VaultCertificateHelper.php diff --git a/composer.json b/composer.json index cb11b37..9fb50a8 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "php": "^8.1", "ext-openssl": "*", "drupal/core": "^9 || ^10", - "drupal/key": "^1.17" + "drupal/key": "^1.17", + "itk-dev/vault": "^0.1" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", diff --git a/os2web_key.services.yml b/os2web_key.services.yml index eca6b32..09d88b7 100644 --- a/os2web_key.services.yml +++ b/os2web_key.services.yml @@ -3,6 +3,29 @@ services: parent: logger.channel_base arguments: [ 'os2web_key' ] - Drupal\os2web_key\KeyHelper: + os2web_key.key_helper: + class: 'Drupal\os2web_key\KeyHelper' arguments: - '@logger.channel.os2web_key' + + os2web_key.certificate_key_type: + class: 'Drupal\os2web_key\Plugin\KeyType\CertificateKeyType' + arguments: + - '@os2web_key.key_helper' + + os2web_key.psr16_cache: + class: 'Drupal\os2web_key\Services\Psr16CacheAdapter' + arguments: + - '@cache.default' + + os2web_key.certificate_helper: + class: 'Drupal\os2web_key\Services\VaultCertificateHelper' + arguments: + - '@file_system' + + os2web_key.key_provider: + class: 'Drupal\os2web_key\Plugin\KeyProvider\VaultKeyProvider' + arguments: + - '@logger.channel.os2web_key' + - '@http_client' + - '@os2web_key.psr16_cache' diff --git a/src/Exception/Exception.php b/src/Exception/Exception.php new file mode 100644 index 0000000..74c7571 --- /dev/null +++ b/src/Exception/Exception.php @@ -0,0 +1,10 @@ +getKeyType(); if (!($type instanceof CertificateKeyType)) { throw $this->createSslRuntimeException(sprintf('Invalid key type: %s', $type::class), $key); } $contents = $key->getKeyValue(); - return $this->parseCertificates( - $contents, - $type->getInputFormat(), - $type->getPassphrase(), - $key - ); + if ($parseCertificate) { + return $this->parseCertificates( + $contents, + $type->getInputFormat(), + $type->getPassphrase(), + $key + ); + } + else { + return ['cert' => $contents]; + } } /** diff --git a/src/Plugin/KeyProvider/VaultKeyProvider.php b/src/Plugin/KeyProvider/VaultKeyProvider.php new file mode 100644 index 0000000..45e78f0 --- /dev/null +++ b/src/Plugin/KeyProvider/VaultKeyProvider.php @@ -0,0 +1,236 @@ +setLogger($logger); + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('logger.channel.default'), + $container->get('http_client'), + $container->get('os2web_key.psr16_cache'), + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + 'vault_path' => 'secret', + 'vault_secret' => '', + 'vault_key' => '', + 'vault_version' => NULL, + 'vault_cache_duration' => 60 * 60, + ]; + } + + /** + * {@inheritdoc} + */ + public function getKeyValue(KeyInterface $key) { + $roleId = Settings::get('itkdev_vault_role_id'); + $secretId = Settings::get('itkdev_vault_secret_id'); + + $vault = $this->getVault(); + + try { + $token = $vault->login( + $roleId, + $secretId, + ); + } + catch (\DateMalformedStringException | \DateMalformedIntervalStringException | UnknownErrorException | VaultException | InvalidArgumentException $e) { + $this->logger->error('Could not fetch login token when attempt to fetch key with id:' . $key->id()); + return ''; + } + $config = $this->configuration; + + $vaultPath = $config['vault_path']; + $vaultSecret = $config['vault_secret']; + $vaultKey = $config['vault_key']; + $vaultVersion = $config['vault_version']; + $vaultCacheDuration = $config['vault_cache_duration'] ?? 0; + $vaultUseCache = $vaultCacheDuration !== 0; + + try { + $secret = $vault->getSecret( + token: $token, + path: $vaultPath, + secret: $vaultSecret, + key: $vaultKey, + version: $vaultVersion, + useCache: $vaultUseCache, + expire: $vaultCacheDuration + ); + } + catch (\DateMalformedStringException | UnknownErrorException | VaultException | InvalidArgumentException $e) { + $this->logger->error('Could not fetch value from key:' . $key->id()); + return ''; + } + + return $secret->value; + } + + /** + * {@inheritDoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $providerConfig = $this->getConfiguration(); + + $form['vault_path'] = [ + '#type' => 'textfield', + '#title' => $this->t('Vault path'), + '#description' => $this->t('The Vault path'), + '#required' => TRUE, + '#default_value' => $providerConfig['vault_path'] ?? $this->defaultConfiguration()['vault_path'], + ]; + + $form['vault_secret'] = [ + '#type' => 'textfield', + '#title' => $this->t('Vault secret'), + '#description' => $this->t('The vault secret that should be used'), + '#required' => TRUE, + '#default_value' => $providerConfig['vault_secret'] ?? $this->defaultConfiguration()['vault_secret'], + ]; + + $form['vault_key'] = [ + '#type' => 'textfield', + '#title' => $this->t('Vault key'), + '#description' => $this->t('The key that should be fetched'), + '#required' => TRUE, + '#default_value' => $providerConfig['vault_key'] ?? $this->defaultConfiguration()['vault_key'], + ]; + + $form['vault_version'] = [ + '#type' => 'number', + '#title' => $this->t('Vault version'), + '#description' => $this->t('The version of key that should be fetched'), + '#default_value' => $providerConfig['vault_version'] ?? $this->defaultConfiguration()['vault_version'], + '#required' => FALSE, + ]; + + $form['vault_cache_duration'] = [ + '#type' => 'number', + '#title' => $this->t('Vault cache expiration duration'), + '#description' => $this->t('The amount of seconds version of key that should be fetched. Set to 0 to disable caching.'), + '#default_value' => $providerConfig['vault_cache_duration'] ?? $this->defaultConfiguration()['vault_cache_duration'], + '#required' => FALSE, + '#min' => 0, + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { + if (empty($form_state->getValue('vault_version'))) { + $form_state->setValue('vault_version', NULL); + } + + $vaultCacheDuration = $form_state->getValue('vault_cache_duration'); + if (empty($vaultCacheDuration) || $vaultCacheDuration < 0) { + $form_state->setValue('vault_cache_duration', 0); + } + + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + $this->setConfiguration($form_state->getValues()); + } + + /** + * Helper function to create vault client. + * + * @return \ItkDev\Vault\Vault + * The client + */ + private function getVault(): VaultClient { + static $vaultClient = NULL; + + $httpFactory = new HttpFactory(); + + if (is_null($vaultClient)) { + $vaultClient = new VaultClient( + httpClient: $this->httpClient, + requestFactory: $httpFactory, + streamFactory: $httpFactory, + cache: $this->cache, + vaultUrl: Settings::get('itkdev_vault_url'), + ); + } + + return $vaultClient; + } + +} diff --git a/src/Plugin/KeyType/CertificateKeyType.php b/src/Plugin/KeyType/CertificateKeyType.php index 4373fc4..4acd58c 100644 --- a/src/Plugin/KeyType/CertificateKeyType.php +++ b/src/Plugin/KeyType/CertificateKeyType.php @@ -56,7 +56,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get(KeyHelper::class) + $container->get('os2web_key.key_helper') ); } diff --git a/src/Services/Psr16CacheAdapter.php b/src/Services/Psr16CacheAdapter.php new file mode 100644 index 0000000..c5abb65 --- /dev/null +++ b/src/Services/Psr16CacheAdapter.php @@ -0,0 +1,108 @@ +cacheBackend = $cacheBackend; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = NULL): mixed { + $cache = $this->cacheBackend->get($key); + return $cache ? $cache->data : $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = NULL): bool { + try { + $this->cacheBackend->set($key, $value, $ttl); + return TRUE; + } + catch (\Exception $exception) { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function delete($key): bool { + try { + $this->cacheBackend->delete($key); + return TRUE; + } + catch (\Exception $exception) { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function clear(): bool { + try { + $this->cacheBackend->deleteAll(); + return TRUE; + } + catch (\Exception $exception) { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = NULL): iterable { + // Not implemented for simplicity. + throw new \Exception('Method not implemented.'); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = NULL): bool { + // Not implemented for simplicity. + throw new \Exception('Method not implemented.'); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys): bool { + // Not implemented for simplicity. + throw new \Exception('Method not implemented.'); + } + + /** + * {@inheritdoc} + */ + public function has($key): bool { + return $this->cacheBackend->get($key) !== FALSE; + } + +} diff --git a/src/Services/VaultCertificateHelper.php b/src/Services/VaultCertificateHelper.php new file mode 100644 index 0000000..bfdee0b --- /dev/null +++ b/src/Services/VaultCertificateHelper.php @@ -0,0 +1,70 @@ +certificatePath = $this->createTempCertificateFile($certificate); + } + + /** + * Ensure temporary certificate file is removed. + */ + public function __destruct() { + // Remove the certificate from disk. + if (file_exists($this->certificatePath)) { + unlink($this->certificatePath); + } + } + + /** + * Gets path to temporary certificate file. + */ + public function getCertificatePath(): string { + return $this->certificatePath; + } + + /** + * Creates a temporary file with certificate. + * + * @return string + * The temporary file path. + * + * @throws \Drupal\os2web_key\Exception\FileException + * File exception. + */ + private function createTempCertificateFile(string $certificate): string { + + $localCertFilename = tempnam($this->fileSystem->getTempDirectory(), 'vault_certificate'); + + if (!$localCertFilename) { + throw new FileException('Could not generate temporary certificate file.'); + } + + file_put_contents($localCertFilename, $certificate); + + return $localCertFilename; + } + +} From 3fcabf29c2e2557da7c5596f9bbbfa51e93b9082 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Tue, 7 Jan 2025 15:21:50 +0100 Subject: [PATCH 02/13] Upgraded php version --- .github/workflows/pr.yml | 6 +++--- README.md | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index ca98f73..452aaf7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1' ] + php-versions: [ '8.3' ] dependency-version: [ prefer-lowest, prefer-stable ] steps: - uses: actions/checkout@master @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1' ] + php-versions: [ '8.3' ] steps: - uses: actions/checkout@master - name: Setup PHP, with composer and extensions @@ -90,7 +90,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ '8.1' ] + php-versions: [ '8.3' ] steps: - uses: actions/checkout@master - name: Setup PHP, with composer and extensions diff --git a/README.md b/README.md index 002f7b9..9fbcf92 100644 --- a/README.md +++ b/README.md @@ -122,11 +122,11 @@ below to run the checks locally. ### PHP ```shell -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer install +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer install # Fix (some) coding standards issues -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer coding-standards-apply +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-apply # Check that code adheres to the coding standards -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer coding-standards-check +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-check ``` ### Markdown @@ -140,9 +140,9 @@ docker run --rm --volume $PWD:/md peterdavehello/markdownlint markdownlint --ign We use [PHPStan](https://phpstan.org/) for static code analysis. -Running statis code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the +Running static code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the analysis: ```shell -docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm ./scripts/code-analysis +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/code-analysis ``` From 3ee27b27cb945de68364f71f2f21a0e52186a075 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 16 Jan 2025 10:08:49 +0100 Subject: [PATCH 03/13] Unit tests and cleanup --- .github/workflows/pr.yml | 29 +++ CHANGELOG.md | 2 + composer.json | 9 +- os2web_key.services.yml | 1 + phpunit.xml | 105 ++++++++ scripts/unit-tests | 41 +++ src/KeyHelper.php | 50 ++-- .../KeyProvider/AzureKeyVaultKeyProvider.php | 211 +++++++++++++++ src/Plugin/KeyProvider/VaultKeyProvider.php | 32 ++- src/Plugin/KeyType/CertificateKeyType.php | 2 +- tests/src/Unit/KeyHelperUnitTest.php | 244 ++++++++++++++++++ 11 files changed, 700 insertions(+), 26 deletions(-) create mode 100644 phpunit.xml create mode 100755 scripts/unit-tests create mode 100644 src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php create mode 100644 tests/src/Unit/KeyHelperUnitTest.php diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 452aaf7..4ebe1dd 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -114,6 +114,35 @@ jobs: run: | ./scripts/code-analysis + php-unit-tests: + name: PHP unit tests + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: [ '8.3' ] + steps: + - uses: actions/checkout@master + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: json + coverage: none + tools: composer:v2 + # https://github.com/shivammathur/setup-php#cache-composer-dependencies + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + - name: Code analysis + run: | + ./scripts/unit-tests + coding-standards-markdown: name: Markdown coding standards runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dcae0a..5271aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* [PR-4](https://github.com/OS2web/os2web_key/pull/4) + Added HashiCorp Vault key provider * [PR-2](https://github.com/OS2web/os2web_key/pull/2) Updated documentation * [PR-1](https://github.com/OS2web/os2web_key/pull/1) diff --git a/composer.json b/composer.json index 9fb50a8..f340a75 100644 --- a/composer.json +++ b/composer.json @@ -19,10 +19,12 @@ "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "drupal/coder": "^8.3", + "drupal/core-dev": "^9 || ^10", "ergebnis/composer-normalize": "^2.42", "mglaman/phpstan-drupal": "^1.2", "phpstan/extension-installer": "^1.3", - "phpstan/phpstan-deprecation-rules": "^1.1" + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpunit/phpunit": "^9.6" }, "repositories": [ { @@ -30,6 +32,11 @@ "url": "https://packages.drupal.org/8" } ], + "autoload-dev": { + "psr-4": { + "Drupal\\Tests\\os2web_key\\": "tests/src/" + } + }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, diff --git a/os2web_key.services.yml b/os2web_key.services.yml index 09d88b7..95cc1fe 100644 --- a/os2web_key.services.yml +++ b/os2web_key.services.yml @@ -29,3 +29,4 @@ services: - '@logger.channel.os2web_key' - '@http_client' - '@os2web_key.psr16_cache' + - '@os2web_key.key_helper' diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..dad1e85 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ./tests/TestSuites/UnitTestSuite.php + + + ./tests/TestSuites/KernelTestSuite.php + + + ./tests/TestSuites/FunctionalTestSuite.php + + + ./tests/TestSuites/FunctionalJavascriptTestSuite.php + + + ./tests/TestSuites/BuildTestSuite.php + + + + + + + + + + ./includes + ./lib + ./modules + ../modules + ../sites + + + ./modules/*/src/Tests + ./modules/*/tests + ../modules/*/src/Tests + ../modules/*/tests + ../modules/*/*/src/Tests + ../modules/*/*/tests + ./lib/** + ./modules/** + ../modules/** + + + diff --git a/scripts/unit-tests b/scripts/unit-tests new file mode 100755 index 0000000..e601771 --- /dev/null +++ b/scripts/unit-tests @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +script_dir=$(pwd) +module_name=$(basename "$script_dir") +drupal_dir=vendor/drupal-module-code-analysis +# Relative to $drupal_dir +module_path=web/modules/contrib/$module_name + +cd "$script_dir" || exit + +drupal_composer() { + composer --working-dir="$drupal_dir" --no-interaction "$@" +} + +# Create new Drupal 10 project +if [ ! -f "$drupal_dir/composer.json" ]; then + composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir" +fi +# Copy our code into the modules folder +mkdir -p "$drupal_dir/$module_path" +# https://stackoverflow.com/a/15373763 +rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path" + +drupal_composer config minimum-stability dev + +# Allow ALL plugins +# https://getcomposer.org/doc/06-config.md#allow-plugins +drupal_composer config --no-plugins allow-plugins true + +# Making Drupal 10 compatible +drupal_composer require psr/http-message:^1.0 +drupal_composer require mglaman/composer-drupal-lenient +drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]' + +drupal_composer require wikimedia/composer-merge-plugin +drupal_composer config extra.merge-plugin.include "$module_path/composer.json" +# https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 +drupal_composer require --dev symfony/phpunit-bridge + + +# Run PHPUnit +(cd "$drupal_dir" && vendor/bin/phpunit "$module_path/tests/src/Unit/KeyHelperUnitTest.php") diff --git a/src/KeyHelper.php b/src/KeyHelper.php index 9c79645..c25658b 100644 --- a/src/KeyHelper.php +++ b/src/KeyHelper.php @@ -32,24 +32,14 @@ public function __construct( * @return array{cert: string, pkey: string} * The certificates. */ - public function getCertificates(KeyInterface $key, bool $parseCertificate = FALSE): array { + public function getCertificates(KeyInterface $key): array { $type = $key->getKeyType(); if (!($type instanceof CertificateKeyType)) { throw $this->createSslRuntimeException(sprintf('Invalid key type: %s', $type::class), $key); } - $contents = $key->getKeyValue(); - if ($parseCertificate) { - return $this->parseCertificates( - $contents, - $type->getInputFormat(), - $type->getPassphrase(), - $key - ); - } - else { - return ['cert' => $contents]; - } + return $this->parseCertificates($key->getKeyValue(), $type->getInputFormat(), $type->getPassphrase(), $key); + } /** @@ -90,7 +80,7 @@ public function getOidcValues(KeyInterface $key): array { * Parse certificates. * * @return array{cert: string, pkey: string} - * The certificates. + * The certificates, note that the certificate has no passphrase. */ public function parseCertificates( string $contents, @@ -102,6 +92,7 @@ public function parseCertificates( CertificateKeyType::CERT => NULL, CertificateKeyType::PKEY => NULL, ]; + switch ($format) { case CertificateKeyType::FORMAT_PFX: if (!openssl_pkcs12_read($contents, $certificates, $passphrase)) { @@ -114,30 +105,42 @@ public function parseCertificates( if (FALSE === $certificate) { throw $this->createSslRuntimeException('Error reading certificate', $key); } - if (!@openssl_x509_export($certificate, $certificates['cert'])) { + if (!@openssl_x509_export($certificate, $certificates[CertificateKeyType::CERT])) { throw $this->createSslRuntimeException('Error exporting x509 certificate', $key); } $pkey = @openssl_pkey_get_private($contents, $passphrase); if (FALSE === $pkey) { throw $this->createSslRuntimeException('Error reading private key', $key); } - if (!@openssl_pkey_export($pkey, $certificates['pkey'])) { + if (!@openssl_pkey_export($pkey, $certificates[CertificateKeyType::PKEY])) { throw $this->createSslRuntimeException('Error exporting private key', $key); } break; } if (!isset($certificates[CertificateKeyType::CERT], $certificates[CertificateKeyType::PKEY])) { - throw $this->createRuntimeException("Cannot read certificate parts 'cert' and 'pkey'", $key); + throw $this->createRuntimeException("Cannot read certificate parts CertificateKeyType::CERT and CertificateKeyType::PKEY", $key); } return $certificates; } /** - * Create a passwordless certificate. + * Converts certificates to format. + * + * @param array $certificates + * Output from parseCertificates. + * @param string $format + * Format to convert into. + * @param ?KeyInterface $key + * The key, for debugging purposes. + * + * @return string + * The converted certificate. + * + * @see self::parseCertificates() */ - public function createPasswordlessCertificate(array $certificates, string $format, ?KeyInterface $key): string { + public function convertCertificates(array $certificates, string $format, ?KeyInterface $key): string { $cert = $certificates[CertificateKeyType::CERT] ?? NULL; if (!isset($cert)) { throw $this->createRuntimeException('Certificate part "cert" not found', $key); @@ -196,7 +199,14 @@ public function createRuntimeException(string $message, ?KeyInterface $key, ?str * Create an SSL runtime exception. */ public function createSslRuntimeException(string $message, ?KeyInterface $key): RuntimeException { - return $this->createRuntimeException($message, $key, openssl_error_string() ?: NULL); + // @see https://www.php.net/manual/en/function.openssl-error-string.php. + $sslError = NULL; + + while ($errorMessage = openssl_error_string()) { + $sslError = $errorMessage; + } + + return $this->createRuntimeException($message, $key, $sslError); } } diff --git a/src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php b/src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php new file mode 100644 index 0000000..f4fe28a --- /dev/null +++ b/src/Plugin/KeyProvider/AzureKeyVaultKeyProvider.php @@ -0,0 +1,211 @@ +get('logger.channel.os2web_key'); + + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $logger + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration(): array { + return [ + self::TENANT_ID => '', + self::APPLICATION_ID => '', + self::CLIENT_SECRET => '', + self::NAME => '', + self::SECRET => '', + self::VERSION => '', + ]; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state): array { + $configuration = $this->getConfiguration(); + + $settings = [ + self::TENANT_ID => ['title' => $this->t('Tenant id')], + self::APPLICATION_ID => ['title' => $this->t('Application id')], + self::CLIENT_SECRET => ['title' => $this->t('Client secret')], + self::NAME => ['title' => $this->t('Name')], + self::SECRET => ['title' => $this->t('Secret')], + self::VERSION => ['title' => $this->t('Version')], + ]; + + foreach ($settings as $key => $info) { + $form[$key] = [ + '#type' => 'textfield', + '#title' => $info['title'], + '#default_value' => $configuration[$key] ?? NULL, + '#required' => TRUE, + ]; + } + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void { + try { + $this->getCertificate([ + self::TENANT_ID => $form_state->getValue(self::TENANT_ID), + self::APPLICATION_ID => $form_state->getValue(self::APPLICATION_ID), + self::CLIENT_SECRET => $form_state->getValue(self::CLIENT_SECRET), + self::NAME => $form_state->getValue(self::NAME), + self::SECRET => $form_state->getValue(self::SECRET), + self::VERSION => $form_state->getValue(self::VERSION), + ]); + } + catch (\Throwable $throwable) { + $form_state->setError($form, $this->t('Error getting certificate: %message', ['%message' => $throwable->getMessage()])); + } + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void { + $this->setConfiguration($form_state->getValues()); + } + + /** + * {@inheritdoc} + */ + public function getKeyValue(KeyInterface $key): ?string { + try { + return $this->getCertificate(); + } + catch (\Throwable $throwable) { + } + + return NULL; + } + + /** + * Get certificate. + * + * @return string + * The certificate. + * + * @throws \ItkDev\AzureKeyVault\Exception\TokenException + * @throws \ItkDev\Serviceplatformen\Certificate\Exception\AzureKeyVaultCertificateLocatorException + */ + private function getCertificate(?array $configuration = NULL): string { + try { + $httpClient = new GuzzleAdapter(new Client()); + $requestFactory = new RequestFactory(); + + $vaultToken = new VaultToken($httpClient, $requestFactory); + + $options = $configuration ?? $this->getConfiguration(); + + $token = $vaultToken->getToken( + $options[self::TENANT_ID], + $options[self::APPLICATION_ID], + $options[self::CLIENT_SECRET], + ); + + $vault = new VaultSecret( + $httpClient, + $requestFactory, + $options[self::NAME], + $token->getAccessToken() + ); + + $locator = new AzureKeyVaultCertificateLocator( + $vault, + $options[self::SECRET], + $options[self::VERSION], + ); + + return $locator->getCertificate(); + } + catch (\Exception $exception) { + // Log the exception and re-throw it. + $this->logger->error('Error getting certificate: @message', [ + '@message' => $exception->getMessage(), + 'throwable' => $exception, + ]); + throw $exception; + } + } + +} diff --git a/src/Plugin/KeyProvider/VaultKeyProvider.php b/src/Plugin/KeyProvider/VaultKeyProvider.php index 45e78f0..c831e98 100644 --- a/src/Plugin/KeyProvider/VaultKeyProvider.php +++ b/src/Plugin/KeyProvider/VaultKeyProvider.php @@ -8,6 +8,8 @@ use Drupal\key\KeyInterface; use Drupal\key\Plugin\KeyPluginFormInterface; use Drupal\key\Plugin\KeyProviderBase; +use Drupal\os2web_key\KeyHelper; +use Drupal\os2web_key\Plugin\KeyType\CertificateKeyType; use GuzzleHttp\Psr7\HttpFactory; use ItkDev\Vault\Exception\UnknownErrorException; use ItkDev\Vault\Exception\VaultException; @@ -50,6 +52,8 @@ final class VaultKeyProvider extends KeyProviderBase implements KeyPluginFormInt * A http client. * @param \Psr\SimpleCache\CacheInterface $cache * A PSR-16 Cache service. + * @param \Drupal\os2web_key\KeyHelper $keyHelper + * The key helper. */ public function __construct( array $configuration, @@ -58,6 +62,7 @@ public function __construct( LoggerChannelInterface $logger, private readonly ClientInterface $httpClient, private readonly CacheInterface $cache, + private readonly KeyHelper $keyHelper, ) { $this->setLogger($logger); parent::__construct($configuration, $plugin_id, $plugin_definition); @@ -74,6 +79,7 @@ public static function create(ContainerInterface $container, array $configuratio $container->get('logger.channel.default'), $container->get('http_client'), $container->get('os2web_key.psr16_cache'), + $container->get('os2web_key.key_helper'), ); } @@ -106,9 +112,16 @@ public function getKeyValue(KeyInterface $key) { ); } catch (\DateMalformedStringException | \DateMalformedIntervalStringException | UnknownErrorException | VaultException | InvalidArgumentException $e) { - $this->logger->error('Could not fetch login token when attempt to fetch key with id:' . $key->id()); - return ''; + // Log the exception and re-throw it. + $this->logger->error('Error fetching login token for key (@key_id): @message', [ + '@key_id' => $key->id(), + '@message' => $e->getMessage(), + 'throwable' => $e, + ]); + + throw $e; } + $config = $this->configuration; $vaultPath = $config['vault_path']; @@ -130,8 +143,19 @@ public function getKeyValue(KeyInterface $key) { ); } catch (\DateMalformedStringException | UnknownErrorException | VaultException | InvalidArgumentException $e) { - $this->logger->error('Could not fetch value from key:' . $key->id()); - return ''; + // Log the exception and re-throw it. + $this->logger->error('Error getting certificate for key (@key_id): @message', [ + '@key_id' => $key->id(), + '@message' => $e->getMessage(), + 'throwable' => $e, + ]); + + throw $e; + } + + $type = $key->getKeyType(); + if (!($type instanceof CertificateKeyType)) { + throw $this->keyHelper->createSslRuntimeException(sprintf('Invalid key type: %s', $type::class), $key); } return $secret->value; diff --git a/src/Plugin/KeyType/CertificateKeyType.php b/src/Plugin/KeyType/CertificateKeyType.php index 4acd58c..acd3f6a 100644 --- a/src/Plugin/KeyType/CertificateKeyType.php +++ b/src/Plugin/KeyType/CertificateKeyType.php @@ -143,7 +143,7 @@ public function validateKeyValue(array $form, FormStateInterface $form_state, $k } try { - $this->certificateHelper->createPasswordlessCertificate($certificates, $outputFormat, NULL); + $this->certificateHelper->convertCertificates($certificates, $outputFormat, NULL); } catch (RuntimeException $exception) { $form_state->setError($form, $this->t('Error creating passwordless certificate: @message', ['@message' => $exception->getMessage()])); diff --git a/tests/src/Unit/KeyHelperUnitTest.php b/tests/src/Unit/KeyHelperUnitTest.php new file mode 100644 index 0000000..e8f19a5 --- /dev/null +++ b/tests/src/Unit/KeyHelperUnitTest.php @@ -0,0 +1,244 @@ +mockLogger = $this->createMock(LoggerChannelInterface::class); + $this->keyHelper = new KeyHelper($this->mockLogger); + + } + + /** + * Tests creation of KeyHelper. + */ + public function testKeyHelperCreation() { + $this->assertInstanceOf(KeyHelper::class, $this->keyHelper); + } + + /** + * Tests creation of RuntimeException no SSL message. + */ + public function testCreateRuntimeExceptionNoSslMessage() { + $this->mockLogger->expects($this->once())->method('error'); + $message = 'test'; + $result = $this->keyHelper->createRuntimeException($message, NULL); + $this->assertEquals($message, $result->getMessage()); + } + + /** + * Tests creation of RuntimeException no SSL message. + */ + public function testCreateRuntimeExceptionWithSslMessage() { + $this->mockLogger->expects($this->once())->method('error'); + $message = 'test'; + $excepted = $message . ' (some SSL error)'; + $result = $this->keyHelper->createRuntimeException($message, NULL, 'some SSL error'); + $this->assertEquals($excepted, $result->getMessage()); + } + + /** + * Tests creation of RuntimeException no SSL message. + */ + public function testCreateRuntimeExceptionWithKeyAndSslMessage() { + $mockMessage = 'test'; + $exceptedMessage = $mockMessage . ' (some SSL error)'; + $mockKeyId = 'some_key_id'; + $this->mockLogger->expects($this->once())->method('error')->with('@key: @message', [ + '@key' => $mockKeyId, + '@message' => $exceptedMessage, + ]); + $mockKey = $this->createMock('Drupal\key\KeyInterface'); + $mockKey->expects($this->once())->method('id')->willReturn($mockKeyId); + $result = $this->keyHelper->createRuntimeException($mockMessage, $mockKey, 'some SSL error'); + $this->assertEquals($exceptedMessage, $result->getMessage()); + } + + /** + * Data provider for parse certificates test. + */ + public static function parseCertificatesDataProvider(): \Generator { + yield [ + 'test_without_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + '', + [ + CertificateKeyType::CERT => 'p12_without_passphrase_cert.txt', + CertificateKeyType::PKEY => 'p12_without_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_with_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + 'test', + [ + CertificateKeyType::CERT => 'p12_with_passphrase_cert.txt', + CertificateKeyType::PKEY => 'p12_with_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_without_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + '', + [ + CertificateKeyType::CERT => 'pem_without_passphrase_cert.txt', + CertificateKeyType::PKEY => 'pem_without_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + 'test', + [ + CertificateKeyType::CERT => 'pem_with_passphrase_cert.txt', + CertificateKeyType::PKEY => 'pem_with_passphrase_pkey.txt', + ], + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + 'wrong_passphrase', + // @command openssl pkey -in test_with_passphrase.pem -passin pass:wrong_passphrase + new RuntimeException('Error reading private key (error:11800074:PKCS12 routines::pkcs12 cipherfinal error)'), + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PFX, + 'some_non_important_passphrase', + // @command openssl pkcs12 -in test_with_passphrase.pem -passin pass:some_non_important_passphrase + new RuntimeException('Error reading certificate (error:0688010A:asn1 encoding routines::nested asn1 error)'), + ]; + } + + /** + * Tests parse certificates. + * + * @dataProvider parseCertificatesDataProvider + */ + public function testParseCertificates(string $certificate, string $format, string $passphrase, array|RuntimeException $expected) { + + if ($expected instanceof RuntimeException) { + $this->expectException($expected::class); + $this->expectExceptionMessage($expected->getMessage()); + } + + $certificates = $this->keyHelper->parseCertificates(file_get_contents(__DIR__ . '/certificates/' . $certificate), $format, $passphrase, NULL); + + // Assert certificate. + $this->assertEquals(file_get_contents(__DIR__ . '/certificates/' . $expected[CertificateKeyType::CERT]), $certificates[CertificateKeyType::CERT]); + // Assert private key. + $this->assertEquals(file_get_contents(__DIR__ . '/certificates/' . $expected[CertificateKeyType::PKEY]), $certificates[CertificateKeyType::PKEY]); + } + + /** + * Data provider for parse certificates test. + */ + public static function parsedAndConvertedCertificatesAreEqualProvider(): \Generator { + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PEM, + 'test', + ]; + + yield [ + 'test_with_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PFX, + 'test', + ]; + + yield [ + 'test_without_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PEM, + '', + ]; + + yield [ + 'test_without_passphrase.pem', + CertificateKeyType::FORMAT_PEM, + CertificateKeyType::FORMAT_PFX, + '', + ]; + + yield [ + 'test_with_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PFX, + 'test', + ]; + + yield [ + 'test_with_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PEM, + 'test', + ]; + + yield [ + 'test_without_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PFX, + '', + ]; + + yield [ + 'test_without_passphrase.p12', + CertificateKeyType::FORMAT_PFX, + CertificateKeyType::FORMAT_PEM, + '', + ]; + } + + /** + * Tests conversion of certificates. + * + * @dataProvider parsedAndConvertedCertificatesAreEqualProvider + */ + public function testParsedAndConvertedCertificatesAreEqual(string $certificate, string $inputFormat, string $outputFormat, string $passphrase) { + $certificates = $this->keyHelper->parseCertificates(file_get_contents(__DIR__ . '/certificates/' . $certificate), $inputFormat, $passphrase, NULL); + $converted = $this->keyHelper->convertCertificates($certificates, $outputFormat, NULL); + + // Test that converted (now passwordless) certificate yields same result as + // original parsed certificate. + $convertedParsedCertificates = $this->keyHelper->parseCertificates($converted, $outputFormat, '', NULL); + + $this->assertEquals($certificates, $convertedParsedCertificates); + } + +} From 30829a11f887a7e1a73122c50b71bfb905867f3f Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 16 Jan 2025 11:08:49 +0100 Subject: [PATCH 04/13] Update code analysis script to use D10 --- .github/workflows/pr.yml | 2 +- composer.json | 7 +++++++ scripts/code-analysis | 26 ++++++++++---------------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4ebe1dd..1f57a6f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -139,7 +139,7 @@ jobs: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- - - name: Code analysis + - name: Unit tests run: | ./scripts/unit-tests diff --git a/composer.json b/composer.json index f340a75..f13f12b 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,13 @@ "sort-packages": true }, "scripts": { + "code-analysis": [ + "@code-analysis/drupal-check" + ], + "code-analysis/drupal-check": [ + "# @see https://github.com/mglaman/drupal-check/issues/261#issuecomment-1030141772 for details on exclude-dir value", + "drupal-check --deprecations --analysis --exclude-dir='vendor' *.* src" + ], "coding-standards-apply": [ "@coding-standards-apply/phpcs" ], diff --git a/scripts/code-analysis b/scripts/code-analysis index 5a3c1e2..91957ba 100755 --- a/scripts/code-analysis +++ b/scripts/code-analysis @@ -11,26 +11,14 @@ drupal_composer() { composer --working-dir="$drupal_dir" --no-interaction "$@" } -# Create new Drupal 9 project +# Create new Drupal 10 project if [ ! -f "$drupal_dir/composer.json" ]; then - composer --no-interaction create-project drupal/recommended-project:^9 "$drupal_dir" + composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir" fi # Copy our code into the modules folder - -# Clean up -rm -fr "${drupal_dir:?}/$module_path" - +mkdir -p "$drupal_dir/$module_path" # https://stackoverflow.com/a/15373763 -# rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path" - -# The rsync command in not available in itkdev/php8.1-fpm - -git config --global --add safe.directory /app -# Copy module files into module path -for f in $(git ls-files); do - mkdir -p "$drupal_dir/$module_path/$(dirname "$f")" - cp "$f" "$drupal_dir/$module_path/$f" -done +rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path" drupal_composer config minimum-stability dev @@ -38,10 +26,16 @@ drupal_composer config minimum-stability dev # https://getcomposer.org/doc/06-config.md#allow-plugins drupal_composer config --no-plugins allow-plugins true +# Making Drupal 10 compatible +drupal_composer require psr/http-message:^1.0 +drupal_composer require mglaman/composer-drupal-lenient +drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]' + drupal_composer require wikimedia/composer-merge-plugin drupal_composer config extra.merge-plugin.include "$module_path/composer.json" # https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 drupal_composer require --dev symfony/phpunit-bridge + # Run PHPStan (cd "$drupal_dir/$module_path" && ../../../../vendor/bin/phpstan) From b1e56f545322f57e82c0dac0a4b5eb871f15f998 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 16 Jan 2025 17:05:02 +0100 Subject: [PATCH 05/13] Update coding standards and add missing test files --- .github/workflows/pr.yml | 2 + composer.json | 1 + scripts/code-analysis | 3 + scripts/unit-tests | 8 +- src/Services/Psr16CacheAdapter.php | 5 ++ .../certificates/p12_with_passphrase_cert.txt | 30 +++++++ .../certificates/p12_with_passphrase_pkey.txt | 52 +++++++++++ .../p12_without_passphrase_cert.txt | 30 +++++++ .../p12_without_passphrase_pkey.txt | 52 +++++++++++ .../certificates/pem_with_passphrase_cert.txt | 30 +++++++ .../certificates/pem_with_passphrase_pkey.txt | 52 +++++++++++ .../pem_without_passphrase_cert.txt | 30 +++++++ .../pem_without_passphrase_pkey.txt | 52 +++++++++++ .../certificates/test_with_passphrase.p12 | Bin 0 -> 4211 bytes .../certificates/test_with_passphrase.pem | 84 ++++++++++++++++++ .../certificates/test_without_passphrase.p12 | Bin 0 -> 4211 bytes .../certificates/test_without_passphrase.pem | 82 +++++++++++++++++ 17 files changed, 510 insertions(+), 3 deletions(-) create mode 100644 tests/src/Unit/certificates/p12_with_passphrase_cert.txt create mode 100644 tests/src/Unit/certificates/p12_with_passphrase_pkey.txt create mode 100644 tests/src/Unit/certificates/p12_without_passphrase_cert.txt create mode 100644 tests/src/Unit/certificates/p12_without_passphrase_pkey.txt create mode 100644 tests/src/Unit/certificates/pem_with_passphrase_cert.txt create mode 100644 tests/src/Unit/certificates/pem_with_passphrase_pkey.txt create mode 100644 tests/src/Unit/certificates/pem_without_passphrase_cert.txt create mode 100644 tests/src/Unit/certificates/pem_without_passphrase_pkey.txt create mode 100644 tests/src/Unit/certificates/test_with_passphrase.p12 create mode 100644 tests/src/Unit/certificates/test_with_passphrase.pem create mode 100644 tests/src/Unit/certificates/test_without_passphrase.p12 create mode 100644 tests/src/Unit/certificates/test_without_passphrase.pem diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 1f57a6f..9899dbe 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -139,6 +139,8 @@ jobs: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 - name: Unit tests run: | ./scripts/unit-tests diff --git a/composer.json b/composer.json index f13f12b..d538dfc 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "ext-openssl": "*", "drupal/core": "^9 || ^10", "drupal/key": "^1.17", + "itk-dev/serviceplatformen": "^1.6", "itk-dev/vault": "^0.1" }, "require-dev": { diff --git a/scripts/code-analysis b/scripts/code-analysis index 91957ba..0435ebd 100755 --- a/scripts/code-analysis +++ b/scripts/code-analysis @@ -22,6 +22,8 @@ rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" -- drupal_composer config minimum-stability dev +drupal_composer --append repositories.os2web/os2web_key path "$module_path" + # Allow ALL plugins # https://getcomposer.org/doc/06-config.md#allow-plugins drupal_composer config --no-plugins allow-plugins true @@ -35,6 +37,7 @@ drupal_composer require wikimedia/composer-merge-plugin drupal_composer config extra.merge-plugin.include "$module_path/composer.json" # https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 drupal_composer require --dev symfony/phpunit-bridge +drupal_composer --no-interaction install # Run PHPStan diff --git a/scripts/unit-tests b/scripts/unit-tests index e601771..9fca774 100755 --- a/scripts/unit-tests +++ b/scripts/unit-tests @@ -1,7 +1,7 @@ #!/usr/bin/env bash script_dir=$(pwd) module_name=$(basename "$script_dir") -drupal_dir=vendor/drupal-module-code-analysis +drupal_dir=vendor/drupal-module-unit-tests-analysis # Relative to $drupal_dir module_path=web/modules/contrib/$module_name @@ -22,6 +22,8 @@ rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" -- drupal_composer config minimum-stability dev +drupal_composer --append repositories.os2web/os2web_key path "$module_path" + # Allow ALL plugins # https://getcomposer.org/doc/06-config.md#allow-plugins drupal_composer config --no-plugins allow-plugins true @@ -35,7 +37,7 @@ drupal_composer require wikimedia/composer-merge-plugin drupal_composer config extra.merge-plugin.include "$module_path/composer.json" # https://www.drupal.org/project/drupal/issues/3220043#comment-14845434 drupal_composer require --dev symfony/phpunit-bridge - +drupal_composer --no-interaction install # Run PHPUnit -(cd "$drupal_dir" && vendor/bin/phpunit "$module_path/tests/src/Unit/KeyHelperUnitTest.php") +(cd "$drupal_dir" && vendor/bin/phpunit --configuration web/core/phpunit.xml.dist "$module_path/tests/src/Unit/KeyHelperUnitTest.php") diff --git a/src/Services/Psr16CacheAdapter.php b/src/Services/Psr16CacheAdapter.php index c5abb65..577c8d3 100644 --- a/src/Services/Psr16CacheAdapter.php +++ b/src/Services/Psr16CacheAdapter.php @@ -84,6 +84,11 @@ public function getMultiple($keys, $default = NULL): iterable { /** * {@inheritdoc} + * + * @param iterable $values + * A list of key => value pairs for a multiple-set operation. + * @param \DateInterval|int|null $ttl + * Optional. The TTL value of this item. */ public function setMultiple($values, $ttl = NULL): bool { // Not implemented for simplicity. diff --git a/tests/src/Unit/certificates/p12_with_passphrase_cert.txt b/tests/src/Unit/certificates/p12_with_passphrase_cert.txt new file mode 100644 index 0000000..807c385 --- /dev/null +++ b/tests/src/Unit/certificates/p12_with_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUKvQXv3tnDrA/aa2skX93GLIuY0UwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzNDM5WhcNMjYw +MTE0MDkzNDM5WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAOft02M4VwGemD5YbWldJbirf1vXTfkrJPTLkT4y +udUFjrksQ7Ar24JdW0jfOAxcZS1J5AYK+JdCpYqOJJvEUNO5nN14qiBiMde06le4 +61mAaM56U6ZrMLE2KwJeYORoMU4ZIW9+x5IeOk/rxZagk3Og1ZJlkV5s5ZGQRS+/ ++JQTcQKZ5ts46QHJLjpSEM1WYp24EaPjFUkNwpLAgU+YhSiQcVvVPa0Doawct9uO +QdKlym1vo2wsGevTnqbbaEZfQonjIkxnG2kpNkUXs7dw+Sc5zgJJc/E21fJMB6hc +UBBGcGC0yWDA3bbp77adE0nZAQUmk+Of4RmZCjriFUVsrKLEebxrBLzdEGF2KGjT +fmyF0ZmxVZlQRhLAx1ofRWTvrs7cySxGGpvqJjjASqdP11FvWy7pCt+TZ7t+FaMH +NFxsYmtHpWUek+saKr55HPJ5xzeuc99RCR8+FFxJ41XnFbjW0OaDcA7s7LS55uv+ +cOJoJ3QyfgZhS+kZ6OC++FBEnT+UTukrDkd73LYjF5R/tCXgL8ixBps5pNO4sJiJ +MpkCqwxKBuUmicxBvNsRar7fWJBdGQ2B1LdsBxPH42rRyKk82SZcpEr1iRmpCeeU +yZFfz1aDkb29idWGr70qBB3KSJ8eP+tu6q2Qn0J9s5nHrqKjMOnScFWSen0Clu2H +y/a3AgMBAAGjUzBRMB0GA1UdDgQWBBRbrAyINQ3uSX8Yz7jhlR6EPvPQhTAfBgNV +HSMEGDAWgBRbrAyINQ3uSX8Yz7jhlR6EPvPQhTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCUNDbywMGcSedFF7jM/1d2A10ZfKCNAPYCSPoNK8Pd +OstRM+7n7p4zRuT+2p8YjpTwxPpj/2o346lZ86WgAjwBgG2JQsu4IHyE4Hy6odYG +bbMGD5rQmRKF0haksIAAVEY9XjPtxnn8/0jOiMC1D1aLTNH6B+iG5XwPpTynd0bZ +Z4zR8AvbpjxViDifGeYbTmqYFqLqxJXD0eQAGIin5DkfXPVoaOaMoDLlJZs317px +xk3RfcQgII4i98mFauIIu0a40f358zT96M46s1kOAuVgXRtk8MbmdsLzRrMRingY +iKOegOlc6ImR3IRX7RByxBBF0J77OCX1aAniBJaKcjOgrzbhzrEz8bXTEThKqjhq +w/OM0I92GvG3L/CU7DXecRXmvuD014RO/IeCP5TgMjWxJH2teax8SoCKP2LY3sJw +P5NtcBbOtABGFFTNFhq7usjz/SkoqDoF2WNJvgGTQKWJBStGTVX7+8MLypE2wGRd +YONzzEy6YaxlPbcUXPlfCHLeXmKgO8oOP0vc81yFuWW/BpVnyWDlCWhZpzOT1Zy8 +SebM8atRPwy8TvyPLBOcd96521fvE+nklG0mFfAftwMGPVus9314vSsuchO0E3ww +BUB3WgLv+F0kus6TRokntQaKuu1SzJsAbEPCs6SfjPKXg6LQdXRSEb4RaYfo0VDJ +3w== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/p12_with_passphrase_pkey.txt b/tests/src/Unit/certificates/p12_with_passphrase_pkey.txt new file mode 100644 index 0000000..b5585ac --- /dev/null +++ b/tests/src/Unit/certificates/p12_with_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDn7dNjOFcBnpg+ +WG1pXSW4q39b1035KyT0y5E+MrnVBY65LEOwK9uCXVtI3zgMXGUtSeQGCviXQqWK +jiSbxFDTuZzdeKogYjHXtOpXuOtZgGjOelOmazCxNisCXmDkaDFOGSFvfseSHjpP +68WWoJNzoNWSZZFebOWRkEUvv/iUE3ECmebbOOkByS46UhDNVmKduBGj4xVJDcKS +wIFPmIUokHFb1T2tA6GsHLfbjkHSpcptb6NsLBnr056m22hGX0KJ4yJMZxtpKTZF +F7O3cPknOc4CSXPxNtXyTAeoXFAQRnBgtMlgwN226e+2nRNJ2QEFJpPjn+EZmQo6 +4hVFbKyixHm8awS83RBhdiho035shdGZsVWZUEYSwMdaH0Vk767O3MksRhqb6iY4 +wEqnT9dRb1su6Qrfk2e7fhWjBzRcbGJrR6VlHpPrGiq+eRzyecc3rnPfUQkfPhRc +SeNV5xW41tDmg3AO7Oy0uebr/nDiaCd0Mn4GYUvpGejgvvhQRJ0/lE7pKw5He9y2 +IxeUf7Ql4C/IsQabOaTTuLCYiTKZAqsMSgblJonMQbzbEWq+31iQXRkNgdS3bAcT +x+Nq0cipPNkmXKRK9YkZqQnnlMmRX89Wg5G9vYnVhq+9KgQdykifHj/rbuqtkJ9C +fbOZx66iozDp0nBVknp9Apbth8v2twIDAQABAoICABQzTcNceSnuC/+RVYVx55YU +Jdi0QeY7+kRYZXM5f5/Zeir2e0/6uewHjcRYLaClL+phmOE0qNGjErNxmigRBLzN +wI2cc8dNr8URMybNBEtvpXFA2UyE4iEezfpiVjKx2oPqKQd16OCSqAYlZTiV2tHq +EvMxzBc7CXgdgARexlu+h78v8802dNQpfbb2eXn3YxZ7akBQFOufrbASrhCZmnt3 +SgOAHEn99Ef/DL2sQCwEy0LiqguDADnFXRIJlfIownn4O2WhLPno7RrYEiVqyxPX +pot8P1A/Z1bRUj3S1uX1l9ZS2LlUtkFQuQa6qDi5FF9dxhxZmAVro9opR9+bzaG8 +xatoo0DELirbiZCZSMDGvI+nR9pDPdUQ2+qcKLSHgTnVz/VR+fMi8a/+Ch6HH6JU +dHG7Xc2OqtOr6gTrqmSD3PYE3S93ydKO5l8SoM35bhz2SfLUMtClWF4QFweF+SU/ +dbo0RLtZmhA+C1KenkVgCW0RZd+NUd053x1igN6wlZY8CsgOH9yIer3hW5DKmREZ +1Fl/OrsZFwJpNOm7BhpJ9YL1GmoltXZeascz5l6anKAQspCeC1jd21JsaRoDH6+U +7lbmMqLUmO+QmKFqN1NwlBpFLlHPFYN7eeR5vdaKsJMux0ks5IZ5hoiMVCYtCuW5 +qN25P7Bq8/O3yiCIkG/FAoIBAQD7MOQ1k0D/n/2HsLQj+UX7CDpA2kpSValUxdoP +zy8izXrc8ekgb4q4BROYVuF6wvz1/m2ck1LKM+DNBMd4QSTl5qe7TyHez2CsGY4Q +crA4nrzZYS6uqpl5zCq1wwa9WILq5D6dWLN1WpI+0uBFSPlwPrHnhbG5ISVbUt1i +qzK5+WyEw8wx0miDnsjFZafGkq94dqTUYscpuph/nnFFgeuI/EphY/hwBxS5xNRM +yz4qSyCDYNFuORN3TxEsM5epAFC7v3Oi064hNtjzQ4zud7evQvUgtNYz/DX37SKA +zZvFs7cDZq6NH+MrMqkiXPIJr1xyBDpGzOcdNkD6xsQXGY2lAoIBAQDsXoeaJd+d +msVB05uap30GKKdshXJmb7tlfqELCfyVA60DWAVhUpgHXYTEeSB95BxkNoEcaQNy +lAsvP2Zg+IKIMp8EltRoNmwtuLsl2cjiQlgld9FzysMzub7PAPR70Ddse+GdVPJs +/9rMivMr3Ghlbw2g3LTLaIcqexViZe9U6G7T0Dq50BMtEfR7xduQ1OkcJ9asieQn +JEY4FUqFzxIoVPO1OSo/onYzf73iuytu29S6L2/c4FteTmWwyppW7EF12fZntYXX +zrtLDIczPIyc26J0BJdkjn/T6Ka93XUVE4LH2CQfQKiWSnPaBbZeHwKt07Yg6Wfa +vygdq5iHzLwrAoIBAQCaN44khl+lkWhqYkZ2v5gDYy3Am0R+ADGR5lbW1fCugVEp +TSw/ybt090jsgoq0f/omXSqWsqxlkCw4MvL0Faq9qzrXeOHmlZNYlefumTsvx+VR +4KR0gDDx4jNmnHIK1ciqxa7bk5W6Ov0GrRkb8I2UKCm5Zw+SBZOV2e4pocnm/Adw +C3XWMMQR6u6AosS62Vv5rX3NF0gHipQN5gzNC9K7q4lO+b2RqgdDdmCRNUbZWH6B +qnNqJIia+2Qjqxk1nQfz/LrWAPgrVC0mTRa9YarYY1dz2RKZoALSUDias6ErZAa8 +g9IuuqER6GyGuGGxKIStS4Mr7KkH+kMys824EOwxAoIBAQDhP/xNo8YDtVhBsNxa +dz4nvlo+irVIdTXKNPBXSr8Pap9XzmviIQmMC0zYxu11ywpbXa2QW2rTp804y1xP +EMOj80mi3/GvFh3lPGPsYCyVoc/KdPJ/PDzlcB8ruadCzlvi3/NTMX6M7Q2fWtL9 +e19pOnlsUKdiKEyW6RCp4qY+7fmflJJCPM3pZ3Dq+tSQorU7BaQIbKelvWXFUFTi +S0Dsenh8vOtMFRA7Z4KXLPLpBEkm3MnnXU71AhTac1i9eQuwuXOdxVi1HedST7AP +3aXKXWvkz/NN+pZKZS1xRjerTBqXQ5y4w69k+bKJlGQEMd0ltdmcZ5F23i/mhn4m ++65dAoIBAHRb5qsc3euMoHRK3hSdQa4Rhcw5rUnNX3LfQ+QJGOmUKH61eRt0akx1 +0VygnBmULU8xN8cDDUsx44zsVCowe93pFiXzAOCzPRD2L8QIX+fcHeIDqUDTlPwb +mbhgzyQcUpqGCLGAU/VQphs7UxfLM4EfHbI3X7FV8k4ELJnz7X5UqmYoxblbLYSU +lzb6uj5jvu3xZg7qFAgOJculqaa7se0Qssq/0hvOwSVEJU1TEj+3oM/lIjSwYgA0 +uv5FkIZjlt1mO9fiHbbpS8f0lwmNI3qtco1+T8GWTEjiwDsleape0ZrntwMkx9O2 +7nZr/JN7tErXucFV+4c34sUTvgHOLsM= +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/p12_without_passphrase_cert.txt b/tests/src/Unit/certificates/p12_without_passphrase_cert.txt new file mode 100644 index 0000000..893f4c2 --- /dev/null +++ b/tests/src/Unit/certificates/p12_without_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUHr62vcO7axcfps7XoWV2DcL5l/YwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzNjE3WhcNMjYw +MTE0MDkzNjE3WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAMQL/GhI14ngaVWCT3RhUdzcxrw2q9fJRTcEwSX1 +AUjXukJaRTUmJp4qm8bSfUu5wzEb+Dqbe44d3yUykmpMk+ZVuylrWoREpJ/dyVgK +EthGRNU7svLRd8z1sCF4HQcLqfqnXqQAKDoyhugF3R/ED+n5S7agmMj2bL/4tncN +62mUThcsNGiJ/pkbchEtWyBb7xcxmtfoGOPTPUOuhi0lfxoux+fNy+m3g5/7DTky +xJ+M/9HUbCG1CMNNS3+w3Aeyb/CdvWOEI4pVxQxcd30t6s0mP27wDdwSB0PdAlcs +gdUL3GucnMjDDKtJUjCfDHcsVylzvMZuyjGFKFg90HBYsjSXBGmk8bb8LvZGuDjI +s2u3frz7kit/1/WBs+Na5qkl11uvzfbuFM/tYSSgi43vjELJIDd60YvVnEkV88O2 +MU0wQH7sQMAasVKV388eD+SsmHTx6yvHUL1mKXQ021ZJyo6toouDaFWCXX3gK/kx +gWOmvrytWupXw/v5JT07SWAEk14OaejFMsE5ydbgjRdeT3vAmCkrVv1ZA1dlOkQq +5tMYmAastIw7JUwQz4ACQtCSbrOOnFgbDt+sKmLAOKqmVFIhULOrZxqVmVT8OuUj +/KatvoXh6iYtSApJE7NgEaH8+KTUTscsqo9oIcQ/FvPwGZDJBq0eQH+V2eKcWMrx +jvllAgMBAAGjUzBRMB0GA1UdDgQWBBTy4BOkfWFkOZ5wjZTmsVjyLM+zmjAfBgNV +HSMEGDAWgBTy4BOkfWFkOZ5wjZTmsVjyLM+zmjAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQAt21NBll90kV9Ithzg7GwD0AoxFZR4MpUVmk35QD26 +zR0rh/XjUTIcdhN5xn7+0tZ/k6GyrgiVTR61vgxDVM0AOKhiSLfnCW5fBG/yUX80 +L4rj1pHnhL04+/c9sUO0kbG+JJAN60DMwptkh6SS4R+PsD8b8u5ehEFfBedm6A3r +gqqF57190FoHyzkikG15+77HmXOO1kHU6NCZUEgFinvszYbjQTPK47pF5wiP9RG7 +eI3XDUGBnyu0+7Q+TBi4pq+NLpgTWSxRQ3ouj+JjNThQkTcdZaYgeQrN1xZ4Bmin +WGPO2zLNLdFD3Me34S/nzPzHnaB0s6bJCncym1v/zcktY7J52W5agwvpdTvbBZNk +CHV9peGcAppU5hlOdLbtI99fkaN/iPMKjc86PKAiU5W5vf9BgRNIxQPzxJ3R63Yg +e6WJKCH3iCuvmzGeh5NkVG7+DJIIbQnoX6W+gD+xAIGdSh8sJ0rdaSVdhVEP4bT3 +seyDi4uJs6cLF4kodE1vQ6W5NMFtxFjrqVYvLaulWunEVLV/NsJiSdLSdMSs0+4P +u7E2zKKGQBDVnA7EkUaHbrDkeUrb4gTZIskcuhXrqD2DHz2vqfXegSsxi/Zmucnh +XYDVX9kPwCQ/KTsm3Ib+vF6zAtPwF13AoyIVoGqjrEXGwzmkuh5/f5mUZkMAgf5o +2w== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/p12_without_passphrase_pkey.txt b/tests/src/Unit/certificates/p12_without_passphrase_pkey.txt new file mode 100644 index 0000000..fe0f81f --- /dev/null +++ b/tests/src/Unit/certificates/p12_without_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +/cmQTxlq7+EoWysYrHsg9mvxBo6q81o= +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/pem_with_passphrase_cert.txt b/tests/src/Unit/certificates/pem_with_passphrase_cert.txt new file mode 100644 index 0000000..d2f437d --- /dev/null +++ b/tests/src/Unit/certificates/pem_with_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUOxTqD8szgQSYH8WVixrU3HYc3RAwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODA3WhcNMjYw +MTE0MDkzODA3WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANxXnV+sXqdkjm8GSucnrIhRerEk4eMjbJ3hg01R +tfcnZBzpj6i0RmVCAX4Ij/U+LKqLL2chblKKvG7vFwKSKGVCzpZAOuOxZaUtqjry +XDshdhE+ToegZbt+rxLsuFtED4A6zq5ZGuntadbbh57CuMpPpjAmcDWaZ6e9VF0g +ihVlfv4Psvx1Bi5zBjfKa6IKLfO3jU5g7i37ri6w3dWzmx19Dyb3u8LiMtDua2ph +VcfX3bwUA+UB+Ct0h3xe+FxMo+KxiXutuqMg/yWczJS5mKH4u8hSex64VhGP/Jxh +vsuZ1pmDBMKhGEK7jDdDWdCyvqY8pyPXfMXp9kFejjgw75Cv/83yknmeoI9OHNtc +77wsrAU5Xk6SnqaaJviiSHZIa4xbtRBekqz5T63ZT4D5wfSZcNhRhBDL5jbTZ4t0 +Fldwapf2kqHe4Y8YgzBNMJu04CCpFdzcpjeMHhmZe55nPOaZFj5fSgrSol6kwoMr +m3q8WaFOjeBIGOuiylvJj24YLYBvKH7+M34mgqfDJhlxpuyNpESu10p8+62PgXul +BLfHcekb84JjljHFcdc6331JFkBqofT6x5osWK2CcuJGRP8h6TaRIe8jy7uKbX+e +jlzT1+0rEd69Uy8e8UseZwEZ2eMZg8sW/hd8eEQfSkSDZtTAs7GIjx3ZkyUUzix2 +zU5PAgMBAAGjUzBRMB0GA1UdDgQWBBTfX/93AkTP0qCJtjM/wecomdjlVTAfBgNV +HSMEGDAWgBTfX/93AkTP0qCJtjM/wecomdjlVTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQBNFBjyk0M+PJwVmkEHm4Y34ShCVkicCV3gD96SvzDK +Oav/XZePruiCX9dGplxG9OnKmO+VCAwJNVyxkT1+sf2M7ir8amVXMoRu0y31m9pA +wQ7/2MD6qi8ae5FWz0J83SvmhoPWT2tXiRVfB4+gOGSRfHdKscbF+MMhsCMozTmP +rjjS4nI8rZUC9Hhr475Demk6zCqUqTz1+RnrtlNvK/dYSOgbe0G178Y/uEMJCqO5 +4i4OnDVdAuttixwfLvoCVQpCodec6xfa77U6v+/HvFLJDHBnhzeNQrNcsBVIBlog +FtPnNw05rH4eUJP4glmlM3OOkpVsBrkjVCHDxo2Up3fb7qbg6CYk/WQi/u01ueJ5 +fZAQXPAznKsCo7h7Gh0w5BhWb5GendOJw3nQOEU1+dE4Z20zjkOxGEGn5M4umf3q +JbJhOeOx+74fnzMNQokgIuESbAKfnd6VZbJzh4Sj3iEA20HN/vq+OQCLw/JQg6iM +Ki4Ry22oa6vWCbF4xtkVnmsYk8D4XqRQdDG8NFkFcsFOBkRjuK+LxLAnpsYCGo68 +0uzEU6JQila0Jav691oE4R3bHiAYLa97pr+VqCTh45/FkPcjX8o8vWkTgUhcmRXp +g6s+9WrysM7UN/yb4MTw+2FV4DMDFN+Jsl8dZpW7SF7/yXTtkcmTt294HWmfmAS3 +mg== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/pem_with_passphrase_pkey.txt b/tests/src/Unit/certificates/pem_with_passphrase_pkey.txt new file mode 100644 index 0000000..3186899 --- /dev/null +++ b/tests/src/Unit/certificates/pem_with_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +yeVmJ3Z5MregjUwQwbz4eRSpyhUuqg== +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/pem_without_passphrase_cert.txt b/tests/src/Unit/certificates/pem_without_passphrase_cert.txt new file mode 100644 index 0000000..8925696 --- /dev/null +++ b/tests/src/Unit/certificates/pem_without_passphrase_cert.txt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUKG+LSVZOsm0JLoxNlXf7d+vYEtQwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODU0WhcNMjYw +MTE0MDkzODU0WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAKr72psmqBTBhA6GlInwoGHpBolOGz+X+wMD/zsf +nsdH66MdyStEF5LXu8Va/MrsG18Nz4lIehNeFIGAz5w2E30cRyIB9xkqI674dDSa +mFz+ENGKKeVVMzMoahKue44z77aQXMJRDnuDgLw3G96L+t/+0WTmx2E+ym6TxJSq +JtFk2Gssi968wwO+3ifUzCOPcTdhmfrKyB09aFsl3xnpVEzkIieWHgbkWvr9odWJ +VDoFo2ti7UM6NMoKzaCK2NGSJKcFD1clpffVwz1o/OMKcaBB+WbzNbXLghEl96gE +4ZPDbI1KZLDS1dFhpfYTEvg3izJGbOCrjcFM/G1ZzfoC5S8X9U1OYbYKAuQTIC+E +JUPhBTOD+g7hKzyHojAyRP2ox8F2Z4RygNadaMHxmHJnOBPKf3CbE9HvUS0jpOEM +Dyu5x5sxVWsCaSt227XJBhWO0egXhN8d4iBHrLiJrfDO5mX6dep6U2Ts47/Ish7g +S3vGjm5v9PrMM37wq5runDH3kGOkLRVGbjdAu6eMp3NKaNxRxe1AxY5LBSq7lEqr +3Wiq8x3wzCJSYK0gJsaKoHCfM2NmMlV74P7oiBk9c7ruAq1nKLRv9t6ebtvQPcpP +2UjjZ6wnw/+sPvz2wNl52sBZ+GYr24Oey3WNLkrm48lv50DBLBTL2KuYv1KM62Ie +6K0HAgMBAAGjUzBRMB0GA1UdDgQWBBQSB8SGPGoqbKogypKN/y/xytuXJzAfBgNV +HSMEGDAWgBQSB8SGPGoqbKogypKN/y/xytuXJzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCE956GUHzpGasQfXp1n893qbRHXL3JuAA7KFvQUAGy +I5a8GQV6+COCWHE0YzPQjrWFpi/MZV5KsQV5WwWZL5CIirw5k2jk5og1VcQMdHpB +QQzIHe5/efUBFKM4u7etRwQqaVlsqRPTe+ChRMMH7Kj+V5C4d2Me0A7LI/fb5OhO +tSF9Gt/tDuFcNfYQtVIzia4vzbEfiktDF6/2uaz1RTBIcc6tAFI+0TCaBp5q2zet +BEjbb9rg8257Lqvfsd2SFZCr/PkbnFN1ZO/fZIfQWaaH8FqB2vFraQsX2t/Uy4eK +OqCudeJW3yHXRBAnwhz6OmDjIlX8BtUIyERovD4GqUcr61DvNb7Q99WZ0SL+kS8x +f+2sennQ9uUcEDXMmXmLwVsk7YVKYB177fCxRFOm6EusVscTKkbiNQMMntTVtUav +idCVk4hEG1zkf7UhEqKPQwOFvhGZCtqFMn38yovdNgld6vDJyBarbddHHF4MdWZU +tl3ywwq6kpdGrrrqt4truac6tbMIa2XILylXLTo9thW8y81E+58G1/OU2+fvADXw +z8qoDiBuLsyghW96SUtrlHz9P22bd6SjopwU2HOVNe2yB06wVmaB7MpXBbn0mQR9 +28lP38ecXys3wOucDxwAIZrx+CpMXM7WuTtENC75xmDSJM5TiKeM4QQcPJkFhUWd +bA== +-----END CERTIFICATE----- diff --git a/tests/src/Unit/certificates/pem_without_passphrase_pkey.txt b/tests/src/Unit/certificates/pem_without_passphrase_pkey.txt new file mode 100644 index 0000000..832c20e --- /dev/null +++ b/tests/src/Unit/certificates/pem_without_passphrase_pkey.txt @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +1sy9cBx0ZARHTD27VxkpdyZOr8AAJw== +-----END PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/test_with_passphrase.p12 b/tests/src/Unit/certificates/test_with_passphrase.p12 new file mode 100644 index 0000000000000000000000000000000000000000..adf744cf522b1aa6d9b82c006a3e2d06a1861380 GIT binary patch literal 4211 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 literal 0 HcmV?d00001 diff --git a/tests/src/Unit/certificates/test_with_passphrase.pem b/tests/src/Unit/certificates/test_with_passphrase.pem new file mode 100644 index 0000000..b386f47 --- /dev/null +++ b/tests/src/Unit/certificates/test_with_passphrase.pem @@ -0,0 +1,84 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUOxTqD8szgQSYH8WVixrU3HYc3RAwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODA3WhcNMjYw +MTE0MDkzODA3WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANxXnV+sXqdkjm8GSucnrIhRerEk4eMjbJ3hg01R +tfcnZBzpj6i0RmVCAX4Ij/U+LKqLL2chblKKvG7vFwKSKGVCzpZAOuOxZaUtqjry +XDshdhE+ToegZbt+rxLsuFtED4A6zq5ZGuntadbbh57CuMpPpjAmcDWaZ6e9VF0g +ihVlfv4Psvx1Bi5zBjfKa6IKLfO3jU5g7i37ri6w3dWzmx19Dyb3u8LiMtDua2ph +VcfX3bwUA+UB+Ct0h3xe+FxMo+KxiXutuqMg/yWczJS5mKH4u8hSex64VhGP/Jxh +vsuZ1pmDBMKhGEK7jDdDWdCyvqY8pyPXfMXp9kFejjgw75Cv/83yknmeoI9OHNtc +77wsrAU5Xk6SnqaaJviiSHZIa4xbtRBekqz5T63ZT4D5wfSZcNhRhBDL5jbTZ4t0 +Fldwapf2kqHe4Y8YgzBNMJu04CCpFdzcpjeMHhmZe55nPOaZFj5fSgrSol6kwoMr +m3q8WaFOjeBIGOuiylvJj24YLYBvKH7+M34mgqfDJhlxpuyNpESu10p8+62PgXul +BLfHcekb84JjljHFcdc6331JFkBqofT6x5osWK2CcuJGRP8h6TaRIe8jy7uKbX+e +jlzT1+0rEd69Uy8e8UseZwEZ2eMZg8sW/hd8eEQfSkSDZtTAs7GIjx3ZkyUUzix2 +zU5PAgMBAAGjUzBRMB0GA1UdDgQWBBTfX/93AkTP0qCJtjM/wecomdjlVTAfBgNV +HSMEGDAWgBTfX/93AkTP0qCJtjM/wecomdjlVTAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQBNFBjyk0M+PJwVmkEHm4Y34ShCVkicCV3gD96SvzDK +Oav/XZePruiCX9dGplxG9OnKmO+VCAwJNVyxkT1+sf2M7ir8amVXMoRu0y31m9pA +wQ7/2MD6qi8ae5FWz0J83SvmhoPWT2tXiRVfB4+gOGSRfHdKscbF+MMhsCMozTmP +rjjS4nI8rZUC9Hhr475Demk6zCqUqTz1+RnrtlNvK/dYSOgbe0G178Y/uEMJCqO5 +4i4OnDVdAuttixwfLvoCVQpCodec6xfa77U6v+/HvFLJDHBnhzeNQrNcsBVIBlog +FtPnNw05rH4eUJP4glmlM3OOkpVsBrkjVCHDxo2Up3fb7qbg6CYk/WQi/u01ueJ5 +fZAQXPAznKsCo7h7Gh0w5BhWb5GendOJw3nQOEU1+dE4Z20zjkOxGEGn5M4umf3q +JbJhOeOx+74fnzMNQokgIuESbAKfnd6VZbJzh4Sj3iEA20HN/vq+OQCLw/JQg6iM +Ki4Ry22oa6vWCbF4xtkVnmsYk8D4XqRQdDG8NFkFcsFOBkRjuK+LxLAnpsYCGo68 +0uzEU6JQila0Jav691oE4R3bHiAYLa97pr+VqCTh45/FkPcjX8o8vWkTgUhcmRXp +g6s+9WrysM7UN/yb4MTw+2FV4DMDFN+Jsl8dZpW7SF7/yXTtkcmTt294HWmfmAS3 +mg== +-----END CERTIFICATE----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +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 +OdntOpiFfWNOCIebk2Fgz9YTMisw+H1I +-----END ENCRYPTED PRIVATE KEY----- diff --git a/tests/src/Unit/certificates/test_without_passphrase.p12 b/tests/src/Unit/certificates/test_without_passphrase.p12 new file mode 100644 index 0000000000000000000000000000000000000000..a2a3a4fcd20ba4b0d92326d8be73e9a155329142 GIT binary patch literal 4211 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 literal 0 HcmV?d00001 diff --git a/tests/src/Unit/certificates/test_without_passphrase.pem b/tests/src/Unit/certificates/test_without_passphrase.pem new file mode 100644 index 0000000..e48af62 --- /dev/null +++ b/tests/src/Unit/certificates/test_without_passphrase.pem @@ -0,0 +1,82 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIUKG+LSVZOsm0JLoxNlXf7d+vYEtQwDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUwMTE0MDkzODU0WhcNMjYw +MTE0MDkzODU0WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAKr72psmqBTBhA6GlInwoGHpBolOGz+X+wMD/zsf +nsdH66MdyStEF5LXu8Va/MrsG18Nz4lIehNeFIGAz5w2E30cRyIB9xkqI674dDSa +mFz+ENGKKeVVMzMoahKue44z77aQXMJRDnuDgLw3G96L+t/+0WTmx2E+ym6TxJSq +JtFk2Gssi968wwO+3ifUzCOPcTdhmfrKyB09aFsl3xnpVEzkIieWHgbkWvr9odWJ +VDoFo2ti7UM6NMoKzaCK2NGSJKcFD1clpffVwz1o/OMKcaBB+WbzNbXLghEl96gE +4ZPDbI1KZLDS1dFhpfYTEvg3izJGbOCrjcFM/G1ZzfoC5S8X9U1OYbYKAuQTIC+E +JUPhBTOD+g7hKzyHojAyRP2ox8F2Z4RygNadaMHxmHJnOBPKf3CbE9HvUS0jpOEM +Dyu5x5sxVWsCaSt227XJBhWO0egXhN8d4iBHrLiJrfDO5mX6dep6U2Ts47/Ish7g +S3vGjm5v9PrMM37wq5runDH3kGOkLRVGbjdAu6eMp3NKaNxRxe1AxY5LBSq7lEqr +3Wiq8x3wzCJSYK0gJsaKoHCfM2NmMlV74P7oiBk9c7ruAq1nKLRv9t6ebtvQPcpP +2UjjZ6wnw/+sPvz2wNl52sBZ+GYr24Oey3WNLkrm48lv50DBLBTL2KuYv1KM62Ie +6K0HAgMBAAGjUzBRMB0GA1UdDgQWBBQSB8SGPGoqbKogypKN/y/xytuXJzAfBgNV +HSMEGDAWgBQSB8SGPGoqbKogypKN/y/xytuXJzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQCE956GUHzpGasQfXp1n893qbRHXL3JuAA7KFvQUAGy +I5a8GQV6+COCWHE0YzPQjrWFpi/MZV5KsQV5WwWZL5CIirw5k2jk5og1VcQMdHpB +QQzIHe5/efUBFKM4u7etRwQqaVlsqRPTe+ChRMMH7Kj+V5C4d2Me0A7LI/fb5OhO +tSF9Gt/tDuFcNfYQtVIzia4vzbEfiktDF6/2uaz1RTBIcc6tAFI+0TCaBp5q2zet +BEjbb9rg8257Lqvfsd2SFZCr/PkbnFN1ZO/fZIfQWaaH8FqB2vFraQsX2t/Uy4eK +OqCudeJW3yHXRBAnwhz6OmDjIlX8BtUIyERovD4GqUcr61DvNb7Q99WZ0SL+kS8x +f+2sennQ9uUcEDXMmXmLwVsk7YVKYB177fCxRFOm6EusVscTKkbiNQMMntTVtUav +idCVk4hEG1zkf7UhEqKPQwOFvhGZCtqFMn38yovdNgld6vDJyBarbddHHF4MdWZU +tl3ywwq6kpdGrrrqt4truac6tbMIa2XILylXLTo9thW8y81E+58G1/OU2+fvADXw +z8qoDiBuLsyghW96SUtrlHz9P22bd6SjopwU2HOVNe2yB06wVmaB7MpXBbn0mQR9 +28lP38ecXys3wOucDxwAIZrx+CpMXM7WuTtENC75xmDSJM5TiKeM4QQcPJkFhUWd +bA== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +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 +1sy9cBx0ZARHTD27VxkpdyZOr8AAJw== +-----END PRIVATE KEY----- From b4cfe6240a5a22e271b8a61769b6f1fa0f04f8b4 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 16 Jan 2025 17:06:53 +0100 Subject: [PATCH 06/13] Cleaned up scripts --- .github/workflows/pr.yml | 2 -- scripts/code-analysis | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9899dbe..1f57a6f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -139,8 +139,6 @@ jobs: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- - - name: Setup tmate session - uses: mxschmitt/action-tmate@v3 - name: Unit tests run: | ./scripts/unit-tests diff --git a/scripts/code-analysis b/scripts/code-analysis index 0435ebd..aabb740 100755 --- a/scripts/code-analysis +++ b/scripts/code-analysis @@ -41,4 +41,4 @@ drupal_composer --no-interaction install # Run PHPStan -(cd "$drupal_dir/$module_path" && ../../../../vendor/bin/phpstan) +(cd "$drupal_dir" && vendor/bin/phpstan) From 5fc0468e9f437ce2b9383f60bbcbb24da8c89e95 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 16 Jan 2025 17:09:17 +0100 Subject: [PATCH 07/13] Made code analysis script function --- scripts/code-analysis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/code-analysis b/scripts/code-analysis index aabb740..0435ebd 100755 --- a/scripts/code-analysis +++ b/scripts/code-analysis @@ -41,4 +41,4 @@ drupal_composer --no-interaction install # Run PHPStan -(cd "$drupal_dir" && vendor/bin/phpstan) +(cd "$drupal_dir/$module_path" && ../../../../vendor/bin/phpstan) From e78753a643603b4a64178a4ab7c7be8758725f1f Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 17 Jan 2025 09:24:25 +0100 Subject: [PATCH 08/13] Clean up and documentation --- README.md | 25 ++++++++++-- composer.json | 7 ---- phpunit.xml | 105 -------------------------------------------------- 3 files changed, 21 insertions(+), 116 deletions(-) delete mode 100644 phpunit.xml diff --git a/README.md b/README.md index 9fbcf92..68d16d3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Key types and providers for OS2Web built on the [Key module](https://www.drupal.org/project/key). The OS2Web key module provides two _key types_, [Certificate](#certificate) and [OpenID Connect -(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and [Infisical](#infisical), are +(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and [HashiCorp Vault](#hashicorp-vault), are planned, but not yet implemented. See [the Key Developer Guide](https://www.drupal.org/docs/contributed-modules/key/developer-guide) for details in how to @@ -106,13 +106,16 @@ $key = $repository->getKey('openid_connect_ad'); ## Providers +The module comes with two key providers. + ### Azure Key Vault -`@todo` +Used for fetching certificate from Azure Key vault. -### Infisical +### HashiCorp Vault -`@todo` +Used to fetch any sort of secret string from HashiCorp vault. Note that +this can only provide string values, i.e. no binary files. ## Coding standards @@ -146,3 +149,17 @@ analysis: ```shell docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/code-analysis ``` + +## Unit tests + +We use [PHPUnit](https://phpunit.de/documentation.html) for unit testing. + +Testing mostly centers around the conversion and parsing of certificates. For this purpose a bunch of test +certificates has been generated. + +Running PHPUnit tests in a standalone Drupal module is a bit tricky, so we use a helper script to run the +analysis: + +```shell +docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/unit-tests +``` diff --git a/composer.json b/composer.json index d538dfc..013a5c7 100644 --- a/composer.json +++ b/composer.json @@ -47,13 +47,6 @@ "sort-packages": true }, "scripts": { - "code-analysis": [ - "@code-analysis/drupal-check" - ], - "code-analysis/drupal-check": [ - "# @see https://github.com/mglaman/drupal-check/issues/261#issuecomment-1030141772 for details on exclude-dir value", - "drupal-check --deprecations --analysis --exclude-dir='vendor' *.* src" - ], "coding-standards-apply": [ "@coding-standards-apply/phpcs" ], diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index dad1e85..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ./tests/TestSuites/UnitTestSuite.php - - - ./tests/TestSuites/KernelTestSuite.php - - - ./tests/TestSuites/FunctionalTestSuite.php - - - ./tests/TestSuites/FunctionalJavascriptTestSuite.php - - - ./tests/TestSuites/BuildTestSuite.php - - - - - - - - - - ./includes - ./lib - ./modules - ../modules - ../sites - - - ./modules/*/src/Tests - ./modules/*/tests - ../modules/*/src/Tests - ../modules/*/tests - ../modules/*/*/src/Tests - ../modules/*/*/tests - ./lib/** - ./modules/** - ../modules/** - - - From bf5cbcb99bfffe2ac730877f2b590b7d90185022 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 17 Jan 2025 09:25:32 +0100 Subject: [PATCH 09/13] Markdown standards --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 68d16d3..f3adf7f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ Key types and providers for OS2Web built on the [Key module](https://www.drupal.org/project/key). The OS2Web key module provides two _key types_, [Certificate](#certificate) and [OpenID Connect -(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and [HashiCorp Vault](#hashicorp-vault), are -planned, but not yet implemented. +(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and +[HashiCorp Vault](#hashicorp-vault), are planned, but not yet implemented. See [the Key Developer Guide](https://www.drupal.org/docs/contributed-modules/key/developer-guide) for details in how to use keys in Drupal. From 09afcedd06cecc7f277b5a1e2d6ebb8d3e3482cd Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 17 Jan 2025 12:40:00 +0100 Subject: [PATCH 10/13] Update setting names --- README.md | 12 ++++++++++-- src/Plugin/KeyProvider/VaultKeyProvider.php | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f3adf7f..22cc409 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ Key types and providers for OS2Web built on the [Key module](https://www.drupal.org/project/key). The OS2Web key module provides two _key types_, [Certificate](#certificate) and [OpenID Connect -(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and -[HashiCorp Vault](#hashicorp-vault), are planned, but not yet implemented. +(OIDC)](#openid-connect-oidc). It also comes with two _key providers_, +[Azure Key Vault](#azure-key-vault) and [HashiCorp Vault](#hashicorp-vault). See [the Key Developer Guide](https://www.drupal.org/docs/contributed-modules/key/developer-guide) for details in how to use keys in Drupal. @@ -117,6 +117,14 @@ Used for fetching certificate from Azure Key vault. Used to fetch any sort of secret string from HashiCorp vault. Note that this can only provide string values, i.e. no binary files. +To use this provider you must configure the following in `settings.local.php`: + +``` php +$settings['os2web_vault_role_id'] = '{ROLE_ID}'; +$settings['os2web_vault_secret_id'] = '{SECRET_ID}'; +$settings['os2web_vault_url'] = '{VAULT_URL}'; +``` + ## Coding standards Our coding are checked by GitHub Actions (cf. [.github/workflows/pr.yml](.github/workflows/pr.yml)). Use the commands diff --git a/src/Plugin/KeyProvider/VaultKeyProvider.php b/src/Plugin/KeyProvider/VaultKeyProvider.php index c831e98..691344d 100644 --- a/src/Plugin/KeyProvider/VaultKeyProvider.php +++ b/src/Plugin/KeyProvider/VaultKeyProvider.php @@ -100,8 +100,8 @@ public function defaultConfiguration(): array { * {@inheritdoc} */ public function getKeyValue(KeyInterface $key) { - $roleId = Settings::get('itkdev_vault_role_id'); - $secretId = Settings::get('itkdev_vault_secret_id'); + $roleId = Settings::get('os2web_vault_role_id'); + $secretId = Settings::get('os2web_vault_secret_id'); $vault = $this->getVault(); @@ -250,7 +250,7 @@ private function getVault(): VaultClient { requestFactory: $httpFactory, streamFactory: $httpFactory, cache: $this->cache, - vaultUrl: Settings::get('itkdev_vault_url'), + vaultUrl: Settings::get('os2web_vault_url'), ); } From d110ec29b9c81a73368c0b04c2da8b1d87ee1ceb Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 17 Jan 2025 12:51:24 +0100 Subject: [PATCH 11/13] Fixed typo --- tests/src/Unit/KeyHelperUnitTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/src/Unit/KeyHelperUnitTest.php b/tests/src/Unit/KeyHelperUnitTest.php index e8f19a5..d8a2a1b 100644 --- a/tests/src/Unit/KeyHelperUnitTest.php +++ b/tests/src/Unit/KeyHelperUnitTest.php @@ -61,9 +61,9 @@ public function testCreateRuntimeExceptionNoSslMessage() { public function testCreateRuntimeExceptionWithSslMessage() { $this->mockLogger->expects($this->once())->method('error'); $message = 'test'; - $excepted = $message . ' (some SSL error)'; + $expected = $message . ' (some SSL error)'; $result = $this->keyHelper->createRuntimeException($message, NULL, 'some SSL error'); - $this->assertEquals($excepted, $result->getMessage()); + $this->assertEquals($expected, $result->getMessage()); } /** @@ -71,16 +71,16 @@ public function testCreateRuntimeExceptionWithSslMessage() { */ public function testCreateRuntimeExceptionWithKeyAndSslMessage() { $mockMessage = 'test'; - $exceptedMessage = $mockMessage . ' (some SSL error)'; + $expectedMessage = $mockMessage . ' (some SSL error)'; $mockKeyId = 'some_key_id'; $this->mockLogger->expects($this->once())->method('error')->with('@key: @message', [ '@key' => $mockKeyId, - '@message' => $exceptedMessage, + '@message' => $expectedMessage, ]); $mockKey = $this->createMock('Drupal\key\KeyInterface'); $mockKey->expects($this->once())->method('id')->willReturn($mockKeyId); $result = $this->keyHelper->createRuntimeException($mockMessage, $mockKey, 'some SSL error'); - $this->assertEquals($exceptedMessage, $result->getMessage()); + $this->assertEquals($expectedMessage, $result->getMessage()); } /** From ef2527d82e16c4e67955da48e69383c51f33b291 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 17 Jan 2025 13:48:42 +0100 Subject: [PATCH 12/13] Documented test certificate creation --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 22cc409..fb7c6e5 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/ We use [PHPUnit](https://phpunit.de/documentation.html) for unit testing. Testing mostly centers around the conversion and parsing of certificates. For this purpose a bunch of test -certificates has been generated. +certificates has been generated. See [Test certificates](#test-certificates) for how this is done. Running PHPUnit tests in a standalone Drupal module is a bit tricky, so we use a helper script to run the analysis: @@ -171,3 +171,49 @@ analysis: ```shell docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/unit-tests ``` + +### Test certificates + +Certificates have been generated in the follow way + +```shell +# p12 with password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:test -keyout test.key -out test.crt +openssl pkcs12 -export -out test_with_passphrase.p12 -passin pass:test -passout pass:test -inkey test.key -in test.crt +openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -noenc + +# p12 without password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:'' -keyout test_without_passphrase.key -out test_without_passphrase.crt +openssl pkcs12 -export -out test_without_passphrase.p12 -passin pass:'' -passout pass:'' -inkey test_without_passphrase.key -in test_without_passphrase.crt +openssl pkcs12 -in test_without_passphrase.p12 -passin pass:'' -noenc + +# PEM with password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:test -keyout test.key -out test.crt +cat test.crt test.key > test_with_passphrase.pem +openssl x509 -in test_with_passphrase.pem + +# PEM without password +openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:'' -keyout test_without_passphrase.key -out test_without_passphrase.crt -noenc +cat test_without_passphrase.crt test_without_passphrase.key > test_without_passphrase.pem +openssl x509 -in test_without_passphrase.pem +``` + +Extraction of certificate and private key parts in the following way + +```shell +# P12 with passphrase +openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -clcerts -nokeys | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > p12_with_passphrase_cert.txt +openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -nocerts -nodes | sed -ne '/-----BEGIN PRIVATE KEY-----/,/-----END PRIVATE KEY-----/p' > p12_with_passphrase_pkey.txt + +# P12 without passphrase +openssl pkcs12 -in test_without_passphrase.p12 -passin pass: -clcerts -nokeys | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > p12_without_passphrase_cert.txt +openssl pkcs12 -in test_without_passphrase.p12 -passin pass: -nocerts -nodes | sed -ne '/-----BEGIN PRIVATE KEY-----/,/-----END PRIVATE KEY-----/p' > p12_without_passphrase_pkey.txt + +# PEM with passphrase +openssl x509 -in test_with_passphrase.pem -passin pass:test -out pem_with_passphrase_cert.txt +openssl pkey -in test_with_passphrase.pem -passin pass:test -out pem_with_passphrase_pkey.txt + +# PEM without passphrase +openssl x509 -in test_without_passphrase.pem -passin pass: -out pem_without_passphrase_cert.txt +openssl pkey -in test_without_passphrase.pem -passin pass: -out pem_without_passphrase_pkey.txt +``` From 9baf9fc6637488088aa58d4410cd19561dfd5f1b Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 17 Jan 2025 15:43:14 +0100 Subject: [PATCH 13/13] Service cleanup --- os2web_key.services.yml | 30 ++------- src/Plugin/KeyProvider/VaultKeyProvider.php | 5 +- src/Plugin/KeyType/CertificateKeyType.php | 2 +- src/Services/Psr16CacheAdapter.php | 10 +-- src/Services/VaultCertificateHelper.php | 70 --------------------- 5 files changed, 11 insertions(+), 106 deletions(-) delete mode 100644 src/Services/VaultCertificateHelper.php diff --git a/os2web_key.services.yml b/os2web_key.services.yml index 95cc1fe..b7ba945 100644 --- a/os2web_key.services.yml +++ b/os2web_key.services.yml @@ -3,30 +3,12 @@ services: parent: logger.channel_base arguments: [ 'os2web_key' ] - os2web_key.key_helper: - class: 'Drupal\os2web_key\KeyHelper' + Drupal\os2web_key\KeyHelper: + autowire: true arguments: - - '@logger.channel.os2web_key' + $logger: '@logger.channel.os2web_key' - os2web_key.certificate_key_type: - class: 'Drupal\os2web_key\Plugin\KeyType\CertificateKeyType' + Drupal\os2web_key\Services\Psr16CacheAdapter: + autowire: true arguments: - - '@os2web_key.key_helper' - - os2web_key.psr16_cache: - class: 'Drupal\os2web_key\Services\Psr16CacheAdapter' - arguments: - - '@cache.default' - - os2web_key.certificate_helper: - class: 'Drupal\os2web_key\Services\VaultCertificateHelper' - arguments: - - '@file_system' - - os2web_key.key_provider: - class: 'Drupal\os2web_key\Plugin\KeyProvider\VaultKeyProvider' - arguments: - - '@logger.channel.os2web_key' - - '@http_client' - - '@os2web_key.psr16_cache' - - '@os2web_key.key_helper' + $cacheBackend: '@cache.default' diff --git a/src/Plugin/KeyProvider/VaultKeyProvider.php b/src/Plugin/KeyProvider/VaultKeyProvider.php index 691344d..c71c919 100644 --- a/src/Plugin/KeyProvider/VaultKeyProvider.php +++ b/src/Plugin/KeyProvider/VaultKeyProvider.php @@ -9,6 +9,7 @@ use Drupal\key\Plugin\KeyPluginFormInterface; use Drupal\key\Plugin\KeyProviderBase; use Drupal\os2web_key\KeyHelper; +use Drupal\os2web_key\Services\Psr16CacheAdapter; use Drupal\os2web_key\Plugin\KeyType\CertificateKeyType; use GuzzleHttp\Psr7\HttpFactory; use ItkDev\Vault\Exception\UnknownErrorException; @@ -78,8 +79,8 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_definition, $container->get('logger.channel.default'), $container->get('http_client'), - $container->get('os2web_key.psr16_cache'), - $container->get('os2web_key.key_helper'), + $container->get(Psr16CacheAdapter::class), + $container->get(KeyHelper::class), ); } diff --git a/src/Plugin/KeyType/CertificateKeyType.php b/src/Plugin/KeyType/CertificateKeyType.php index acd3f6a..865c1c6 100644 --- a/src/Plugin/KeyType/CertificateKeyType.php +++ b/src/Plugin/KeyType/CertificateKeyType.php @@ -56,7 +56,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('os2web_key.key_helper') + $container->get(KeyHelper::class) ); } diff --git a/src/Services/Psr16CacheAdapter.php b/src/Services/Psr16CacheAdapter.php index 577c8d3..64c4c6b 100644 --- a/src/Services/Psr16CacheAdapter.php +++ b/src/Services/Psr16CacheAdapter.php @@ -10,21 +10,13 @@ */ class Psr16CacheAdapter implements CacheInterface { - /** - * The Drupal cache backend. - * - * @var \Drupal\Core\Cache\CacheBackendInterface - */ - protected CacheBackendInterface $cacheBackend; - /** * Constructs a new Psr16CacheAdapter. * * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend * The Drupal cache backend. */ - public function __construct(CacheBackendInterface $cacheBackend) { - $this->cacheBackend = $cacheBackend; + public function __construct(private readonly CacheBackendInterface $cacheBackend) { } /** diff --git a/src/Services/VaultCertificateHelper.php b/src/Services/VaultCertificateHelper.php deleted file mode 100644 index bfdee0b..0000000 --- a/src/Services/VaultCertificateHelper.php +++ /dev/null @@ -1,70 +0,0 @@ -certificatePath = $this->createTempCertificateFile($certificate); - } - - /** - * Ensure temporary certificate file is removed. - */ - public function __destruct() { - // Remove the certificate from disk. - if (file_exists($this->certificatePath)) { - unlink($this->certificatePath); - } - } - - /** - * Gets path to temporary certificate file. - */ - public function getCertificatePath(): string { - return $this->certificatePath; - } - - /** - * Creates a temporary file with certificate. - * - * @return string - * The temporary file path. - * - * @throws \Drupal\os2web_key\Exception\FileException - * File exception. - */ - private function createTempCertificateFile(string $certificate): string { - - $localCertFilename = tempnam($this->fileSystem->getTempDirectory(), 'vault_certificate'); - - if (!$localCertFilename) { - throw new FileException('Could not generate temporary certificate file.'); - } - - file_put_contents($localCertFilename, $certificate); - - return $localCertFilename; - } - -}