Skip to content

Commit d8c7c2e

Browse files
committed
Updated digital post module to allow key and old configuration methods
1 parent 0404c5c commit d8c7c2e

File tree

6 files changed

+293
-13
lines changed

6 files changed

+293
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ before starting to add changes. Use example [placed in the end of the page](#exa
1313

1414
- [#101](https://github.com/OS2Forms/os2forms/pull/101)
1515
- Added support for `os2web_key` in Digital post
16+
- Switched from saving settings in key value store to config, i.e
17+
the module needs to be reconfigured.
1618
- Added support for `os2web_key` in Fasit handler.
1719
- Switched from saving settings in key value store to config, i.e
1820
the module needs to be reconfigured.

modules/os2forms_digital_post/os2forms_digital_post.services.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ services:
3737
- "@Drupal\\os2forms_digital_post\\Helper\\MeMoHelper"
3838
- "@Drupal\\os2forms_digital_post\\Helper\\ForsendelseHelper"
3939
- "@Drupal\\os2forms_digital_post\\Helper\\BeskedfordelerHelper"
40+
- "@Drupal\\os2forms_digital_post\\Helper\\CertificateLocatorHelper"
4041
- "@logger.channel.os2forms_digital_post"
4142
- "@logger.channel.os2forms_digital_post_submission"
4243
- "@os2web_audit.logger"

modules/os2forms_digital_post/src/Form/SettingsForm.php

Lines changed: 178 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Drupal\Core\StringTranslation\StringTranslationTrait;
1212
use Drupal\Core\StringTranslation\TranslatableMarkup;
1313
use Drupal\Core\Url;
14+
use Drupal\os2forms_digital_post\Helper\CertificateLocatorHelper;
1415
use Drupal\os2forms_digital_post\Helper\Settings;
1516
use Symfony\Component\DependencyInjection\ContainerInterface;
1617

@@ -34,6 +35,7 @@ public function __construct(
3435
ConfigFactoryInterface $config_factory,
3536
EntityTypeManagerInterface $entityTypeManager,
3637
private readonly Settings $settings,
38+
private readonly CertificateLocatorHelper $certificateLocatorHelper,
3739
) {
3840
parent::__construct($config_factory);
3941
$this->queueStorage = $entityTypeManager->getStorage('advancedqueue_queue');
@@ -49,6 +51,7 @@ public static function create(ContainerInterface $container) {
4951
$container->get('config.factory'),
5052
$container->get('entity_type.manager'),
5153
$container->get(Settings::class),
54+
$container->get(CertificateLocatorHelper::class),
5255
);
5356
}
5457

@@ -134,17 +137,127 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
134137
'#type' => 'fieldset',
135138
'#title' => $this->t('Certificate'),
136139
'#tree' => TRUE,
140+
];
141+
142+
$form[Settings::CERTIFICATE][Settings::CERTIFICATE_PROVIDER] = [
143+
'#type' => 'select',
144+
'#title' => $this->t('Provider'),
145+
'#options' => [
146+
Settings::PROVIDER_TYPE_FORM => $this->t('Form'),
147+
Settings::PROVIDER_TYPE_KEY => $this->t('Key'),
148+
],
149+
'#default_value' => $this->settings->getEditableValue([Settings::CERTIFICATE, Settings::CERTIFICATE_PROVIDER]) ?? Settings::PROVIDER_TYPE_FORM,
150+
'#description' => $this->t('Specifies which provider to use'),
151+
];
152+
153+
$form[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_TYPE] = [
154+
'#type' => 'select',
155+
'#title' => $this->t('Certificate locator type'),
156+
'#options' => [
157+
CertificateLocatorHelper::LOCATOR_TYPE_AZURE_KEY_VAULT => $this->t('Azure key vault'),
158+
CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM => $this->t('File system'),
159+
],
160+
'#default_value' => $this->settings->getEditableValue([
161+
Settings::CERTIFICATE,
162+
CertificateLocatorHelper::LOCATOR_TYPE,
163+
]) ?? NULL,
164+
'#states' => [
165+
'visible' => [':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM]],
166+
],
167+
'#description' => $this->t('Specifies which locator to use'),
168+
];
137169

138-
Settings::KEY => [
139-
'#type' => 'key_select',
140-
'#key_filters' => [
141-
'type' => 'os2web_key_certificate',
170+
$form[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_TYPE_AZURE_KEY_VAULT] = [
171+
'#type' => 'fieldset',
172+
'#title' => $this->t('Azure key vault'),
173+
'#states' => [
174+
'visible' => [
175+
':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM],
176+
':input[name="certificate[locator_type]"]' => ['value' => CertificateLocatorHelper::LOCATOR_TYPE_AZURE_KEY_VAULT],
177+
],
178+
],
179+
];
180+
181+
$settings = [
182+
CertificateLocatorHelper::LOCATOR_AZURE_KEY_VAULT_TENANT_ID => ['title' => $this->t('Tenant id')],
183+
CertificateLocatorHelper::LOCATOR_AZURE_KEY_VAULT_APPLICATION_ID => ['title' => $this->t('Application id')],
184+
CertificateLocatorHelper::LOCATOR_AZURE_KEY_VAULT_CLIENT_SECRET => ['title' => $this->t('Client secret')],
185+
CertificateLocatorHelper::LOCATOR_AZURE_KEY_VAULT_NAME => ['title' => $this->t('Name')],
186+
CertificateLocatorHelper::LOCATOR_AZURE_KEY_VAULT_SECRET => ['title' => $this->t('Secret')],
187+
CertificateLocatorHelper::LOCATOR_AZURE_KEY_VAULT_VERSION => ['title' => $this->t('Version')],
188+
];
189+
190+
foreach ($settings as $key => $info) {
191+
$form[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_TYPE_AZURE_KEY_VAULT][$key] = [
192+
'#type' => 'textfield',
193+
'#title' => $info['title'],
194+
'#default_value' => $this->settings->getEditableValue([
195+
Settings::CERTIFICATE,
196+
CertificateLocatorHelper::LOCATOR_TYPE_AZURE_KEY_VAULT,
197+
$key,
198+
]) ?? NULL,
199+
'#states' => [
200+
'required' => [
201+
':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM],
202+
':input[name="certificate[locator_type]"]' => ['value' => CertificateLocatorHelper::LOCATOR_TYPE_AZURE_KEY_VAULT],
203+
],
204+
],
205+
];
206+
}
207+
208+
$form[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM] = [
209+
'#type' => 'fieldset',
210+
'#title' => $this->t('File system'),
211+
'#states' => [
212+
'visible' => [
213+
':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM],
214+
':input[name="certificate[locator_type]"]' => ['value' => CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM],
215+
],
216+
],
217+
218+
CertificateLocatorHelper::LOCATOR_FILE_SYSTEM_PATH => [
219+
'#type' => 'textfield',
220+
'#title' => $this->t('Path'),
221+
'#default_value' => $this->settings->getEditableValue([
222+
Settings::CERTIFICATE,
223+
CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM,
224+
CertificateLocatorHelper::LOCATOR_FILE_SYSTEM_PATH,
225+
]) ?? NULL,
226+
'#states' => [
227+
'required' => [
228+
':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM],
229+
':input[name="certificate[locator_type]"]' => ['value' => CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM],
230+
],
142231
],
143-
'#key_description' => FALSE,
144-
'#title' => $this->t('Key'),
145-
'#default_value' => $this->settings->getEditableValue([Settings::CERTIFICATE, Settings::KEY]),
146-
'#required' => TRUE,
147-
'#description' => $this->createDescription([Settings::CERTIFICATE, Settings::KEY]),
232+
],
233+
];
234+
235+
$form[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_PASSPHRASE] = [
236+
'#type' => 'textfield',
237+
'#title' => $this->t('Passphrase'),
238+
'#default_value' => $this->settings->getEditableValue([
239+
Settings::CERTIFICATE,
240+
CertificateLocatorHelper::LOCATOR_PASSPHRASE,
241+
]) ?? '',
242+
'#states' => [
243+
'visible' => [
244+
':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM],
245+
],
246+
],
247+
];
248+
249+
$form[Settings::CERTIFICATE][Settings::PROVIDER_TYPE_KEY] = [
250+
'#type' => 'key_select',
251+
'#key_filters' => [
252+
'type' => 'os2web_key_certificate',
253+
],
254+
'#key_description' => FALSE,
255+
'#title' => $this->t('Key'),
256+
'#default_value' => $this->settings->getEditableValue([Settings::CERTIFICATE, Settings::PROVIDER_TYPE_KEY]),
257+
'#required' => TRUE,
258+
'#description' => $this->createDescription([Settings::CERTIFICATE, Settings::PROVIDER_TYPE_KEY]),
259+
'#states' => [
260+
'visible' => [':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_KEY]],
148261
],
149262
];
150263

@@ -176,15 +289,55 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
176289
),
177290
];
178291

292+
$form['actions']['testCertificate'] = [
293+
'#type' => 'submit',
294+
'#name' => 'testCertificate',
295+
'#value' => $this->t('Test certificate'),
296+
'#states' => [
297+
'visible' => [':input[name="certificate[certificate_provider]"]' => ['value' => Settings::PROVIDER_TYPE_FORM]],
298+
],
299+
];
300+
179301
return $form;
180302
}
181303

304+
/**
305+
* {@inheritdoc}
306+
*
307+
* @phpstan-param array<string, mixed> $form
308+
*/
309+
public function validateForm(array &$form, FormStateInterface $form_state): void {
310+
$triggeringElement = $form_state->getTriggeringElement();
311+
if ('testCertificate' === ($triggeringElement['#name'] ?? NULL)) {
312+
return;
313+
}
314+
315+
$values = $form_state->getValues();
316+
317+
if (Settings::PROVIDER_TYPE_FORM === $values[Settings::CERTIFICATE][Settings::CERTIFICATE_PROVIDER]) {
318+
if (CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM === $values[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_TYPE]) {
319+
$path = $values[Settings::CERTIFICATE][CertificateLocatorHelper::LOCATOR_TYPE_FILE_SYSTEM][CertificateLocatorHelper::LOCATOR_FILE_SYSTEM_PATH] ?? NULL;
320+
if (!file_exists($path)) {
321+
$form_state->setErrorByName('certificate][file_system][path', $this->t('Invalid certificate path: %path', ['%path' => $path]));
322+
}
323+
}
324+
}
325+
326+
parent::validateForm($form, $form_state);
327+
}
328+
182329
/**
183330
* {@inheritdoc}
184331
*
185332
* @phpstan-param array<string, mixed> $form
186333
*/
187334
public function submitForm(array &$form, FormStateInterface $form_state): void {
335+
$triggeringElement = $form_state->getTriggeringElement();
336+
if ('testCertificate' === ($triggeringElement['#name'] ?? NULL)) {
337+
$this->testCertificate();
338+
return;
339+
}
340+
188341
$config = $this->config(Settings::CONFIG_NAME);
189342
foreach ([
190343
Settings::TEST_MODE,
@@ -223,4 +376,20 @@ private function createDescription(string|array $key, ?TranslatableMarkup $descr
223376
return (string) $description;
224377
}
225378

379+
/**
380+
* Test certificate.
381+
*/
382+
private function testCertificate(): void {
383+
try {
384+
385+
$certificateLocator = $this->certificateLocatorHelper->getCertificateLocator();
386+
$certificateLocator->getCertificates();
387+
$this->messenger()->addStatus($this->t('Certificate succesfully tested'));
388+
}
389+
catch (\Throwable $throwable) {
390+
$message = $this->t('Error testing certificate: %message', ['%message' => $throwable->getMessage()]);
391+
$this->messenger()->addError($message);
392+
}
393+
}
394+
226395
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace Drupal\os2forms_digital_post\Helper;
4+
5+
use Drupal\os2forms_digital_post\Exception\CertificateLocatorException;
6+
use GuzzleHttp\Client;
7+
use Http\Adapter\Guzzle7\Client as GuzzleAdapter;
8+
use Http\Factory\Guzzle\RequestFactory;
9+
use ItkDev\AzureKeyVault\Authorisation\VaultToken;
10+
use ItkDev\AzureKeyVault\KeyVault\VaultSecret;
11+
use ItkDev\Serviceplatformen\Certificate\AzureKeyVaultCertificateLocator;
12+
use ItkDev\Serviceplatformen\Certificate\CertificateLocatorInterface;
13+
use ItkDev\Serviceplatformen\Certificate\FilesystemCertificateLocator;
14+
15+
/**
16+
* Certificate locator helper.
17+
*/
18+
class CertificateLocatorHelper {
19+
public const LOCATOR_TYPE = 'locator_type';
20+
public const LOCATOR_TYPE_AZURE_KEY_VAULT = 'azure_key_vault';
21+
public const LOCATOR_TYPE_FILE_SYSTEM = 'file_system';
22+
public const LOCATOR_PASSPHRASE = 'passphrase';
23+
public const LOCATOR_AZURE_KEY_VAULT_TENANT_ID = 'tenant_id';
24+
public const LOCATOR_AZURE_KEY_VAULT_APPLICATION_ID = 'application_id';
25+
public const LOCATOR_AZURE_KEY_VAULT_CLIENT_SECRET = 'client_secret';
26+
public const LOCATOR_AZURE_KEY_VAULT_NAME = 'name';
27+
public const LOCATOR_AZURE_KEY_VAULT_SECRET = 'secret';
28+
public const LOCATOR_AZURE_KEY_VAULT_VERSION = 'version';
29+
public const LOCATOR_FILE_SYSTEM_PATH = 'path';
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function __construct(
35+
private readonly Settings $settings,
36+
) {
37+
}
38+
39+
/**
40+
* Get certificate locator.
41+
*/
42+
public function getCertificateLocator(): CertificateLocatorInterface {
43+
$certificateSettings = $this->settings->getEditableValue(Settings::CERTIFICATE);
44+
45+
$locatorType = $certificateSettings['locator_type'];
46+
$options = $certificateSettings[$locatorType];
47+
$options += [
48+
'passphrase' => $certificateSettings['passphrase'] ?: '',
49+
];
50+
51+
if (self::LOCATOR_TYPE_AZURE_KEY_VAULT === $locatorType) {
52+
$httpClient = new GuzzleAdapter(new Client());
53+
$requestFactory = new RequestFactory();
54+
55+
$vaultToken = new VaultToken($httpClient, $requestFactory);
56+
57+
$token = $vaultToken->getToken(
58+
$options['tenant_id'],
59+
$options['application_id'],
60+
$options['client_secret'],
61+
);
62+
63+
$vault = new VaultSecret(
64+
$httpClient,
65+
$requestFactory,
66+
$options['name'],
67+
$token->getAccessToken()
68+
);
69+
70+
return new AzureKeyVaultCertificateLocator(
71+
$vault,
72+
$options['secret'],
73+
$options['version'],
74+
$options['passphrase'],
75+
);
76+
}
77+
elseif (self::LOCATOR_TYPE_FILE_SYSTEM === $locatorType) {
78+
$certificatepath = realpath($options['path']) ?: NULL;
79+
if (NULL === $certificatepath) {
80+
throw new CertificateLocatorException(sprintf('Invalid certificate path %s', $options['path']));
81+
}
82+
return new FilesystemCertificateLocator($certificatepath, $options['passphrase']);
83+
}
84+
85+
throw new CertificateLocatorException(sprintf('Invalid certificate locator type: %s', $locatorType));
86+
}
87+
88+
}

modules/os2forms_digital_post/src/Helper/DigitalPostHelper.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function __construct(
3535
private readonly MeMoHelper $meMoHelper,
3636
private readonly ForsendelseHelper $forsendelseHelper,
3737
private readonly BeskedfordelerHelper $beskedfordelerHelper,
38+
private readonly CertificateLocatorHelper $certificateLocatorHelper,
3839
private readonly LoggerChannelInterface $logger,
3940
private readonly LoggerChannelInterface $submissionLogger,
4041
private readonly Logger $auditLogger,
@@ -60,14 +61,23 @@ public function __construct(
6061
*/
6162
public function sendDigitalPost(string $type, Message $message, ?ForsendelseI $forsendelse, ?WebformSubmissionInterface $submission = NULL): array {
6263
$senderSettings = $this->settings->getSender();
64+
65+
if (Settings::PROVIDER_TYPE_FORM === $this->settings->getCertificateProvider()) {
66+
$certificateLocator = $this->certificateLocatorHelper->getCertificateLocator();
67+
}
68+
else {
69+
$certificateLocator = new KeyCertificateLocator(
70+
$this->settings->getCertificateKey(),
71+
$this->keyHelper
72+
);
73+
}
74+
6375
$options = [
6476
'test_mode' => (bool) $this->settings->getTestMode(),
6577
'authority_cvr' => $senderSettings[Settings::SENDER_IDENTIFIER],
66-
'certificate_locator' => new KeyCertificateLocator(
67-
$this->settings->getCertificateKey(),
68-
$this->keyHelper
69-
),
78+
'certificate_locator' => $certificateLocator,
7079
];
80+
7181
$service = new SF1601($options);
7282
$transactionId = Serializer::createUuid();
7383

0 commit comments

Comments
 (0)