From 012d84886e0ff3f5767e1d9e4adcfed0a4b004c5 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 27 Jun 2024 16:10:57 +0200 Subject: [PATCH 1/5] 1062: Force encryption on all elements --- modules/os2forms_encrypt/README.md | 33 ++++++++++++++ .../os2forms_encrypt/os2forms_encrypt.module | 44 +++++++++++------- .../os2forms_encrypt.services.yml | 6 ++- .../src/Commands/Os2FormsEncryptCommands.php | 5 ++- .../src/Form/SettingsForm.php | 18 ++++++++ .../src/Helper/FormHelper.php | 29 ++++++++++++ .../src/Helper/Os2FormsEncryptor.php | 45 ++++++++++++++++++- 7 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 modules/os2forms_encrypt/README.md create mode 100644 modules/os2forms_encrypt/src/Helper/FormHelper.php diff --git a/modules/os2forms_encrypt/README.md b/modules/os2forms_encrypt/README.md new file mode 100644 index 00000000..bc1773ba --- /dev/null +++ b/modules/os2forms_encrypt/README.md @@ -0,0 +1,33 @@ +# OS2Forms Encrypt module + +This module extends and modifies upon [Webform Encrypt](https://www.drupal.org/project/webform_encrypt) +to provide encryption of webform element values in the database. + +## Modifications from the base Webform Encrypt module + +### Encryption time + +Any computed elements, e.g. Computed Twig, may cause issues as +their values are attempted computed after encryption. If any calculations +are done this could result in runtime TypeErrors. + +This is handled by modifying the time at which decryption is made, in +`WebformOs2FormsEncryptSubmissionStorage`. + +### Permissions + +The Webform Encrypt module introduces a `view encrypted values` permission. +This permission should be granted to roles that need to view encrypted values. + +**Note**, that in Drupal 9 and newer drush commands are ran as an +anonymous user. This means the anonymous user needs this permission, if +at any point they need values from submissions to do their job. + +### Configurable per element + +The Webform Encrypt module allows configuration on element level. That is, +webform builders can actively enable and disable for each element. + +We want all elements to be encrypted whenever encryption is enabled. +This is done by `os2forms_encrypt_webform_presave` and `os2forms_encrypt_form_alter` in +`os2forms_encrypt.module`. diff --git a/modules/os2forms_encrypt/os2forms_encrypt.module b/modules/os2forms_encrypt/os2forms_encrypt.module index 9e43af31..b25fbc70 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.module +++ b/modules/os2forms_encrypt/os2forms_encrypt.module @@ -5,23 +5,6 @@ * This module enabled webform submission encryption as a default option. */ -/** - * Implements hook_webform_element_info_alter(). - * - * Add extra processing function to "force" enabled encryption on webform - * elements when they are being saved in the UI. - */ -function os2forms_encrypt_element_info_alter(array &$definitions): void { - foreach ($definitions as $element_id => &$definition) { - if ($element_id === 'webform_element_encrypt') { - $definition['#process'][] = [ - 'Drupal\os2forms_encrypt\Element\WebformElementEncrypt', - 'processWebformElementEncrypt', - ]; - } - } -} - /** * Implements hook_entity_type_alter(). * @@ -45,3 +28,30 @@ function os2forms_encrypt_webform_computed_post_save_field_alter(array &$fields) $fields['value'] = $os2formsEncryptor->encryptValue($fields['value'], $fields['name'], $fields['webform_id']); } + +/** + * Implementes hook_webform_presave(). + * + * Enable encryption on all webform elements, whenever saved. + */ +function os2forms_encrypt_webform_presave(\Drupal\webform\WebformInterface $entity): void { + /** @var \Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor $os2formsEncryptor */ + $os2formsEncryptor = Drupal::service('os2forms_encrypt.encryptor'); + + $os2formsEncryptor->enableEncryption($entity); +} + +/** + * Implements hook_form_alter(). + * + * Removes 'element_encrypt' element from element forms. + * + * The hook_webform_presave method ensures all elements are + * configured to be encrypted, making this element redundant. + */ +function os2forms_encrypt_form_alter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, string $form_id) { + /** @var \Drupal\os2forms_encrypt\Helper\FormHelper $formHelper */ + $formHelper = Drupal::service('os2forms_encrypt.form_helper'); + + $formHelper->formAlter($form, $form_state, $form_id); +} diff --git a/modules/os2forms_encrypt/os2forms_encrypt.services.yml b/modules/os2forms_encrypt/os2forms_encrypt.services.yml index 255a0ef5..04eb9393 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.services.yml +++ b/modules/os2forms_encrypt/os2forms_encrypt.services.yml @@ -1,4 +1,8 @@ services: os2forms_encrypt.encryptor: class: Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor - arguments: ['@encryption', '@entity_type.manager'] + arguments: ['@encryption', '@entity_type.manager', '@config.factory'] + + os2forms_encrypt.form_helper: + class: Drupal\os2forms_encrypt\Helper\FormHelper + diff --git a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php index e8920546..5dc4a2d9 100644 --- a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php +++ b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php @@ -56,12 +56,15 @@ public function enabledEncrypt(): void { return; } + $defaultEncryptionProfile = $config->get('default_encryption_profile'); + // Get the storage for Webform entity type. $webformStorage = $this->entityTypeManager->getStorage('webform'); // Load all webform entities. $webforms = $webformStorage->loadMultiple(); + /** @var \Drupal\webform\Entity\Webform $webform */ foreach ($webforms as $webform) { $elements = $webform->getElementsDecoded(); @@ -72,7 +75,7 @@ public function enabledEncrypt(): void { if (!isset($config['element'][$key])) { $config['element'][$key] = [ 'encrypt' => TRUE, - 'encrypt_profile' => 'webform', + 'encrypt_profile' => $defaultEncryptionProfile, ]; $changed = TRUE; } diff --git a/modules/os2forms_encrypt/src/Form/SettingsForm.php b/modules/os2forms_encrypt/src/Form/SettingsForm.php index 35722d3a..0f3f0862 100644 --- a/modules/os2forms_encrypt/src/Form/SettingsForm.php +++ b/modules/os2forms_encrypt/src/Form/SettingsForm.php @@ -58,6 +58,23 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#default_value' => $config->get('enabled'), ]; + // TODO: RESOLVE WITH DEPENDENCY INJECTION + $encryptionOptions = \Drupal::service('encrypt.encryption_profile.manager') + ->getEncryptionProfileNamesAsOptions(); + + $form['default_encryption_profile'] = [ + '#type' => 'select', + '#title' => $this->t('Default encryption profile'), + '#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.'), + '#options' => $encryptionOptions, + '#default_value' => $config->get('default_encryption_profile'), + '#states' => [ + 'visible' => [ + ':input[name="enabled"]' => ['checked' => TRUE], + ] + ] + ]; + return parent::buildForm($form, $form_state); } @@ -69,6 +86,7 @@ public function submitForm(array &$form, FormStateInterface $form_state): void { $this->config(self::$configName) ->set('enabled', $form_state->getValue('enabled')) + ->set('default_encryption_profile', $form_state->getValue('default_encryption_profile')) ->save(); } diff --git a/modules/os2forms_encrypt/src/Helper/FormHelper.php b/modules/os2forms_encrypt/src/Helper/FormHelper.php new file mode 100644 index 00000000..372f2394 --- /dev/null +++ b/modules/os2forms_encrypt/src/Helper/FormHelper.php @@ -0,0 +1,29 @@ +encryptionService = $encryptService; $this->entityTypeManager = $entityTypeManager; + $this->configFactory = $configFactory; } /** @@ -62,4 +74,35 @@ public function encryptValue(string $value, string $element, string $webformId): return serialize($encrypted_data); } + /** + * Enables encrypt on all elements of webform. + * + * @param WebformInterface $webform + * The webform. + * + * @return void + */ + public function enableEncryption(WebformInterface $webform): void { + + // Check that encryption is enabled. + $config = $this->configFactory->get(SettingsForm::$configName); + if (!$config->get('enabled') || !$webform instanceof Webform) { + return; + } + + // Check that there are any elements to enable encryption on. + $elements = $webform->getElementsDecoded(); + + if (empty($elements)) { + return; + } + + $encryptedElements = array_map(static fn () => [ + 'encrypt' => TRUE, + 'encrypt_profile' => $config->get('default_encryption_profile'), + ] , $elements); + + $webform->setThirdPartySetting('webform_encrypt', 'element', $encryptedElements); + } + } From 9b1e54e84a858438e0981d1b9996458361edc24c Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 27 Jun 2024 16:26:38 +0200 Subject: [PATCH 2/5] 1062: Use dependency injection --- .../os2forms_encrypt.services.yml | 3 ++ .../src/Form/SettingsForm.php | 28 +++++++++++++++++-- .../src/Helper/Os2FormsEncryptor.php | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/modules/os2forms_encrypt/os2forms_encrypt.services.yml b/modules/os2forms_encrypt/os2forms_encrypt.services.yml index 04eb9393..3d474c7f 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.services.yml +++ b/modules/os2forms_encrypt/os2forms_encrypt.services.yml @@ -6,3 +6,6 @@ services: os2forms_encrypt.form_helper: class: Drupal\os2forms_encrypt\Helper\FormHelper + os2forms_encrypt.settings_form: + class: Drupal\os2forms_encrypt\Form\SettingsForm + arguments: ['@config.factory', '@encrypt.encryption_profile.manager'] diff --git a/modules/os2forms_encrypt/src/Form/SettingsForm.php b/modules/os2forms_encrypt/src/Form/SettingsForm.php index 0f3f0862..4b6edcb3 100644 --- a/modules/os2forms_encrypt/src/Form/SettingsForm.php +++ b/modules/os2forms_encrypt/src/Form/SettingsForm.php @@ -2,9 +2,12 @@ namespace Drupal\os2forms_encrypt\Form; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Link; +use Drupal\encrypt\EncryptionProfileManager; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Class SettingsForm. @@ -20,6 +23,27 @@ class SettingsForm extends ConfigFormBase { */ public static string $configName = 'os2forms_encrypt.settings'; + /** + * The config factory. + * + * @var \Drupal\encrypt\EncryptionProfileManager + */ + private EncryptionProfileManager $encryptionProfileManager; + + public function __construct(ConfigFactoryInterface $config_factory, EncryptionProfileManager $encryptionProfileManager) + { + parent::__construct($config_factory); + $this->encryptionProfileManager = $encryptionProfileManager; + } + + public static function create(ContainerInterface $container) + { + return new static( + $container->get('config.factory'), + $container->get('encrypt.encryption_profile.manager') + ); + } + /** * {@inheritdoc} */ @@ -58,9 +82,7 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#default_value' => $config->get('enabled'), ]; - // TODO: RESOLVE WITH DEPENDENCY INJECTION - $encryptionOptions = \Drupal::service('encrypt.encryption_profile.manager') - ->getEncryptionProfileNamesAsOptions(); + $encryptionOptions = $this->encryptionProfileManager->getEncryptionProfileNamesAsOptions(); $form['default_encryption_profile'] = [ '#type' => 'select', diff --git a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php index 29048f03..3dc5df9d 100644 --- a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php +++ b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php @@ -32,7 +32,7 @@ class Os2FormsEncryptor { /** * The config factory. * - * @var ConfigFactoryInterface + * @var \Drupal\Core\Config\ConfigFactoryInterface */ private ConfigFactoryInterface $configFactory; From 608430c72df629723674b5b413bed2223d5bdd51 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 28 Jun 2024 10:01:15 +0200 Subject: [PATCH 3/5] 1062: Coding standards --- modules/os2forms_encrypt/os2forms_encrypt.module | 9 ++++++--- .../src/Commands/Os2FormsEncryptCommands.php | 1 - modules/os2forms_encrypt/src/Form/SettingsForm.php | 13 +++++++------ modules/os2forms_encrypt/src/Helper/FormHelper.php | 11 ++++++----- .../src/Helper/Os2FormsEncryptor.php | 12 +++++------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/modules/os2forms_encrypt/os2forms_encrypt.module b/modules/os2forms_encrypt/os2forms_encrypt.module index b25fbc70..b6a9003c 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.module +++ b/modules/os2forms_encrypt/os2forms_encrypt.module @@ -5,6 +5,9 @@ * This module enabled webform submission encryption as a default option. */ +use Drupal\Core\Form\FormStateInterface; +use Drupal\webform\WebformInterface; + /** * Implements hook_entity_type_alter(). * @@ -30,11 +33,11 @@ function os2forms_encrypt_webform_computed_post_save_field_alter(array &$fields) } /** - * Implementes hook_webform_presave(). + * Implements hook_webform_presave(). * * Enable encryption on all webform elements, whenever saved. */ -function os2forms_encrypt_webform_presave(\Drupal\webform\WebformInterface $entity): void { +function os2forms_encrypt_webform_presave(WebformInterface $entity): void { /** @var \Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor $os2formsEncryptor */ $os2formsEncryptor = Drupal::service('os2forms_encrypt.encryptor'); @@ -49,7 +52,7 @@ function os2forms_encrypt_webform_presave(\Drupal\webform\WebformInterface $enti * The hook_webform_presave method ensures all elements are * configured to be encrypted, making this element redundant. */ -function os2forms_encrypt_form_alter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, string $form_id) { +function os2forms_encrypt_form_alter(array &$form, FormStateInterface $form_state, string $form_id) { /** @var \Drupal\os2forms_encrypt\Helper\FormHelper $formHelper */ $formHelper = Drupal::service('os2forms_encrypt.form_helper'); diff --git a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php index 5dc4a2d9..a34153e5 100644 --- a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php +++ b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php @@ -64,7 +64,6 @@ public function enabledEncrypt(): void { // Load all webform entities. $webforms = $webformStorage->loadMultiple(); - /** @var \Drupal\webform\Entity\Webform $webform */ foreach ($webforms as $webform) { $elements = $webform->getElementsDecoded(); diff --git a/modules/os2forms_encrypt/src/Form/SettingsForm.php b/modules/os2forms_encrypt/src/Form/SettingsForm.php index 4b6edcb3..e80341a8 100644 --- a/modules/os2forms_encrypt/src/Form/SettingsForm.php +++ b/modules/os2forms_encrypt/src/Form/SettingsForm.php @@ -30,14 +30,15 @@ class SettingsForm extends ConfigFormBase { */ private EncryptionProfileManager $encryptionProfileManager; - public function __construct(ConfigFactoryInterface $config_factory, EncryptionProfileManager $encryptionProfileManager) - { + public function __construct(ConfigFactoryInterface $config_factory, EncryptionProfileManager $encryptionProfileManager) { parent::__construct($config_factory); $this->encryptionProfileManager = $encryptionProfileManager; } - public static function create(ContainerInterface $container) - { + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), $container->get('encrypt.encryption_profile.manager') @@ -93,8 +94,8 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#states' => [ 'visible' => [ ':input[name="enabled"]' => ['checked' => TRUE], - ] - ] + ], + ], ]; return parent::buildForm($form, $form_state); diff --git a/modules/os2forms_encrypt/src/Helper/FormHelper.php b/modules/os2forms_encrypt/src/Helper/FormHelper.php index 372f2394..9d16b398 100644 --- a/modules/os2forms_encrypt/src/Helper/FormHelper.php +++ b/modules/os2forms_encrypt/src/Helper/FormHelper.php @@ -4,20 +4,20 @@ use Drupal\Core\Form\FormStateInterface; -class FormHelper -{ +/** + * Form helper class. + */ +class FormHelper { /** * Removes 'element_encrypt' element from element forms. * * @param array $form * The form. - * @param FormStateInterface $form_state + * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * @param string $form_id * The form id. - * - * @return void */ public function formAlter(array &$form, FormStateInterface $form_state, string $form_id) { if ('webform_ui_element_form' === $form_id) { @@ -26,4 +26,5 @@ public function formAlter(array &$form, FormStateInterface $form_state, string $ } } } + } diff --git a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php index 3dc5df9d..bc40911b 100644 --- a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php +++ b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php @@ -77,10 +77,8 @@ public function encryptValue(string $value, string $element, string $webformId): /** * Enables encrypt on all elements of webform. * - * @param WebformInterface $webform + * @param \Drupal\webform\WebformInterface $webform * The webform. - * - * @return void */ public function enableEncryption(WebformInterface $webform): void { @@ -94,13 +92,13 @@ public function enableEncryption(WebformInterface $webform): void { $elements = $webform->getElementsDecoded(); if (empty($elements)) { - return; + return; } $encryptedElements = array_map(static fn () => [ - 'encrypt' => TRUE, - 'encrypt_profile' => $config->get('default_encryption_profile'), - ] , $elements); + 'encrypt' => TRUE, + 'encrypt_profile' => $config->get('default_encryption_profile'), + ], $elements); $webform->setThirdPartySetting('webform_encrypt', 'element', $encryptedElements); } From e60a372ff3b24ba8a16e0d1b7d184dae97dd0194 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 28 Jun 2024 10:03:38 +0200 Subject: [PATCH 4/5] 1062: Updated CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b552c06..e3f76901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ before starting to add changes. Use example [placed in the end of the page](#exa ## [Unreleased] +- [#117](https://github.com/OS2Forms/os2forms/pull/117) + Encrypts all elements if encryption enabled. - [#114](https://github.com/OS2Forms/os2forms/pull/114) Encrypted computed elements. From 6cc8f0d4288099a86b57726a08d11bf787af062e Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Fri, 28 Jun 2024 12:52:13 +0200 Subject: [PATCH 5/5] 1062: Hid encryption element --- modules/os2forms_encrypt/src/Helper/FormHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/os2forms_encrypt/src/Helper/FormHelper.php b/modules/os2forms_encrypt/src/Helper/FormHelper.php index 9d16b398..fe2166d4 100644 --- a/modules/os2forms_encrypt/src/Helper/FormHelper.php +++ b/modules/os2forms_encrypt/src/Helper/FormHelper.php @@ -22,7 +22,7 @@ class FormHelper { public function formAlter(array &$form, FormStateInterface $form_state, string $form_id) { if ('webform_ui_element_form' === $form_id) { if (isset($form['element_encrypt'])) { - unset($form['element_encrypt']); + $form['element_encrypt']['#access'] = FALSE; } } }