Skip to content

Commit 119b31e

Browse files
authored
Merge pull request #119 from OS2Forms/develop
Release 3.15.4
2 parents e07f404 + 273c25b commit 119b31e

File tree

10 files changed

+586
-25
lines changed

10 files changed

+586
-25
lines changed

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ before starting to add changes. Use example [placed in the end of the page](#exa
1111

1212
## [Unreleased]
1313

14+
## [3.15.4] 2024-07-08
15+
16+
- [#117](https://github.com/OS2Forms/os2forms/pull/117)
17+
Encrypts all elements if encryption enabled.
18+
- [#114](https://github.com/OS2Forms/os2forms/pull/114)
19+
Encrypted computed elements.
20+
1421
## [3.15.3] 2024-06-25
1522

1623
- [OS-74] Replacing DAWA matrikula select with Datafordeler select
@@ -234,7 +241,9 @@ before starting to add changes. Use example [placed in the end of the page](#exa
234241
- Security in case of vulnerabilities.
235242
```
236243

237-
[Unreleased]: https://github.com/OS2Forms/os2forms/compare/3.15.2...HEAD
244+
[Unreleased]: https://github.com/OS2Forms/os2forms/compare/3.15.4...HEAD
245+
[3.15.4]: https://github.com/OS2Forms/os2forms/compare/3.15.3...3.15.4
246+
[3.15.3]: https://github.com/OS2Forms/os2forms/compare/3.15.2...3.15.3
238247
[3.15.2]: https://github.com/OS2Forms/os2forms/compare/3.15.1...3.15.2
239248
[3.15.1]: https://github.com/OS2Forms/os2forms/compare/3.15.0...3.15.1
240249
[3.15.0]: https://github.com/OS2Forms/os2forms/compare/3.14.1...3.15.0

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@
104104
"2733781 - Add Export to Word Support": "https://www.drupal.org/files/issues/2019-11-22/2733781-47.patch"
105105
},
106106
"drupal/webform": {
107-
"Unlock possibility of using Entity print module export to Word": "https://www.drupal.org/files/issues/2020-02-29/3096552-6.patch"
107+
"Unlock possibility of using Entity print module export to Word": "https://www.drupal.org/files/issues/2020-02-29/3096552-6.patch",
108+
"Webform computed element post save alter": "https://www.drupal.org/files/issues/2024-06-25/webform_computed_post_save_field_alter.patch"
108109
},
109110
"drupal/user_default_page": {
110111
"Warning: in_array() expects parameter 2 to be array, null given in user_default_page_user_logout() (https://www.drupal.org/node/3246986)": "https://www.drupal.org/files/issues/2021-11-01/user_default_page-3246986-2.patch"

modules/os2forms_encrypt/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# OS2Forms Encrypt module
2+
3+
This module extends and modifies upon [Webform Encrypt](https://www.drupal.org/project/webform_encrypt)
4+
to provide encryption of webform element values in the database.
5+
6+
## Modifications from the base Webform Encrypt module
7+
8+
### Encryption time
9+
10+
Any computed elements, e.g. Computed Twig, may cause issues as
11+
their values are attempted computed after encryption. If any calculations
12+
are done this could result in runtime TypeErrors.
13+
14+
This is handled by modifying the time at which decryption is made, in
15+
`WebformOs2FormsEncryptSubmissionStorage`.
16+
17+
### Permissions
18+
19+
The Webform Encrypt module introduces a `view encrypted values` permission.
20+
This permission should be granted to roles that need to view encrypted values.
21+
22+
**Note**, that in Drupal 9 and newer drush commands are ran as an
23+
anonymous user. This means the anonymous user needs this permission, if
24+
at any point they need values from submissions to do their job.
25+
26+
### Configurable per element
27+
28+
The Webform Encrypt module allows configuration on element level. That is,
29+
webform builders can actively enable and disable for each element.
30+
31+
We want all elements to be encrypted whenever encryption is enabled.
32+
This is done by `os2forms_encrypt_webform_presave` and `os2forms_encrypt_form_alter` in
33+
`os2forms_encrypt.module`.

modules/os2forms_encrypt/os2forms_encrypt.module

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,8 @@
55
* This module enabled webform submission encryption as a default option.
66
*/
77

8-
/**
9-
* Implements hook_webform_element_info_alter().
10-
*
11-
* Add extra processing function to "force" enabled encryption on webform
12-
* elements when they are being saved in the UI.
13-
*/
14-
function os2forms_encrypt_element_info_alter(array &$definitions): void {
15-
foreach ($definitions as $element_id => &$definition) {
16-
if ($element_id === 'webform_element_encrypt') {
17-
$definition['#process'][] = [
18-
'Drupal\os2forms_encrypt\Element\WebformElementEncrypt',
19-
'processWebformElementEncrypt',
20-
];
21-
}
22-
}
23-
}
8+
use Drupal\Core\Form\FormStateInterface;
9+
use Drupal\webform\WebformInterface;
2410

2511
/**
2612
* Implements hook_entity_type_alter().
@@ -33,3 +19,42 @@ function os2forms_encrypt_entity_type_alter(array &$entity_types): void {
3319
$entity_types['webform_submission']->setStorageClass('Drupal\os2forms_encrypt\WebformOs2FormsEncryptSubmissionStorage');
3420
}
3521
}
22+
23+
/**
24+
* Implements hook_webform_computed_post_save_field_alter().
25+
*
26+
* Ensure encryption of computed element values.
27+
*/
28+
function os2forms_encrypt_webform_computed_post_save_field_alter(array &$fields): void {
29+
/** @var \Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor $os2formsEncryptor */
30+
$os2formsEncryptor = \Drupal::service('os2forms_encrypt.encryptor');
31+
32+
$fields['value'] = $os2formsEncryptor->encryptValue($fields['value'], $fields['name'], $fields['webform_id']);
33+
}
34+
35+
/**
36+
* Implements hook_webform_presave().
37+
*
38+
* Enable encryption on all webform elements, whenever saved.
39+
*/
40+
function os2forms_encrypt_webform_presave(WebformInterface $entity): void {
41+
/** @var \Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor $os2formsEncryptor */
42+
$os2formsEncryptor = Drupal::service('os2forms_encrypt.encryptor');
43+
44+
$os2formsEncryptor->enableEncryption($entity);
45+
}
46+
47+
/**
48+
* Implements hook_form_alter().
49+
*
50+
* Removes 'element_encrypt' element from element forms.
51+
*
52+
* The hook_webform_presave method ensures all elements are
53+
* configured to be encrypted, making this element redundant.
54+
*/
55+
function os2forms_encrypt_form_alter(array &$form, FormStateInterface $form_state, string $form_id) {
56+
/** @var \Drupal\os2forms_encrypt\Helper\FormHelper $formHelper */
57+
$formHelper = Drupal::service('os2forms_encrypt.form_helper');
58+
59+
$formHelper->formAlter($form, $form_state, $form_id);
60+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
services:
2+
os2forms_encrypt.encryptor:
3+
class: Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor
4+
arguments: ['@encryption', '@entity_type.manager', '@config.factory']
5+
6+
os2forms_encrypt.form_helper:
7+
class: Drupal\os2forms_encrypt\Helper\FormHelper
8+
9+
os2forms_encrypt.settings_form:
10+
class: Drupal\os2forms_encrypt\Form\SettingsForm
11+
arguments: ['@config.factory', '@encrypt.encryption_profile.manager']

modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ public function enabledEncrypt(): void {
5656
return;
5757
}
5858

59+
$defaultEncryptionProfile = $config->get('default_encryption_profile');
60+
5961
// Get the storage for Webform entity type.
6062
$webformStorage = $this->entityTypeManager->getStorage('webform');
6163

@@ -72,7 +74,7 @@ public function enabledEncrypt(): void {
7274
if (!isset($config['element'][$key])) {
7375
$config['element'][$key] = [
7476
'encrypt' => TRUE,
75-
'encrypt_profile' => 'webform',
77+
'encrypt_profile' => $defaultEncryptionProfile,
7678
];
7779
$changed = TRUE;
7880
}

modules/os2forms_encrypt/src/Form/SettingsForm.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
namespace Drupal\os2forms_encrypt\Form;
44

5+
use Drupal\Core\Config\ConfigFactoryInterface;
56
use Drupal\Core\Form\ConfigFormBase;
67
use Drupal\Core\Form\FormStateInterface;
78
use Drupal\Core\Link;
9+
use Drupal\encrypt\EncryptionProfileManager;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
811

912
/**
1013
* Class SettingsForm.
@@ -20,6 +23,28 @@ class SettingsForm extends ConfigFormBase {
2023
*/
2124
public static string $configName = 'os2forms_encrypt.settings';
2225

26+
/**
27+
* The config factory.
28+
*
29+
* @var \Drupal\encrypt\EncryptionProfileManager
30+
*/
31+
private EncryptionProfileManager $encryptionProfileManager;
32+
33+
public function __construct(ConfigFactoryInterface $config_factory, EncryptionProfileManager $encryptionProfileManager) {
34+
parent::__construct($config_factory);
35+
$this->encryptionProfileManager = $encryptionProfileManager;
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
public static function create(ContainerInterface $container) {
42+
return new static(
43+
$container->get('config.factory'),
44+
$container->get('encrypt.encryption_profile.manager')
45+
);
46+
}
47+
2348
/**
2449
* {@inheritdoc}
2550
*/
@@ -58,6 +83,21 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
5883
'#default_value' => $config->get('enabled'),
5984
];
6085

86+
$encryptionOptions = $this->encryptionProfileManager->getEncryptionProfileNamesAsOptions();
87+
88+
$form['default_encryption_profile'] = [
89+
'#type' => 'select',
90+
'#title' => $this->t('Default encryption profile'),
91+
'#description' => $this->t('Upon saving webforms, elements that are not configured to be encrypted will be configured to encrypted with the selected encryption profile. The os2forms-encrypt:enable command will also use the default encryption profile.'),
92+
'#options' => $encryptionOptions,
93+
'#default_value' => $config->get('default_encryption_profile'),
94+
'#states' => [
95+
'visible' => [
96+
':input[name="enabled"]' => ['checked' => TRUE],
97+
],
98+
],
99+
];
100+
61101
return parent::buildForm($form, $form_state);
62102
}
63103

@@ -69,6 +109,7 @@ public function submitForm(array &$form, FormStateInterface $form_state): void {
69109

70110
$this->config(self::$configName)
71111
->set('enabled', $form_state->getValue('enabled'))
112+
->set('default_encryption_profile', $form_state->getValue('default_encryption_profile'))
72113
->save();
73114
}
74115

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Drupal\os2forms_encrypt\Helper;
4+
5+
use Drupal\Core\Form\FormStateInterface;
6+
7+
/**
8+
* Form helper class.
9+
*/
10+
class FormHelper {
11+
12+
/**
13+
* Removes 'element_encrypt' element from element forms.
14+
*
15+
* @param array $form
16+
* The form.
17+
* @param \Drupal\Core\Form\FormStateInterface $form_state
18+
* The form state.
19+
* @param string $form_id
20+
* The form id.
21+
*/
22+
public function formAlter(array &$form, FormStateInterface $form_state, string $form_id) {
23+
if ('webform_ui_element_form' === $form_id) {
24+
if (isset($form['element_encrypt'])) {
25+
$form['element_encrypt']['#access'] = FALSE;
26+
}
27+
}
28+
}
29+
30+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace Drupal\os2forms_encrypt\Helper;
4+
5+
use Drupal\Core\Config\ConfigFactoryInterface;
6+
use Drupal\Core\Entity\EntityTypeManagerInterface;
7+
use Drupal\encrypt\EncryptServiceInterface;
8+
use Drupal\encrypt\Entity\EncryptionProfile;
9+
use Drupal\os2forms_encrypt\Form\SettingsForm;
10+
use Drupal\webform\Entity\Webform;
11+
use Drupal\webform\WebformInterface;
12+
13+
/**
14+
* The Os2FormsEncryptor class.
15+
*/
16+
class Os2FormsEncryptor {
17+
18+
/**
19+
* The encryption Service.
20+
*
21+
* @var \Drupal\encrypt\EncryptServiceInterface
22+
*/
23+
private EncryptServiceInterface $encryptionService;
24+
25+
/**
26+
* The entity type manager.
27+
*
28+
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
29+
*/
30+
private EntityTypeManagerInterface $entityTypeManager;
31+
32+
/**
33+
* The config factory.
34+
*
35+
* @var \Drupal\Core\Config\ConfigFactoryInterface
36+
*/
37+
private ConfigFactoryInterface $configFactory;
38+
39+
public function __construct(EncryptServiceInterface $encryptService, EntityTypeManagerInterface $entityTypeManager, ConfigFactoryInterface $configFactory) {
40+
$this->encryptionService = $encryptService;
41+
$this->entityTypeManager = $entityTypeManager;
42+
$this->configFactory = $configFactory;
43+
}
44+
45+
/**
46+
* Encrypts value if element is configured to be encrypted.
47+
*
48+
* @param string $value
49+
* The value that should be encrypted.
50+
* @param string $element
51+
* The element.
52+
* @param string $webformId
53+
* The webform id.
54+
*
55+
* @return string
56+
* The resulting value.
57+
*/
58+
public function encryptValue(string $value, string $element, string $webformId): string {
59+
/** @var \Drupal\webform\WebformInterface $webform */
60+
$webform = $this->entityTypeManager->getStorage('webform')->load($webformId);
61+
62+
$config = $webform->getThirdPartySetting('webform_encrypt', 'element');
63+
$encryption_profile = isset($config[$element]) ? EncryptionProfile::load($config[$element]['encrypt_profile']) : FALSE;
64+
65+
if (!$encryption_profile) {
66+
return $value;
67+
}
68+
69+
$encrypted_data = [
70+
'data' => base64_encode($this->encryptionService->encrypt($value, $encryption_profile)),
71+
'encrypt_profile' => $encryption_profile->id(),
72+
];
73+
74+
return serialize($encrypted_data);
75+
}
76+
77+
/**
78+
* Enables encrypt on all elements of webform.
79+
*
80+
* @param \Drupal\webform\WebformInterface $webform
81+
* The webform.
82+
*/
83+
public function enableEncryption(WebformInterface $webform): void {
84+
85+
// Check that encryption is enabled.
86+
$config = $this->configFactory->get(SettingsForm::$configName);
87+
if (!$config->get('enabled') || !$webform instanceof Webform) {
88+
return;
89+
}
90+
91+
// Check that there are any elements to enable encryption on.
92+
$elements = $webform->getElementsDecoded();
93+
94+
if (empty($elements)) {
95+
return;
96+
}
97+
98+
$encryptedElements = array_map(static fn () => [
99+
'encrypt' => TRUE,
100+
'encrypt_profile' => $config->get('default_encryption_profile'),
101+
], $elements);
102+
103+
$webform->setThirdPartySetting('webform_encrypt', 'element', $encryptedElements);
104+
}
105+
106+
}

0 commit comments

Comments
 (0)