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); + } + }