Skip to content

Commit df3ba03

Browse files
authored
Merge pull request #4 from itk-dev/feature/hashi-corp-vault
Added HashiCorp vault key provider
2 parents e2b0eb2 + 9baf9fc commit df3ba03

27 files changed

+1554
-47
lines changed

.github/workflows/pr.yml

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
runs-on: ubuntu-latest
2424
strategy:
2525
matrix:
26-
php-versions: [ '8.1' ]
26+
php-versions: [ '8.3' ]
2727
dependency-version: [ prefer-lowest, prefer-stable ]
2828
steps:
2929
- uses: actions/checkout@master
@@ -58,7 +58,7 @@ jobs:
5858
runs-on: ubuntu-latest
5959
strategy:
6060
matrix:
61-
php-versions: [ '8.1' ]
61+
php-versions: [ '8.3' ]
6262
steps:
6363
- uses: actions/checkout@master
6464
- name: Setup PHP, with composer and extensions
@@ -90,7 +90,7 @@ jobs:
9090
runs-on: ubuntu-latest
9191
strategy:
9292
matrix:
93-
php-versions: [ '8.1' ]
93+
php-versions: [ '8.3' ]
9494
steps:
9595
- uses: actions/checkout@master
9696
- name: Setup PHP, with composer and extensions
@@ -114,6 +114,35 @@ jobs:
114114
run: |
115115
./scripts/code-analysis
116116
117+
php-unit-tests:
118+
name: PHP unit tests
119+
runs-on: ubuntu-latest
120+
strategy:
121+
matrix:
122+
php-versions: [ '8.3' ]
123+
steps:
124+
- uses: actions/checkout@master
125+
- name: Setup PHP, with composer and extensions
126+
uses: shivammathur/setup-php@v2
127+
with:
128+
php-version: ${{ matrix.php-versions }}
129+
extensions: json
130+
coverage: none
131+
tools: composer:v2
132+
# https://github.com/shivammathur/setup-php#cache-composer-dependencies
133+
- name: Get composer cache directory
134+
id: composer-cache
135+
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
136+
- name: Cache dependencies
137+
uses: actions/cache@v2
138+
with:
139+
path: ${{ steps.composer-cache.outputs.dir }}
140+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
141+
restore-keys: ${{ runner.os }}-composer-
142+
- name: Unit tests
143+
run: |
144+
./scripts/unit-tests
145+
117146
coding-standards-markdown:
118147
name: Markdown coding standards
119148
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
* [PR-4](https://github.com/OS2web/os2web_key/pull/4)
11+
Added HashiCorp Vault key provider
1012
* [PR-2](https://github.com/OS2web/os2web_key/pull/2)
1113
Updated documentation
1214
* [PR-1](https://github.com/OS2web/os2web_key/pull/1)

README.md

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
Key types and providers for OS2Web built on the [Key module](https://www.drupal.org/project/key).
44

55
The OS2Web key module provides two _key types_, [Certificate](#certificate) and [OpenID Connect
6-
(OIDC)](#openid-connect-oidc). Two _key providers_, [Azure Key Vault](#azure-key-vault) and [Infisical](#infisical), are
7-
planned, but not yet implemented.
6+
(OIDC)](#openid-connect-oidc). It also comes with two _key providers_,
7+
[Azure Key Vault](#azure-key-vault) and [HashiCorp Vault](#hashicorp-vault).
88

99
See [the Key Developer Guide](https://www.drupal.org/docs/contributed-modules/key/developer-guide) for details in how to
1010
use keys in Drupal.
@@ -106,13 +106,24 @@ $key = $repository->getKey('openid_connect_ad');
106106

107107
## Providers
108108

109+
The module comes with two key providers.
110+
109111
### Azure Key Vault
110112

111-
`@todo` <https://azure.microsoft.com/en-us/products/key-vault>
113+
Used for fetching certificate from Azure Key vault.
114+
115+
### HashiCorp Vault
112116

113-
### Infisical
117+
Used to fetch any sort of secret string from HashiCorp vault. Note that
118+
this can only provide string values, i.e. no binary files.
114119

115-
`@todo` <https://infisical.com/>
120+
To use this provider you must configure the following in `settings.local.php`:
121+
122+
``` php
123+
$settings['os2web_vault_role_id'] = '{ROLE_ID}';
124+
$settings['os2web_vault_secret_id'] = '{SECRET_ID}';
125+
$settings['os2web_vault_url'] = '{VAULT_URL}';
126+
```
116127

117128
## Coding standards
118129

@@ -122,11 +133,11 @@ below to run the checks locally.
122133
### PHP
123134

124135
```shell
125-
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer install
136+
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer install
126137
# Fix (some) coding standards issues
127-
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer coding-standards-apply
138+
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-apply
128139
# Check that code adheres to the coding standards
129-
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm composer coding-standards-check
140+
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm composer coding-standards-check
130141
```
131142

132143
### Markdown
@@ -140,9 +151,69 @@ docker run --rm --volume $PWD:/md peterdavehello/markdownlint markdownlint --ign
140151

141152
We use [PHPStan](https://phpstan.org/) for static code analysis.
142153

143-
Running statis code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the
154+
Running static code analysis on a standalone Drupal module is a bit tricky, so we use a helper script to run the
155+
analysis:
156+
157+
```shell
158+
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/code-analysis
159+
```
160+
161+
## Unit tests
162+
163+
We use [PHPUnit](https://phpunit.de/documentation.html) for unit testing.
164+
165+
Testing mostly centers around the conversion and parsing of certificates. For this purpose a bunch of test
166+
certificates has been generated. See [Test certificates](#test-certificates) for how this is done.
167+
168+
Running PHPUnit tests in a standalone Drupal module is a bit tricky, so we use a helper script to run the
144169
analysis:
145170

146171
```shell
147-
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.1-fpm ./scripts/code-analysis
172+
docker run --rm --volume ${PWD}:/app --workdir /app itkdev/php8.3-fpm ./scripts/unit-tests
173+
```
174+
175+
### Test certificates
176+
177+
Certificates have been generated in the follow way
178+
179+
```shell
180+
# p12 with password
181+
openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:test -keyout test.key -out test.crt
182+
openssl pkcs12 -export -out test_with_passphrase.p12 -passin pass:test -passout pass:test -inkey test.key -in test.crt
183+
openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -noenc
184+
185+
# p12 without password
186+
openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:'' -keyout test_without_passphrase.key -out test_without_passphrase.crt
187+
openssl pkcs12 -export -out test_without_passphrase.p12 -passin pass:'' -passout pass:'' -inkey test_without_passphrase.key -in test_without_passphrase.crt
188+
openssl pkcs12 -in test_without_passphrase.p12 -passin pass:'' -noenc
189+
190+
# PEM with password
191+
openssl req -x509 -newkey rsa:4096 -days 365 -subj "/CN=example.com" -passout pass:test -keyout test.key -out test.crt
192+
cat test.crt test.key > test_with_passphrase.pem
193+
openssl x509 -in test_with_passphrase.pem
194+
195+
# PEM without password
196+
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
197+
cat test_without_passphrase.crt test_without_passphrase.key > test_without_passphrase.pem
198+
openssl x509 -in test_without_passphrase.pem
199+
```
200+
201+
Extraction of certificate and private key parts in the following way
202+
203+
```shell
204+
# P12 with passphrase
205+
openssl pkcs12 -in test_with_passphrase.p12 -passin pass:test -clcerts -nokeys | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > p12_with_passphrase_cert.txt
206+
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
207+
208+
# P12 without passphrase
209+
openssl pkcs12 -in test_without_passphrase.p12 -passin pass: -clcerts -nokeys | sed -ne '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > p12_without_passphrase_cert.txt
210+
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
211+
212+
# PEM with passphrase
213+
openssl x509 -in test_with_passphrase.pem -passin pass:test -out pem_with_passphrase_cert.txt
214+
openssl pkey -in test_with_passphrase.pem -passin pass:test -out pem_with_passphrase_pkey.txt
215+
216+
# PEM without passphrase
217+
openssl x509 -in test_without_passphrase.pem -passin pass: -out pem_without_passphrase_cert.txt
218+
openssl pkey -in test_without_passphrase.pem -passin pass: -out pem_without_passphrase_pkey.txt
148219
```

composer.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,31 @@
1313
"php": "^8.1",
1414
"ext-openssl": "*",
1515
"drupal/core": "^9 || ^10",
16-
"drupal/key": "^1.17"
16+
"drupal/key": "^1.17",
17+
"itk-dev/serviceplatformen": "^1.6",
18+
"itk-dev/vault": "^0.1"
1719
},
1820
"require-dev": {
1921
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
2022
"drupal/coder": "^8.3",
23+
"drupal/core-dev": "^9 || ^10",
2124
"ergebnis/composer-normalize": "^2.42",
2225
"mglaman/phpstan-drupal": "^1.2",
2326
"phpstan/extension-installer": "^1.3",
24-
"phpstan/phpstan-deprecation-rules": "^1.1"
27+
"phpstan/phpstan-deprecation-rules": "^1.1",
28+
"phpunit/phpunit": "^9.6"
2529
},
2630
"repositories": [
2731
{
2832
"type": "composer",
2933
"url": "https://packages.drupal.org/8"
3034
}
3135
],
36+
"autoload-dev": {
37+
"psr-4": {
38+
"Drupal\\Tests\\os2web_key\\": "tests/src/"
39+
}
40+
},
3241
"config": {
3342
"allow-plugins": {
3443
"dealerdirect/phpcodesniffer-composer-installer": true,

os2web_key.services.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@ services:
44
arguments: [ 'os2web_key' ]
55

66
Drupal\os2web_key\KeyHelper:
7+
autowire: true
78
arguments:
8-
- '@logger.channel.os2web_key'
9+
$logger: '@logger.channel.os2web_key'
10+
11+
Drupal\os2web_key\Services\Psr16CacheAdapter:
12+
autowire: true
13+
arguments:
14+
$cacheBackend: '@cache.default'

scripts/code-analysis

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,34 @@ drupal_composer() {
1111
composer --working-dir="$drupal_dir" --no-interaction "$@"
1212
}
1313

14-
# Create new Drupal 9 project
14+
# Create new Drupal 10 project
1515
if [ ! -f "$drupal_dir/composer.json" ]; then
16-
composer --no-interaction create-project drupal/recommended-project:^9 "$drupal_dir"
16+
composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir"
1717
fi
1818
# Copy our code into the modules folder
19-
20-
# Clean up
21-
rm -fr "${drupal_dir:?}/$module_path"
22-
19+
mkdir -p "$drupal_dir/$module_path"
2320
# https://stackoverflow.com/a/15373763
24-
# rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path"
25-
26-
# The rsync command in not available in itkdev/php8.1-fpm
27-
28-
git config --global --add safe.directory /app
29-
# Copy module files into module path
30-
for f in $(git ls-files); do
31-
mkdir -p "$drupal_dir/$module_path/$(dirname "$f")"
32-
cp "$f" "$drupal_dir/$module_path/$f"
33-
done
21+
rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path"
3422

3523
drupal_composer config minimum-stability dev
3624

25+
drupal_composer --append repositories.os2web/os2web_key path "$module_path"
26+
3727
# Allow ALL plugins
3828
# https://getcomposer.org/doc/06-config.md#allow-plugins
3929
drupal_composer config --no-plugins allow-plugins true
4030

31+
# Making Drupal 10 compatible
32+
drupal_composer require psr/http-message:^1.0
33+
drupal_composer require mglaman/composer-drupal-lenient
34+
drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]'
35+
4136
drupal_composer require wikimedia/composer-merge-plugin
4237
drupal_composer config extra.merge-plugin.include "$module_path/composer.json"
4338
# https://www.drupal.org/project/drupal/issues/3220043#comment-14845434
4439
drupal_composer require --dev symfony/phpunit-bridge
40+
drupal_composer --no-interaction install
41+
4542

4643
# Run PHPStan
4744
(cd "$drupal_dir/$module_path" && ../../../../vendor/bin/phpstan)

scripts/unit-tests

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env bash
2+
script_dir=$(pwd)
3+
module_name=$(basename "$script_dir")
4+
drupal_dir=vendor/drupal-module-unit-tests-analysis
5+
# Relative to $drupal_dir
6+
module_path=web/modules/contrib/$module_name
7+
8+
cd "$script_dir" || exit
9+
10+
drupal_composer() {
11+
composer --working-dir="$drupal_dir" --no-interaction "$@"
12+
}
13+
14+
# Create new Drupal 10 project
15+
if [ ! -f "$drupal_dir/composer.json" ]; then
16+
composer --no-interaction create-project drupal/recommended-project:^10 "$drupal_dir"
17+
fi
18+
# Copy our code into the modules folder
19+
mkdir -p "$drupal_dir/$module_path"
20+
# https://stackoverflow.com/a/15373763
21+
rsync --archive --compress . --filter=':- .gitignore' --exclude "$drupal_dir" --exclude .git "$drupal_dir/$module_path"
22+
23+
drupal_composer config minimum-stability dev
24+
25+
drupal_composer --append repositories.os2web/os2web_key path "$module_path"
26+
27+
# Allow ALL plugins
28+
# https://getcomposer.org/doc/06-config.md#allow-plugins
29+
drupal_composer config --no-plugins allow-plugins true
30+
31+
# Making Drupal 10 compatible
32+
drupal_composer require psr/http-message:^1.0
33+
drupal_composer require mglaman/composer-drupal-lenient
34+
drupal_composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/coc_forms_auto_export", "drupal/webform_node_element"]'
35+
36+
drupal_composer require wikimedia/composer-merge-plugin
37+
drupal_composer config extra.merge-plugin.include "$module_path/composer.json"
38+
# https://www.drupal.org/project/drupal/issues/3220043#comment-14845434
39+
drupal_composer require --dev symfony/phpunit-bridge
40+
drupal_composer --no-interaction install
41+
42+
# Run PHPUnit
43+
(cd "$drupal_dir" && vendor/bin/phpunit --configuration web/core/phpunit.xml.dist "$module_path/tests/src/Unit/KeyHelperUnitTest.php")

src/Exception/Exception.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Drupal\os2web_key\Exception;
4+
5+
/**
6+
* OS2Web key base exception.
7+
*/
8+
class Exception extends \Exception {
9+
10+
}

src/Exception/FileException.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Drupal\os2web_key\Exception;
4+
5+
/**
6+
* File exception.
7+
*/
8+
class FileException extends Exception {
9+
10+
}

0 commit comments

Comments
 (0)