diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c980d97..6f9b9d8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,23 @@ before starting to add changes. Use example [placed in the end of the page](#exa ## [Unreleased] +- Added webform encryption modules - Adding Lat and Long fetching to DataAddress - CprFetchData adding ajax error fix - [#84](https://github.com/OS2Forms/os2forms/pull/84) Added digital post test command. - Added FBS handler for supporting user creation in library systems +- [#95](https://github.com/OS2Forms/os2forms/pull/95) + - Added `base_url` variable to twig templates. + - Handled tokens in Maestro notification html. +- [#92](https://github.com/OS2Forms/os2forms/pull/92) + Allow denying address protected citizen from webform. +- [#96](https://github.com/OS2Forms/os2forms/pull/96) + NemLogin autologout pop-up styling. +- [#99](https://github.com/OS2Forms/os2forms/pull/99) + Fix coding standards. +- [#102](https://github.com/OS2Forms/os2forms/pull/102) + Fix array access with `purge_days` configuration. ## [3.14.1] 2024-01-16 diff --git a/composer.json b/composer.json index 50d3d8f6..beee4327 100644 --- a/composer.json +++ b/composer.json @@ -55,6 +55,7 @@ "drupal/simple_ldap": "^1.0@alpha", "drupal/simplesamlphp_auth": "^3.2", "drupal/smtp": "^1.0@beta", + "drupal/sodium": "^2.4", "drupal/switch_page_theme": "^4.0", "drupal/telephone_validation": "^2.2", "drupal/token": "^1.5", @@ -62,6 +63,7 @@ "drupal/user_default_page": "^2.1", "drupal/webform": "^6.1", "drupal/webform_composite": "^1.0@RC", + "drupal/webform_encrypt": "^2.0@alpha", "drupal/webform_migrate": "^1.1", "drupal/webform_node_element": "^1.2", "drupal/webform_remote_handlers": "^1.6.0", @@ -112,6 +114,9 @@ }, "drupal/dynamic_entity_reference": { "entityQuery reference JOINs should specify target_type (https://www.drupal.org/project/dynamic_entity_reference/issues/3120952#comment-14141038)": "https://www.drupal.org/files/issues/2021-06-22/entityquery-reference-joins-should-specify-target_type-3120952-24.patch" + }, + "drupal/webform_encrypt": { + "Ensure data is base64 encoded (https://www.drupal.org/project/webform_encrypt/issues/3399414)": "https://git.drupalcode.org/project/webform_encrypt/-/merge_requests/4.patch" } } }, diff --git a/modules/os2forms_attachment/README.md b/modules/os2forms_attachment/README.md index 09ff6cf1..f732ad2f 100644 --- a/modules/os2forms_attachment/README.md +++ b/modules/os2forms_attachment/README.md @@ -13,3 +13,11 @@ To add custom headers/footer ```admin/structure/webform/config/os2forms_attachme To specify headers/footers that will override the default ones on a global level (**Third party settings** -> **Entity print** section): ```admin/structure/webform/config``` To specify headers/footers that will override the default ones on a form level (**Third party settings** -> **Entity print** section): ```/admin/structure/webform/manage/[webform]/settings``` + +# Overwriting templates + +With some setups it might be necessary to overwrite templates +in order to access stylesheets or images. + +See [templates](modules/os2forms_forloeb/README.md#templates) +for more details on how to do this. diff --git a/modules/os2forms_attachment/os2forms_attachment.module b/modules/os2forms_attachment/os2forms_attachment.module index 9f8892c9..86f313a9 100644 --- a/modules/os2forms_attachment/os2forms_attachment.module +++ b/modules/os2forms_attachment/os2forms_attachment.module @@ -8,6 +8,7 @@ use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Site\Settings; use Drupal\os2forms_attachment\Entity\AttachmentComponent; /** @@ -317,3 +318,12 @@ function os2forms_attachment_module_implements_alter(&$implementations, $hook) { } } + +/** + * Implements hook_preprocess(). + * + * Add 'base_url' variable to be used by templates. + */ +function os2forms_attachment_preprocess(&$vars) { + $vars['base_url'] = Settings::get('base_url'); +} diff --git a/modules/os2forms_digital_post/src/Drush/Commands/DigitalPostTestCommands.php b/modules/os2forms_digital_post/src/Drush/Commands/DigitalPostTestCommands.php index 653783ff..8db7f4f1 100644 --- a/modules/os2forms_digital_post/src/Drush/Commands/DigitalPostTestCommands.php +++ b/modules/os2forms_digital_post/src/Drush/Commands/DigitalPostTestCommands.php @@ -26,7 +26,7 @@ public function __construct( private readonly DigitalPostHelper $digitalPostHelper, private readonly Token $token, private readonly EntityPrintPluginManagerInterface $entityPrintPluginManager, - private readonly Settings $digitalPostSettings + private readonly Settings $digitalPostSettings, ) { } @@ -53,12 +53,15 @@ public function __construct( * @command os2forms-digital-post:test:send * @usage os2forms-digital-post:test:send --help */ - public function send(array $recipients, array $options = [ - 'subject' => 'os2forms_digital_post', - 'message' => 'This is a test message from os2forms_digital_post sent on [current-date:html_datetime].', - 'digital-post-type' => SF1601::TYPE_AUTOMATISK_VALG, - 'dump-digital-post-settings' => FALSE, - ]): void { + public function send( + array $recipients, + array $options = [ + 'subject' => 'os2forms_digital_post', + 'message' => 'This is a test message from os2forms_digital_post sent on [current-date:html_datetime].', + 'digital-post-type' => SF1601::TYPE_AUTOMATISK_VALG, + 'dump-digital-post-settings' => FALSE, + ], + ): void { $io = new SymfonyStyle($this->input(), $this->output()); if ($options['dump-digital-post-settings']) { diff --git a/modules/os2forms_digital_post/src/EventSubscriber/BeskedfordelerEventSubscriber.php b/modules/os2forms_digital_post/src/EventSubscriber/BeskedfordelerEventSubscriber.php index 0ebbc4ef..cbef5ad4 100644 --- a/modules/os2forms_digital_post/src/EventSubscriber/BeskedfordelerEventSubscriber.php +++ b/modules/os2forms_digital_post/src/EventSubscriber/BeskedfordelerEventSubscriber.php @@ -23,7 +23,8 @@ public function __construct( private readonly BeskedfordelerHelper $beskedfordelerHelper, private readonly MessageHelper $messageHelper, private readonly WebformHelperSF1601 $webformHelper, - LoggerInterface $logger) { + LoggerInterface $logger, + ) { parent::__construct($logger); } diff --git a/modules/os2forms_digital_post/src/Form/SettingsForm.php b/modules/os2forms_digital_post/src/Form/SettingsForm.php index 0d968590..ad0fd962 100644 --- a/modules/os2forms_digital_post/src/Form/SettingsForm.php +++ b/modules/os2forms_digital_post/src/Form/SettingsForm.php @@ -32,7 +32,7 @@ final class SettingsForm extends FormBase { public function __construct( private readonly Settings $settings, private readonly CertificateLocatorHelper $certificateLocatorHelper, - EntityTypeManagerInterface $entityTypeManager + EntityTypeManagerInterface $entityTypeManager, ) { $this->queueStorage = $entityTypeManager->getStorage('advancedqueue_queue'); } diff --git a/modules/os2forms_digital_post/src/Helper/AbstractMessageHelper.php b/modules/os2forms_digital_post/src/Helper/AbstractMessageHelper.php index 100bac4b..d9fb96a3 100644 --- a/modules/os2forms_digital_post/src/Helper/AbstractMessageHelper.php +++ b/modules/os2forms_digital_post/src/Helper/AbstractMessageHelper.php @@ -24,7 +24,7 @@ abstract class AbstractMessageHelper { public function __construct( readonly protected Settings $settings, readonly protected ElementInfoManager $elementInfoManager, - readonly protected WebformTokenManagerInterface $webformTokenManager + readonly protected WebformTokenManagerInterface $webformTokenManager, ) { } diff --git a/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php b/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php index e24b5093..5b93da5e 100644 --- a/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php +++ b/modules/os2forms_digital_post/src/Helper/BeskedfordelerHelper.php @@ -24,7 +24,8 @@ class BeskedfordelerHelper { public function __construct( private readonly Connection $database, private readonly MeMoHelper $meMoHelper, - LoggerInterface $logger) { + LoggerInterface $logger, + ) { $this->setLogger($logger); } diff --git a/modules/os2forms_digital_post/src/Helper/CertificateLocatorHelper.php b/modules/os2forms_digital_post/src/Helper/CertificateLocatorHelper.php index 1e5d9f12..7c4d4117 100644 --- a/modules/os2forms_digital_post/src/Helper/CertificateLocatorHelper.php +++ b/modules/os2forms_digital_post/src/Helper/CertificateLocatorHelper.php @@ -23,7 +23,7 @@ class CertificateLocatorHelper { * {@inheritdoc} */ public function __construct( - private readonly Settings $settings + private readonly Settings $settings, ) { } diff --git a/modules/os2forms_digital_post/src/Helper/DigitalPostHelper.php b/modules/os2forms_digital_post/src/Helper/DigitalPostHelper.php index 78972ef4..c51f0dee 100644 --- a/modules/os2forms_digital_post/src/Helper/DigitalPostHelper.php +++ b/modules/os2forms_digital_post/src/Helper/DigitalPostHelper.php @@ -34,7 +34,7 @@ public function __construct( private readonly ForsendelseHelper $forsendelseHelper, private readonly BeskedfordelerHelper $beskedfordelerHelper, private readonly LoggerChannelInterface $logger, - private readonly LoggerChannelInterface $submissionLogger + private readonly LoggerChannelInterface $submissionLogger, ) { } diff --git a/modules/os2forms_digital_post/src/Helper/WebformHelperSF1601.php b/modules/os2forms_digital_post/src/Helper/WebformHelperSF1601.php index 4fb86767..f3054f8b 100644 --- a/modules/os2forms_digital_post/src/Helper/WebformHelperSF1601.php +++ b/modules/os2forms_digital_post/src/Helper/WebformHelperSF1601.php @@ -57,7 +57,7 @@ public function __construct( private readonly BeskedfordelerHelper $beskedfordelerHelper, private readonly LoggerChannelInterface $logger, private readonly LoggerChannelInterface $submissionLogger, - private readonly DigitalPostHelper $digitalPostHelper + private readonly DigitalPostHelper $digitalPostHelper, ) { $this->webformSubmissionStorage = $entityTypeManager->getStorage('webform_submission'); $this->queueStorage = $entityTypeManager->getStorage('advancedqueue_queue'); diff --git a/modules/os2forms_digital_post/src/Model/Document.php b/modules/os2forms_digital_post/src/Model/Document.php index 42768ad3..d34c5a57 100644 --- a/modules/os2forms_digital_post/src/Model/Document.php +++ b/modules/os2forms_digital_post/src/Model/Document.php @@ -17,7 +17,7 @@ public function __construct( readonly public string $content, readonly public string $mimeType, readonly public string $filename, - readonly public string $language = self::LANGUAGE_DEFAULT + readonly public string $language = self::LANGUAGE_DEFAULT, ) { } diff --git a/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php b/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php index 6e10480d..3e9cd0a0 100644 --- a/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php +++ b/modules/os2forms_digital_post/src/Plugin/AdvancedQueue/JobType/SendDigitalPostSF1601.php @@ -50,7 +50,7 @@ public function __construct( array $configuration, $plugin_id, $plugin_definition, - WebformHelperSF1601 $helper + WebformHelperSF1601 $helper, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->helper = $helper; diff --git a/modules/os2forms_encrypt/config/install/encrypt.profile.webform.yml b/modules/os2forms_encrypt/config/install/encrypt.profile.webform.yml new file mode 100644 index 00000000..19891dfd --- /dev/null +++ b/modules/os2forms_encrypt/config/install/encrypt.profile.webform.yml @@ -0,0 +1,13 @@ +uuid: 98e9a380-a5d6-4d2a-9176-7c50a9ec7c47 +langcode: en +status: true +dependencies: + config: + - key.key.webform + module: + - sodium +id: webform +label: Webform +encryption_method: sodium +encryption_method_configuration: { } +encryption_key: webform diff --git a/modules/os2forms_encrypt/config/install/key.key.webform.yml b/modules/os2forms_encrypt/config/install/key.key.webform.yml new file mode 100644 index 00000000..aaabea7f --- /dev/null +++ b/modules/os2forms_encrypt/config/install/key.key.webform.yml @@ -0,0 +1,17 @@ +uuid: be3383e8-1b0e-4b50-989f-e132900d02a0 +langcode: en +status: true +dependencies: { } +id: webform +label: Webform +description: 'Encrypt webform submissions' +key_type: encryption +key_type_settings: + key_size: 256 +key_provider: config +key_provider_settings: + key_value: LWD5+0klWZn48ZVs13UVHaHJYawX62PAMd3sklkKj/w= + base64_encoded: true +key_input: text_field +key_input_settings: + base64_encoded: true diff --git a/modules/os2forms_encrypt/config/install/os2forms_encrypt.settings.yml b/modules/os2forms_encrypt/config/install/os2forms_encrypt.settings.yml new file mode 100644 index 00000000..b2c9a5f5 --- /dev/null +++ b/modules/os2forms_encrypt/config/install/os2forms_encrypt.settings.yml @@ -0,0 +1 @@ +enabled: 0 diff --git a/modules/os2forms_encrypt/drush.services.yml b/modules/os2forms_encrypt/drush.services.yml new file mode 100644 index 00000000..ccb7a2aa --- /dev/null +++ b/modules/os2forms_encrypt/drush.services.yml @@ -0,0 +1,6 @@ +services: + os2forms_encrypt.commands: + class: Drupal\os2forms_encrypt\Commands\Os2FormsEncryptCommands + arguments: ['@entity_type.manager', '@config.factory'] + tags: + - { name: drush.command } diff --git a/modules/os2forms_encrypt/os2forms_encrypt.info.yml b/modules/os2forms_encrypt/os2forms_encrypt.info.yml new file mode 100644 index 00000000..5e1e7edf --- /dev/null +++ b/modules/os2forms_encrypt/os2forms_encrypt.info.yml @@ -0,0 +1,11 @@ +name: OS2Forms Encrypt +description: Encryption functionality for OS2Forms web-forms +package: OS2Forms +type: module +version: 1.0.0 +core_version_requirement: ^8 || ^9 || ^10 +dependencies: + - 'drupal:webform_encrypt' + - 'drupal:sodium' + +configure: os2forms_encrypt.admin_settings diff --git a/modules/os2forms_encrypt/os2forms_encrypt.links.menu.yml b/modules/os2forms_encrypt/os2forms_encrypt.links.menu.yml new file mode 100644 index 00000000..c7c63f8c --- /dev/null +++ b/modules/os2forms_encrypt/os2forms_encrypt.links.menu.yml @@ -0,0 +1,5 @@ +os2forms_encrypt.admin_settings: + title: 'OS2Forms Encrypt settings' + parent: system.admin_config_system + description: 'Settings for the OS2Forms Encrypt module' + route_name: os2forms_encrypt.admin_settings diff --git a/modules/os2forms_encrypt/os2forms_encrypt.module b/modules/os2forms_encrypt/os2forms_encrypt.module new file mode 100644 index 00000000..daef0b2d --- /dev/null +++ b/modules/os2forms_encrypt/os2forms_encrypt.module @@ -0,0 +1,23 @@ + &$definition) { + if ($element_id === 'webform_element_encrypt') { + $definition['#process'][] = [ + 'Drupal\os2forms_encrypt\Element\WebformElementEncrypt', + 'processWebformElementEncrypt', + ]; + } + } +} diff --git a/modules/os2forms_encrypt/os2forms_encrypt.routing.yml b/modules/os2forms_encrypt/os2forms_encrypt.routing.yml new file mode 100644 index 00000000..2b23f680 --- /dev/null +++ b/modules/os2forms_encrypt/os2forms_encrypt.routing.yml @@ -0,0 +1,7 @@ +os2forms_encrypt.admin_settings: + path: '/admin/config/os2forms_encrypt/settings' + defaults: + _form: '\Drupal\os2forms_encrypt\Form\SettingsForm' + _title: 'OS2Forms Encrypt settings' + requirements: + _permission: 'administer encrypt' diff --git a/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php new file mode 100644 index 00000000..e8920546 --- /dev/null +++ b/modules/os2forms_encrypt/src/Commands/Os2FormsEncryptCommands.php @@ -0,0 +1,90 @@ +entityTypeManager = $entityTypeManager; + $this->configFactory = $configFactory; + parent::__construct(); + } + + /** + * Enable encrypt for all existing webform elements. + * + * @command os2forms-encrypt:enable + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ + public function enabledEncrypt(): void { + $config = $this->configFactory->get(SettingsForm::$configName); + if (!$config->get('enabled')) { + $this->output()->writeln('Encrypt has not been enabled.'); + return; + } + + // 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(); + $config = $webform->getThirdPartySettings('webform_encrypt'); + + $changed = FALSE; + foreach ($elements as $key => $element) { + if (!isset($config['element'][$key])) { + $config['element'][$key] = [ + 'encrypt' => TRUE, + 'encrypt_profile' => 'webform', + ]; + $changed = TRUE; + } + } + + // Save the webform entity so the changes persist, if any changes where + // made. + if ($changed) { + $webform->setThirdPartySetting('webform_encrypt', 'element', $config['element']); + $webform->save(); + } + } + } + +} diff --git a/modules/os2forms_encrypt/src/Element/WebformElementEncrypt.php b/modules/os2forms_encrypt/src/Element/WebformElementEncrypt.php new file mode 100644 index 00000000..4e271890 --- /dev/null +++ b/modules/os2forms_encrypt/src/Element/WebformElementEncrypt.php @@ -0,0 +1,30 @@ +get('enabled')) { + $element['element_encrypt']['encrypt']['#default_value'] = TRUE; + } + + return $element; + } + +} diff --git a/modules/os2forms_encrypt/src/Form/SettingsForm.php b/modules/os2forms_encrypt/src/Form/SettingsForm.php new file mode 100644 index 00000000..35722d3a --- /dev/null +++ b/modules/os2forms_encrypt/src/Form/SettingsForm.php @@ -0,0 +1,75 @@ +config('os2forms_encrypt.settings'); + + $link = Link::createFromRoute($this->t('administration'), 'entity.key.collection'); + $form['notice'] = [ + '#type' => 'inline_template', + '#template' => '
{{ message|t }}
{{ adminMessage|t }}
', + '#context' => [ + 'title' => 'Please note', + 'message' => 'The encryption key that comes with this module should not be used and should be changed before encrypting anything.', + 'adminMessage' => 'You can modify the key (named "webform") in the keys ' . $link->toString() . ' panel. Additionally, the execution of this command can generate a new 256-bit key for you:dd if=/dev/urandom bs=32 count=1 | base64 -i -', + ], + ]; + + $form['enabled'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Enabled encryption'), + '#description' => $this->t('Enable encryption for all webform fields. Please note that encryption will be applied only to those fields that are modified after enabling this option.'), + '#default_value' => $config->get('enabled'), + ]; + + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state): void { + parent::submitForm($form, $form_state); + + $this->config(self::$configName) + ->set('enabled', $form_state->getValue('enabled')) + ->save(); + } + +} diff --git a/modules/os2forms_forloeb/README.md b/modules/os2forms_forloeb/README.md index 5d00e940..7cf06186 100644 --- a/modules/os2forms_forloeb/README.md +++ b/modules/os2forms_forloeb/README.md @@ -19,7 +19,7 @@ You can also install the module by using Drush: Maestro 3.1 adds a `hook_webform_submission_form_alter` hook which we utilize to send assignment, reminder and escalation notifications by adding a *Maestro notification* handler to a form that spawns a Maestro workflow or assigns a -task. If the notification recipient is identified by an an email address, the +task. If the notification recipient is identified by an email address, the notification is sent as an email, and if the identifier is a Danish CPR number, the notifications is sent as digital post. @@ -44,3 +44,22 @@ must be processed asynchronously. Specify the queue handling notification jobs. #### Templates Define templates for emails and digital post (PDF). + + +To reference assets, e.g. stylesheet or images, in your templates, +you can use the `base_url` Twig variable to get the base URL: + +```html +tokenManager->replace( - $processValue($content['value']), - $submission, - $maestroTokenData - ); + $content['value'] = $processValue($content['value']); } $actionLabel = $this->tokenManager->replace($notificationSetting[MaestroNotificationHandler::NOTIFICATION_ACTION_LABEL], $submission); @@ -538,11 +534,11 @@ public function renderNotification(WebformSubmissionInterface $submission, strin switch ($contentType) { case 'email': - $content = $this->renderHtml($contentType, $subject, $content, $taskUrl, $actionLabel, $submission); + $content = $this->renderHtml($contentType, $subject, $content, $taskUrl, $actionLabel, $submission, $maestroTokenData); break; case 'pdf': - $pdfContent = $this->renderHtml($contentType, $subject, $content, $taskUrl, $actionLabel, $submission); + $pdfContent = $this->renderHtml($contentType, $subject, $content, $taskUrl, $actionLabel, $submission, $maestroTokenData); // Get dompdf plugin from entity_print module. /** @var \Drupal\entity_print\Plugin\EntityPrint\PrintEngine\PdfEngineBase $printer */ @@ -583,6 +579,8 @@ public function renderNotification(WebformSubmissionInterface $submission, strin * The action label. * @param \Drupal\webform\WebformSubmissionInterface $submission * The webform submission. + * @param array $maestroTokenData + * The Maestro token data. * * @return string|MarkupInterface * The rendered content. @@ -593,7 +591,8 @@ private function renderHtml( array $content, string $taskUrl, string $actionLabel, - WebformSubmissionInterface $submission + WebformSubmissionInterface $submission, + array $maestroTokenData, ): string|MarkupInterface { $template = $this->config->get('templates')['notification_' . $type] ?? NULL; if (file_exists($template)) { @@ -615,10 +614,17 @@ private function renderHtml( 'action_label' => $actionLabel, 'webform_submission' => $submission, 'handler' => $this, + 'base_url' => Settings::get('base_url'), ], ]; - return Markup::create(trim((string) $this->webformThemeManager->renderPlain($build))); + $html = trim((string) $this->webformThemeManager->renderPlain($build)); + + return Markup::create($this->tokenManager->replace( + $html, + $submission, + $maestroTokenData + )); } /** diff --git a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php index 8968e03e..7c236f61 100644 --- a/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php +++ b/modules/os2forms_forloeb/src/Plugin/AdvancedQueue/JobType/SendMeastroNotification.php @@ -40,7 +40,7 @@ public function __construct( array $configuration, $plugin_id, $plugin_definition, - private readonly MaestroHelper $helper + private readonly MaestroHelper $helper, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); } diff --git a/modules/os2forms_nemid/README.md b/modules/os2forms_nemid/README.md index 6f67a0e9..98ec5532 100644 --- a/modules/os2forms_nemid/README.md +++ b/modules/os2forms_nemid/README.md @@ -14,6 +14,7 @@ Besides this module adds a special settings to the Third Party Webform settings: - Webform type - Redirect to nemlogin automatically +- Hide form if under address protection Settings: admin/structure/webform/manage/[webform]/settings diff --git a/modules/os2forms_nemid/os2forms_nemid.module b/modules/os2forms_nemid/os2forms_nemid.module index cd86dc49..2d11ae88 100644 --- a/modules/os2forms_nemid/os2forms_nemid.module +++ b/modules/os2forms_nemid/os2forms_nemid.module @@ -11,6 +11,9 @@ use Drupal\migrate\Plugin\MigrateSourceInterface; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\os2forms_nemid\Plugin\WebformElement\NemidElementBase; +use Drupal\os2forms_nemid\Service\FormsHelper; +use Drupal\webform\Utility\WebformFormHelper; +use Drupal\webform\WebformSubmissionInterface; /** * Implements hook_form_FORM_ID_alter(). @@ -21,6 +24,15 @@ function os2forms_nemid_form_webform_os2forms_settings_form_alter(&$form, FormSt os2forms_nemid_webform_third_party_settings_form_alter($form, $form_state); } +/** + * Implements hook_ENTITY_TYPE_prepare_form(). + * + * Prepare webform. + */ +function os2forms_nemid_webform_submission_prepare_form(WebformSubmissionInterface $webform_submission, string $operation, FormStateInterface $form_state): void { + Drupal::service('os2forms_nemid.forms_helper')->webformSubmissionPrepareForm($webform_submission, $operation, $form_state); +} + /** * Implements hook_webform_third_party_settings_form_alter(). */ @@ -71,12 +83,76 @@ function os2forms_nemid_webform_third_party_settings_form_alter(&$form, FormStat '#default_value' => !(empty($settings)) ? $settings['nemlogin_auto_redirect'] : FALSE, '#description' => t('Redirection will happen right after user has is accessing the form, if user is already authenticated via NemID, redirection will not happen.'), ]; + + $nemloginProtectionSettings = $webform->getThirdPartySetting('os2forms', 'os2forms_nemid_address_protection'); + + // OS2Forms NemID. + $form['third_party_settings']['os2forms']['os2forms_nemid_address_protection'] = [ + '#type' => 'details', + '#title' => t('OS2Forms address protection settings'), + '#open' => TRUE, + ]; + + // Nemlogin auto redirect. + $form['third_party_settings']['os2forms']['os2forms_nemid_address_protection']['nemlogin_hide_form'] = [ + '#type' => 'select', + '#options' => [ + FormsHelper::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DEFAULT_BEHAVIOUR => t('No'), + FormsHelper::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DISPLAY_ERROR => t('Yes'), + ], + '#title' => t('Hide form if user is under address protection'), + '#default_value' => !(empty($nemloginProtectionSettings)) ? $nemloginProtectionSettings['nemlogin_hide_form'] : FormsHelper::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DEFAULT_BEHAVIOUR, + '#description' => t('Hides elements and displays error if nemlogin reveals that citizen is under address protection and an address element is found on the webform'), + ]; + + // Nemlogin address protection. + $form['third_party_settings']['os2forms']['os2forms_nemid_address_protection']['nemlogin_hide_message'] = [ + '#title' => t('Access denied message'), + '#type' => 'textarea', + '#default_value' => !(empty($nemloginProtectionSettings)) ? $nemloginProtectionSettings['nemlogin_hide_message'] : '', + '#description' => t('Message shown to user when visiting form'), + '#states' => [ + 'visible' => [ + [':input[name="third_party_settings[os2forms][os2forms_nemid_address_protection][nemlogin_hide_form]"]' => ['value' => FormsHelper::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DISPLAY_ERROR]], + ], + 'required' => [ + [':input[name="third_party_settings[os2forms][os2forms_nemid_address_protection][nemlogin_hide_form]"]' => ['value' => FormsHelper::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DISPLAY_ERROR]], + ], + ], + ]; } /** * Implements hook_webform_submission_form_alter(). */ function os2forms_nemid_webform_submission_form_alter(array &$form, FormStateInterface $form_state, $form_id) { + + // Handle address protection. + if ($tempValue = $form_state->getTemporaryValue(FormsHelper::ADDRESS_PROTECTION_STATE)) { + if (FALSE === ($tempValue['access'] ?? TRUE)) { + // Flattening the elements makes it much easier to access nested elements. + $elements = &WebformFormHelper::flattenElements($form['elements']); + + $message = $tempValue['message'] ?? t('This form cannot be shown because you have address protection'); + + $form['os2forms_nemlogin_message'] = [ + '#theme' => 'status_messages', + '#message_list' => [ + 'error' => [$message], + ], + ]; + + // Hide all actions …. + $form['actions']['#access'] = FALSE; + // … and elements. + foreach ($elements as &$element) { + $element['#access'] = FALSE; + } + } + + return; + } + // Getting webform Nemid settings. /** @var \Drupal\webform\WebformSubmissionInterface Interface $webformSubmission */ $webformSubmission = $form_state->getFormObject()->getEntity(); diff --git a/modules/os2forms_nemid/os2forms_nemid.services.yml b/modules/os2forms_nemid/os2forms_nemid.services.yml index 61f67993..5e159bbc 100644 --- a/modules/os2forms_nemid/os2forms_nemid.services.yml +++ b/modules/os2forms_nemid/os2forms_nemid.services.yml @@ -6,4 +6,4 @@ services: - {name: event_subscriber} os2forms_nemid.forms_helper: class: Drupal\os2forms_nemid\Service\FormsHelper - arguments: ['@os2web_nemlogin.auth_provider', '@plugin.manager.os2web_datalookup'] + arguments: ['@os2web_nemlogin.auth_provider', '@plugin.manager.os2web_datalookup', '@current_route_match'] diff --git a/modules/os2forms_nemid/src/EventSubscriber/NemloginRedirectSubscriber.php b/modules/os2forms_nemid/src/EventSubscriber/NemloginRedirectSubscriber.php index 6297a076..bbc734e9 100644 --- a/modules/os2forms_nemid/src/EventSubscriber/NemloginRedirectSubscriber.php +++ b/modules/os2forms_nemid/src/EventSubscriber/NemloginRedirectSubscriber.php @@ -88,7 +88,8 @@ public function __construct( EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory, MessengerInterface $messenger, - KillSwitch $page_cache_kill_switch) { + KillSwitch $page_cache_kill_switch, + ) { $this->nemloginAuthProvider = $nemloginAuthProvider; $this->account = $account; $this->entityFieldManager = $entity_field_manager; diff --git a/modules/os2forms_nemid/src/Service/FormsHelper.php b/modules/os2forms_nemid/src/Service/FormsHelper.php index e271d552..5f01296d 100644 --- a/modules/os2forms_nemid/src/Service/FormsHelper.php +++ b/modules/os2forms_nemid/src/Service/FormsHelper.php @@ -4,6 +4,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Routing\RouteMatchInterface; use Drupal\os2forms_nemid\Element\NemidCompanyCvrFetchData; use Drupal\os2forms_nemid\Element\NemidCompanyPNumber; use Drupal\os2forms_nemid\Element\NemidCprFetchData; @@ -11,6 +12,7 @@ use Drupal\os2web_datalookup\LookupResult\CprLookupResult; use Drupal\os2web_datalookup\Plugin\DataLookupManager; use Drupal\os2web_nemlogin\Service\AuthProviderService; +use Drupal\webform\WebformSubmissionInterface; /** * FormsHelper. @@ -20,6 +22,32 @@ * @package Drupal\os2forms_nemid\Service */ class FormsHelper { + const ADDRESS_PROTECTION_STATE = 'os2forms_nemlogin_address_protection'; + + /** + * Defines NemID login address protection display error option. + */ + const WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DISPLAY_ERROR = 'os2forms_nemlogin_address_protection_display_error'; + + /** + * Defines NemID login address protection display default behaviour. + */ + const WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DEFAULT_BEHAVIOUR = 'os2forms_nemlogin_address_protection_default_behaviour'; + + /** + * Defines NemID login address related elements. + */ + private const WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_ELEMENT_TYPES = [ + 'os2forms_nemid_address', + 'os2forms_nemid_street', + 'os2forms_nemid_house_nr', + 'os2forms_nemid_floor', + 'os2forms_nemid_apartment_nr', + 'os2forms_nemid_postal_code', + 'os2forms_nemid_city', + 'os2forms_nemid_kommunekode', + 'os2forms_nemid_coaddress', + ]; /** * Auth provider service. @@ -35,6 +63,13 @@ class FormsHelper { */ private $dataLookManager; + /** + * The route match. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ + private RouteMatchInterface $routeMatch; + /** * Constructor. * @@ -42,10 +77,13 @@ class FormsHelper { * Auth provider service. * @param \Drupal\os2web_datalookup\Plugin\DataLookupManager $dataLookPluginManager * Datalookup plugin manager. + * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch + * Route match service. */ - public function __construct(AuthProviderService $authProviderService, DataLookupManager $dataLookPluginManager) { + public function __construct(AuthProviderService $authProviderService, DataLookupManager $dataLookPluginManager, RouteMatchInterface $routeMatch) { $this->authProviderService = $authProviderService; $this->dataLookManager = $dataLookPluginManager; + $this->routeMatch = $routeMatch; } /** @@ -307,4 +345,54 @@ protected function getDataFetchTriggerValue($dataFetchValueFieldName, FormStateI return $value; } + /** + * Implements hook_ENTITY_TYPE_prepare_form(). + */ + public function webformSubmissionPrepareForm(WebformSubmissionInterface $webformSubmission, string $operation, FormStateInterface $formState): void { + // Only perform address protection check when displaying submission form. + $accessCheckRouteNames = [ + // Webform attached to a node. + 'entity.node.canonical', + // Creating a new submission. + 'entity.webform.canonical', + // Editing a submission. + 'entity.webform_submission.edit_form', + ]; + + if (!in_array($this->routeMatch->getRouteName(), $accessCheckRouteNames, TRUE)) { + return; + } + + // Check if hide address protection is selected. + $hideForm = $webformSubmission->getWebform()->getThirdPartySettings('os2forms')['os2forms_nemid_address_protection']['nemlogin_hide_form'] ?? NULL; + + if ($hideForm === self::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_DISPLAY_ERROR) { + $cprResult = $this->retrieveCprLookupResult($formState); + + if ($cprResult && $cprResult->isNameAddressProtected()) { + + // Check if any element violating address + // protection is present in webform. + $elements = $webformSubmission->getWebform()->getElementsDecodedAndFlattened(); + + foreach ($elements as $element) { + + if (in_array($element['#type'], self::WEBFORM_NEM_LOGIN_ADDRESS_PROTECTION_ELEMENT_TYPES)) { + + // Violation detected, + // mark form state with temporary key and return. + $message = $webformSubmission->getWebform()->getThirdPartySettings('os2forms')['os2forms_nemid_address_protection']['nemlogin_hide_message']; + + $formState->setTemporaryValue(self::ADDRESS_PROTECTION_STATE, [ + 'access' => FALSE, + 'message' => $message, + ]); + + return; + } + } + } + } + } + } diff --git a/src/Plugin/WebformHandler/SaveToFileWebformHandler.php b/src/Plugin/WebformHandler/SaveToFileWebformHandler.php index fcb783fa..58910d17 100644 --- a/src/Plugin/WebformHandler/SaveToFileWebformHandler.php +++ b/src/Plugin/WebformHandler/SaveToFileWebformHandler.php @@ -764,7 +764,7 @@ protected function handleError($state, $message, $file_path, $file_type) { */ protected function buildTokenTreeElement( array $token_types = ['webform', 'webform_submission'], - $description = NULL + $description = NULL, ) { $description = $description ?: $this->t('Use [webform_submission:values:ELEMENT_KEY:raw] to get plain text values.'); return parent::buildTokenTreeElement($token_types, $description);