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. 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..b6a9003c 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.module +++ b/modules/os2forms_encrypt/os2forms_encrypt.module @@ -5,22 +5,8 @@ * 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', - ]; - } - } -} +use Drupal\Core\Form\FormStateInterface; +use Drupal\webform\WebformInterface; /** * Implements hook_entity_type_alter(). @@ -45,3 +31,30 @@ function os2forms_encrypt_webform_computed_post_save_field_alter(array &$fields) $fields['value'] = $os2formsEncryptor->encryptValue($fields['value'], $fields['name'], $fields['webform_id']); } + +/** + * Implements hook_webform_presave(). + * + * Enable encryption on all webform elements, whenever saved. + */ +function os2forms_encrypt_webform_presave(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, 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..3d474c7f 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.services.yml +++ b/modules/os2forms_encrypt/os2forms_encrypt.services.yml @@ -1,4 +1,11 @@ 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 + + os2forms_encrypt.settings_form: + class: Drupal\os2forms_encrypt\Form\SettingsForm + arguments: ['@config.factory', '@encrypt.encryption_profile.manager'] diff --git a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php index e8920546..a34153e5 100644 --- a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php +++ b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php @@ -56,6 +56,8 @@ public function enabledEncrypt(): void { return; } + $defaultEncryptionProfile = $config->get('default_encryption_profile'); + // Get the storage for Webform entity type. $webformStorage = $this->entityTypeManager->getStorage('webform'); @@ -72,7 +74,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..e80341a8 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,28 @@ 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; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('encrypt.encryption_profile.manager') + ); + } + /** * {@inheritdoc} */ @@ -58,6 +83,21 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#default_value' => $config->get('enabled'), ]; + $encryptionOptions = $this->encryptionProfileManager->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 +109,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..fe2166d4 --- /dev/null +++ b/modules/os2forms_encrypt/src/Helper/FormHelper.php @@ -0,0 +1,30 @@ +encryptionService = $encryptService; $this->entityTypeManager = $entityTypeManager; + $this->configFactory = $configFactory; } /** @@ -62,4 +74,33 @@ public function encryptValue(string $value, string $element, string $webformId): return serialize($encrypted_data); } + /** + * Enables encrypt on all elements of webform. + * + * @param \Drupal\webform\WebformInterface $webform + * The webform. + */ + 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); + } + }