From 83d72a4e61965da6a150736360eb8ff42e0ca3cf Mon Sep 17 00:00:00 2001 From: Stanislav Kutasevits Date: Wed, 19 Jun 2024 15:19:18 +0300 Subject: [PATCH 01/13] OS-74 - Replacing DAWA matrikula select with Datafordeler select --- CHANGELOG.md | 2 + .../Element/DawaElementAddressMatrikula.php | 47 +++-- .../src/Entity/DatafordelerMatrikula.php | 64 ++++++ .../os2forms_dawa/src/Entity/DawaAddress.php | 18 ++ .../DataLookup/DatafordelerDataLookup.php | 182 ++++++++++++++++++ .../DatafordelerDataLookupInterface.php | 38 ++++ .../os2forms_dawa/src/Service/DawaService.php | 4 +- 7 files changed, 334 insertions(+), 21 deletions(-) create mode 100644 modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php create mode 100644 modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php create mode 100644 modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index db0ded3f..3f71ffaa 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] +- [OS-74] Replacing DAWA matrikula select with Datafordeler select + ## [3.15.2] 2024-05-27 - [#108](https://github.com/OS2Forms/os2forms/pull/108) diff --git a/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php b/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php index fcaf3c51..12cb99f1 100644 --- a/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php +++ b/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php @@ -3,6 +3,7 @@ namespace Drupal\os2forms_dawa\Element; use Drupal\Core\Form\FormStateInterface; +use Drupal\os2forms_dawa\Plugin\os2web\DataLookup\DatafordelerDataLookupInterface; use Drupal\webform\Element\WebformCompositeBase; use Symfony\Component\HttpFoundation\ParameterBag; @@ -94,9 +95,16 @@ public static function getCompositeElements(array $element) { * Array of matrikula options key and the values are identical. */ private static function getMatrikulaOptions($addressValue, array $element) { + $options = []; + /** @var \Drupal\os2forms_dawa\Service\DawaService $dawaService */ $dawaService = \Drupal::service('os2forms_dawa.service'); + /** @var \Drupal\os2forms_dawa\Service\DatafordelerService $datafordelerService */ + + /** @var DatafordelerDataLookupInterface $datafordelerLookup */ + $datafordelerLookup = \Drupal::service('plugin.manager.os2web_datalookup')->createInstance('datafordeler_data_lookup'); + // Getting address. $addressParams = new ParameterBag(); $addressParams->set('q', $addressValue); @@ -106,29 +114,30 @@ private static function getMatrikulaOptions($addressValue, array $element) { $address = $dawaService->getSingleAddress($addressParams); if ($address) { - // Getting matrikula options. - $matrikulaParams = new ParameterBag(); - // Getting municipality code from address. - if ($municipality_code = $address->getMunicipalityCode()) { - $matrikulaParams->set('limit_by_municipality', $municipality_code); - } - // Getting property nr from address. - if ($property_nr = $address->getPropertyNumber()) { - $matrikulaParams->set('limit_by_property', $property_nr); - } - // If the matrikula option must not have the code. - if (isset($element['#remove_code'])) { - $matrikulaParams->set('remove_code', $element['#remove_code']); - } + $addressAccessId = $address->getAccessAddressId(); - // Get the options. - $matrikulaOptions = $dawaService->getMatrikulaMatches($matrikulaParams); + // Find matrikula list from the houseid (husnummer): + $matrikulaIdList = $datafordelerLookup->getMatrikulaIds($addressAccessId); - // Use values as keys. - return array_combine($matrikulaOptions, $matrikulaOptions); + // Find Matrikula entry from matrikulas ID. + if (!empty($matrikulaIdList)) { + foreach ($matrikulaIdList as $matrikulaId) { + $matrikula = $datafordelerLookup->getMatrikulaEntry($matrikulaId); + + if ($matrikula) { + $matrikulaOption = $matrikula->getMatrikulaNumber() . ' ' . $matrikula->getOwnershipName(); + + if (isset($element['#remove_code']) && !$element['#remove_code']) { + $matrikulaOption .= ' (' . $matrikula->getOwnerLicenseCode() . ')'; + } + + $options[$matrikulaOption] = $matrikulaOption; + } + } + } } - return []; + return $options; } /** diff --git a/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php b/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php new file mode 100644 index 00000000..96ab322b --- /dev/null +++ b/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php @@ -0,0 +1,64 @@ +ownerLicenseCode = $jordstykke['properties']['ejerlavskode']; + $this->ownershipName = $jordstykke['properties']['ejerlavsnavn']; + $this->matrikulaNumber = $jordstykke['properties']['matrikelnummer']; + } + } + + public function getOwnerLicenseCode(): string { + return $this->ownerLicenseCode; + } + + public function getOwnershipName(): string { + return $this->ownershipName; + } + + public function getMatrikulaNumber(): string { + return $this->matrikulaNumber; + } + + + +} diff --git a/modules/os2forms_dawa/src/Entity/DawaAddress.php b/modules/os2forms_dawa/src/Entity/DawaAddress.php index 723dde3e..89fe92a7 100644 --- a/modules/os2forms_dawa/src/Entity/DawaAddress.php +++ b/modules/os2forms_dawa/src/Entity/DawaAddress.php @@ -45,6 +45,13 @@ class DawaAddress { */ protected $longitude; + /** + * Address access ID. + * + * @var string + */ + protected $accessAddressId; + /** * DawaAddress constructor. * @@ -61,6 +68,7 @@ public function __construct(array $json) { $this->propertyNumber = $json['adgangsadresse']['esrejendomsnr']; $this->longitude = $json['adgangsadresse']['adgangspunkt']['koordinater'][0]; $this->latitude = $json['adgangsadresse']['adgangspunkt']['koordinater'][1]; + $this->accessAddressId = $json['adgangsadresse']['id']; } } @@ -114,4 +122,14 @@ public function getLongitude() { return $this->longitude; } + /** + * Gets Address access ID. + * + * @return string + * Address access ID. + */ + public function getAccessAddressId() { + return $this->accessAddressId; + } + } diff --git a/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php new file mode 100644 index 00000000..4a5ef8d7 --- /dev/null +++ b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php @@ -0,0 +1,182 @@ +httpClient = $httpClient; + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('http_client') + ); + } + + /** + * {@inheritdoc} + */ + public function getMatrikulaIds(string $addressAccessId) : array { + $url = "https://services.datafordeler.dk/BBR/BBRPublic/1/rest/grund";// + + $configuration = $this->getConfiguration(); + $json = $this->httpClient->request('GET', $url, [ + 'query' => [ + 'husnummer' => $addressAccessId, + 'status' => 7, + 'username' => $configuration['username'], + 'password' => $configuration['password'] + ] + ])->getBody(); + + $jsonDecoded = json_decode($json, TRUE); + if (is_array($jsonDecoded)) { + return $jsonDecoded[0]['jordstykkeList']; + } + + return []; + } + + /** + * {@inheritdoc} + */ + public function getMatrikulaEntry(string $matrikulaId) : ?DatafordelerMatrikula { + $url = "https://services.datafordeler.dk/Matriklen2/Matrikel/2.0.0/rest/SamletFastEjendom"; + + $configuration = $this->getConfiguration(); + $json = $this->httpClient->request('GET', $url, [ + 'query' => [ + 'jordstykkeid' => $matrikulaId, + 'username' => $configuration['username'], + 'password' => $configuration['password'] + ] + ])->getBody(); + + $jsonDecoded = json_decode($json, TRUE); + if (is_array($jsonDecoded)) { + return new DatafordelerMatrikula($jsonDecoded); + } + + return NULL; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return [ + 'username' => '', + 'password' => '', + ] + parent::defaultConfiguration(); + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form['username'] = [ + '#type' => 'textfield', + '#title' => $this->t('Username for service calls'), + '#default_value' => $this->configuration['username'], + '#required' => TRUE, + '#description' => $this->t('Username required for performing API requests'), + ]; + $form['password'] = [ + '#type' => 'textfield', + '#title' => $this->t('Password for service calls'), + '#default_value' => $this->configuration['password'], + '#required' => TRUE, + '#description' => $this->t('Password required for performing API requests'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + parent::validateConfigurationForm($form, $form_state); + +// // Validating 'address_autocomplete_path', 'block_autocomplete_path', +// // 'matrikula_autocomplete_path'. +// $elementsToValidate = [ +// 'address_autocomplete_path', +// 'block_autocomplete_path', +// 'matrikula_autocomplete_path', +// ]; +// foreach ($elementsToValidate as $elementKey) { +// $autocomplete_path = $form_state->getValue($elementKey); +// $json = file_get_contents($autocomplete_path); +// $jsonDecoded = json_decode($json, TRUE); +// if (empty($jsonDecoded)) { +// $form_state->setErrorByName($elementKey, $this->t('URL is not valid or it does not provide the result in the required format')); +// } +// else { +// $entry = reset($jsonDecoded); +// if (!array_key_exists('tekst', $entry)) { +// $form_state->setErrorByName($elementKey, $this->t('URL is not valid or it does not provide the result in the required format')); +// } +// } +// } +// +// // Validating address_api_path. +// $autocomplete_path = $form_state->getValue('address_api_path'); +// // Limiting the output. +// $json = file_get_contents($autocomplete_path . '?per_side=1'); +// $jsonDecoded = json_decode($json, TRUE); +// if (empty($jsonDecoded)) { +// $form_state->setErrorByName('address_api_path', $this->t('URL is not valid or it does not provide the result in the required format')); +// } +// else { +// $entry = reset($jsonDecoded); +// if (!array_key_exists('id', $entry)) { +// $form_state->setErrorByName('address_api_path', $this->t('URL is not valid or it does not provide the result in the required format')); +// } +// } + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $configuration = $this->getConfiguration(); + $configuration['username'] = $form_state->getValue('username'); + $configuration['password'] = $form_state->getValue('password'); + $this->setConfiguration($configuration); + } +} diff --git a/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php new file mode 100644 index 00000000..fb24a715 --- /dev/null +++ b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php @@ -0,0 +1,38 @@ + Date: Fri, 21 Jun 2024 15:38:09 +0300 Subject: [PATCH 02/13] phpcs fixes --- .../Element/DawaElementAddressMatrikula.php | 5 +- .../src/Entity/DatafordelerMatrikula.php | 27 +++++++-- .../DataLookup/DatafordelerDataLookup.php | 60 +++---------------- .../DatafordelerDataLookupInterface.php | 5 +- .../os2forms_dawa/src/Service/DawaService.php | 2 +- 5 files changed, 36 insertions(+), 63 deletions(-) diff --git a/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php b/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php index 12cb99f1..2e75805f 100644 --- a/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php +++ b/modules/os2forms_dawa/src/Element/DawaElementAddressMatrikula.php @@ -3,7 +3,6 @@ namespace Drupal\os2forms_dawa\Element; use Drupal\Core\Form\FormStateInterface; -use Drupal\os2forms_dawa\Plugin\os2web\DataLookup\DatafordelerDataLookupInterface; use Drupal\webform\Element\WebformCompositeBase; use Symfony\Component\HttpFoundation\ParameterBag; @@ -100,9 +99,7 @@ private static function getMatrikulaOptions($addressValue, array $element) { /** @var \Drupal\os2forms_dawa\Service\DawaService $dawaService */ $dawaService = \Drupal::service('os2forms_dawa.service'); - /** @var \Drupal\os2forms_dawa\Service\DatafordelerService $datafordelerService */ - - /** @var DatafordelerDataLookupInterface $datafordelerLookup */ + /** @var \Drupal\os2forms_dawa\Plugin\os2web\DataLookup\DatafordelerDataLookupInterface $datafordelerLookup */ $datafordelerLookup = \Drupal::service('plugin.manager.os2web_datalookup')->createInstance('datafordeler_data_lookup'); // Getting address. diff --git a/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php b/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php index 96ab322b..0e05a272 100644 --- a/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php +++ b/modules/os2forms_dawa/src/Entity/DatafordelerMatrikula.php @@ -5,19 +5,21 @@ /** * Class DatafordelerMatrikula. * - * Wrapper class for Datafordeler matrikula object that easies the matrikula property - * access. + * Wrapper class for Datafordeler matrikula object that easies + * the matrikula property access. */ class DatafordelerMatrikula { /** * Owner licence code / ejerlavskode. + * * @var string */ protected string $ownerLicenseCode; /** * Ownership name / ejerlavsnavn. + * * @var string */ protected string $ownershipName; @@ -25,6 +27,7 @@ class DatafordelerMatrikula { /** * Matrikula number / matrikelnummer. + * * @var string */ protected string $matrikulaNumber; @@ -47,18 +50,34 @@ public function __construct(array $json) { } } + /** + * Returns owner licence code. + * + * @return string + * Owners licence code. + */ public function getOwnerLicenseCode(): string { return $this->ownerLicenseCode; } + /** + * Returns ownership name. + * + * @return string + * ownership name. + */ public function getOwnershipName(): string { return $this->ownershipName; } + /** + * Returns makrikula number. + * + * @return string + * Matrikula number + */ public function getMatrikulaNumber(): string { return $this->matrikulaNumber; } - - } diff --git a/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php index 4a5ef8d7..a702e2d9 100644 --- a/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php +++ b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookup.php @@ -8,7 +8,6 @@ use Drupal\os2web_datalookup\Plugin\os2web\DataLookup\DataLookupBase; use GuzzleHttp\ClientInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpClient\HttpClient; /** * Defines a plugin for Datafordeler Data. @@ -23,7 +22,7 @@ class DatafordelerDataLookup extends DataLookupBase implements DatafordelerDataL /** * The HTTP client to fetch the feed data with. * - * @var ClientInterface + * @var \GuzzleHttp\ClientInterface */ protected $httpClient; @@ -43,7 +42,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('http_client') + $container->get('http_client'), ); } @@ -51,7 +50,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function getMatrikulaIds(string $addressAccessId) : array { - $url = "https://services.datafordeler.dk/BBR/BBRPublic/1/rest/grund";// + $url = "https://services.datafordeler.dk/BBR/BBRPublic/1/rest/grund"; $configuration = $this->getConfiguration(); $json = $this->httpClient->request('GET', $url, [ @@ -59,8 +58,8 @@ public function getMatrikulaIds(string $addressAccessId) : array { 'husnummer' => $addressAccessId, 'status' => 7, 'username' => $configuration['username'], - 'password' => $configuration['password'] - ] + 'password' => $configuration['password'], + ], ])->getBody(); $jsonDecoded = json_decode($json, TRUE); @@ -82,8 +81,8 @@ public function getMatrikulaEntry(string $matrikulaId) : ?DatafordelerMatrikula 'query' => [ 'jordstykkeid' => $matrikulaId, 'username' => $configuration['username'], - 'password' => $configuration['password'] - ] + 'password' => $configuration['password'], + ], ])->getBody(); $jsonDecoded = json_decode($json, TRUE); @@ -126,50 +125,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta return $form; } - /** - * {@inheritdoc} - */ - public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { - parent::validateConfigurationForm($form, $form_state); - -// // Validating 'address_autocomplete_path', 'block_autocomplete_path', -// // 'matrikula_autocomplete_path'. -// $elementsToValidate = [ -// 'address_autocomplete_path', -// 'block_autocomplete_path', -// 'matrikula_autocomplete_path', -// ]; -// foreach ($elementsToValidate as $elementKey) { -// $autocomplete_path = $form_state->getValue($elementKey); -// $json = file_get_contents($autocomplete_path); -// $jsonDecoded = json_decode($json, TRUE); -// if (empty($jsonDecoded)) { -// $form_state->setErrorByName($elementKey, $this->t('URL is not valid or it does not provide the result in the required format')); -// } -// else { -// $entry = reset($jsonDecoded); -// if (!array_key_exists('tekst', $entry)) { -// $form_state->setErrorByName($elementKey, $this->t('URL is not valid or it does not provide the result in the required format')); -// } -// } -// } -// -// // Validating address_api_path. -// $autocomplete_path = $form_state->getValue('address_api_path'); -// // Limiting the output. -// $json = file_get_contents($autocomplete_path . '?per_side=1'); -// $jsonDecoded = json_decode($json, TRUE); -// if (empty($jsonDecoded)) { -// $form_state->setErrorByName('address_api_path', $this->t('URL is not valid or it does not provide the result in the required format')); -// } -// else { -// $entry = reset($jsonDecoded); -// if (!array_key_exists('id', $entry)) { -// $form_state->setErrorByName('address_api_path', $this->t('URL is not valid or it does not provide the result in the required format')); -// } -// } - } - /** * {@inheritdoc} */ @@ -179,4 +134,5 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s $configuration['password'] = $form_state->getValue('password'); $this->setConfiguration($configuration); } + } diff --git a/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php index fb24a715..4dd71a3c 100644 --- a/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php +++ b/modules/os2forms_dawa/src/Plugin/os2web/DataLookup/DatafordelerDataLookupInterface.php @@ -15,7 +15,7 @@ interface DatafordelerDataLookupInterface extends DataLookupInterface { /** - * Returns list of ID for Matrikula / jordstykke that is related with this address. + * Returns list of ID for Matrikula / jordstykke related with this address. * * @param string $addressAccessId * Address to make search against. @@ -31,8 +31,9 @@ public function getMatrikulaIds(string $addressAccessId) : array; * @param string $matrikulaId * Id to make search against. * - * @return DatafordelerMatrikula|NULL + * @return \Drupal\os2forms_dawa\Entity\DatafordelerMatrikula|null * Matrikula entry or NULL. */ public function getMatrikulaEntry(string $matrikulaId) : ?DatafordelerMatrikula; + } diff --git a/modules/os2forms_dawa/src/Service/DawaService.php b/modules/os2forms_dawa/src/Service/DawaService.php index 1c68fa7d..671edcb1 100644 --- a/modules/os2forms_dawa/src/Service/DawaService.php +++ b/modules/os2forms_dawa/src/Service/DawaService.php @@ -7,7 +7,7 @@ use Symfony\Component\HttpFoundation\ParameterBag; /** - * Class DawaService. + * DAWA API service class. * * @package Drupal\os2forms_dawa\Service */ From 9eed4d9eec9eb412e213314fd7f459cc709bd41e Mon Sep 17 00:00:00 2001 From: Stanislav Kutasevits Date: Tue, 25 Jun 2024 11:23:38 +0300 Subject: [PATCH 03/13] Preparing 3.15.3 deploy --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f71ffaa..e020e664 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] +## [3.15.3] 2024-06-25 + - [OS-74] Replacing DAWA matrikula select with Datafordeler select ## [3.15.2] 2024-05-27 From f5090b0a0d78635ca5602e7638a357306c59bd61 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Tue, 25 Jun 2024 12:34:28 +0200 Subject: [PATCH 04/13] 1062: Encryption tweaks --- ...ebformOs2FormsEncryptSubmissionStorage.php | 313 +++++++++++++++++- 1 file changed, 308 insertions(+), 5 deletions(-) diff --git a/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php b/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php index ac02e348..0b0608fa 100644 --- a/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php +++ b/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php @@ -3,25 +3,32 @@ namespace Drupal\os2forms_encrypt; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Session\AccountInterface; +use Drupal\encrypt\EncryptionProfileInterface; +use Drupal\encrypt\Entity\EncryptionProfile; use Drupal\webform\WebformInterface; +use Drupal\webform\WebformSubmissionInterface; use Drupal\webform_encrypt\WebformEncryptSubmissionStorage; use Drupal\webform_revisions\Controller\WebformRevisionsController; /** * This class extension WebformEncryptSubmissionStorage. * - * This is to enabled encryption and decryption for data and checks if webform - * revisions are enabled and runs the same code (copied here as multiple - * inherits is not a thing in PHP). + * This is to encrypt just the data sent to database and check if webform + * revisions are enabled. * - * So the getColumns is an moduleExists check and the rest is copied from - * webform revision storage class. + * This mostly runs the same code (copied here as multiple + * inherits is not a thing in PHP), with minor tweaks. */ class WebformOs2FormsEncryptSubmissionStorage extends WebformEncryptSubmissionStorage { /** * {@inheritdoc} + * + * Overwritten to add if webform_revisions module exists. + * + * @see Drupal\webform\WebformSubmissionStorage::getColumns */ public function getColumns(WebformInterface $webform = NULL, EntityInterface $source_entity = NULL, AccountInterface $account = NULL, $include_elements = TRUE) { if (!\Drupal::moduleHandler()->moduleExists('webform_revisions')) { @@ -172,4 +179,300 @@ public function getColumns(WebformInterface $webform = NULL, EntityInterface $so } } + /** + * {@inheritdoc} + * + * Overwritten to only encrypt data send to database. + * + * @see Drupal\webform\WebformSubmissionStorage::saveData + */ + public function saveData(WebformSubmissionInterface $webform_submission, $delete_first = TRUE) { + // Get submission data rows. + $data_original = $webform_submission->getData(); + + $webform = $webform_submission->getWebform(); + + $encrypted_data = $this->encryptElements($data_original, $webform); + + $webform_submission->setData($encrypted_data); + + $webform_id = $webform_submission->getWebform()->id(); + $sid = $webform_submission->id(); + + $elements = $webform_submission->getWebform()->getElementsInitializedFlattenedAndHasValue(); + $computed_elements = $webform_submission->getWebform()->getElementsComputed(); + + $rows = []; + foreach ($encrypted_data as $name => $item) { + $element = $elements[$name] ?? ['#webform_multiple' => FALSE, '#webform_composite' => FALSE]; + + // Check if this is a computed element which is not + // stored in the database. + $is_computed_element = (isset($computed_elements[$name])) ? TRUE : FALSE; + if ($is_computed_element && empty($element['#store'])) { + continue; + } + + if ($element['#webform_composite']) { + if (is_array($item)) { + $composite_items = (empty($element['#webform_multiple'])) ? [$item] : $item; + foreach ($composite_items as $delta => $composite_item) { + foreach ($composite_item as $property => $value) { + $rows[] = [ + 'webform_id' => $webform_id, + 'sid' => $sid, + 'name' => $name, + 'property' => $property, + 'delta' => $delta, + 'value' => (string) $value, + ]; + } + } + } + } + elseif ($element['#webform_multiple']) { + if (is_array($item)) { + foreach ($item as $delta => $value) { + $rows[] = [ + 'webform_id' => $webform_id, + 'sid' => $sid, + 'name' => $name, + 'property' => '', + 'delta' => $delta, + 'value' => (string) $value, + ]; + } + } + } + else { + $rows[] = [ + 'webform_id' => $webform_id, + 'sid' => $sid, + 'name' => $name, + 'property' => '', + 'delta' => 0, + 'value' => (string) $item, + ]; + } + } + + if ($delete_first) { + // Delete existing submission data rows. + $this->database->delete('webform_submission_data') + ->condition('sid', $sid) + ->execute(); + } + + // Insert new submission data rows. + $query = $this->database + ->insert('webform_submission_data') + ->fields(['webform_id', 'sid', 'name', 'property', 'delta', 'value']); + foreach ($rows as $row) { + $query->values($row); + } + $query->execute(); + + $webform_submission->setData($data_original); + } + + /** + * {@inheritdoc} + * + * Overwritten to avoid webform_submission->getData() before decryption, + * as this may cause issues with computed elements, e.g. computed twig. + * + * @see Drupal\webform_encrypt\WebformEncryptSubmissionStorage::loadData + * @see Drupal\webform\WebformSubmissionStorage::loadData + */ + protected function loadData(array &$webform_submissions) { + + // Load webform submission data. + if ($sids = array_keys($webform_submissions)) { + $submissions_data = []; + + // Initialize all multiple value elements to make sure a value is defined. + $webform_default_data = []; + foreach ($webform_submissions as $sid => $webform_submission) { + /** @var \Drupal\webform\WebformInterface $webform */ + $webform = $webform_submissions[$sid]->getWebform(); + $webform_id = $webform->id(); + if (!isset($webform_default_data[$webform_id])) { + $webform_default_data[$webform_id] = []; + $elements = ($webform) ? $webform->getElementsInitializedFlattenedAndHasValue() : []; + foreach ($elements as $element_key => $element) { + if (!empty($element['#webform_multiple'])) { + $webform_default_data[$webform_id][$element_key] = []; + } + } + } + $submissions_data[$sid] = $webform_default_data[$webform_id]; + } + + /** @var \Drupal\Core\Database\StatementInterface $result */ + $result = $this->database->select('webform_submission_data', 'sd') + ->fields('sd', ['webform_id', 'sid', 'name', 'property', 'delta', 'value']) + ->condition('sd.sid', $sids, 'IN') + ->orderBy('sd.sid', 'ASC') + ->orderBy('sd.name', 'ASC') + ->orderBy('sd.property', 'ASC') + ->orderBy('sd.delta', 'ASC') + ->execute(); + while ($record = $result->fetchAssoc()) { + $sid = $record['sid']; + $name = $record['name']; + + /** @var \Drupal\webform\WebformInterface $webform */ + $webform = $webform_submissions[$sid]->getWebform(); + $elements = ($webform) ? $webform->getElementsInitializedFlattenedAndHasValue() : []; + $element = $elements[$name] ?? ['#webform_multiple' => FALSE, '#webform_composite' => FALSE]; + + if ($element['#webform_composite']) { + if ($element['#webform_multiple']) { + $submissions_data[$sid][$name][$record['delta']][$record['property']] = $record['value']; + } + else { + $submissions_data[$sid][$name][$record['property']] = $record['value']; + } + } + elseif ($element['#webform_multiple']) { + $submissions_data[$sid][$name][$record['delta']] = $record['value']; + } + else { + $submissions_data[$sid][$name] = $record['value']; + } + } + + + foreach ($submissions_data as $sid => $submission_data) { + $this->decryptChildren($submission_data); + $webform_submissions[$sid]->setData($submission_data); + $webform_submissions[$sid]->setOriginalData($submission_data); + } + } + } + + /** + * {@inheritdoc} + * + * Overwritten to avoid encrypting null. + * + * @see Drupal\webform_encrypt\WebformEncryptSubmissionStorage::encryptElements + */ + public function encryptElements(array $data, WebformInterface $webform) { + // Load the configuration. + $config = $webform->getThirdPartySetting('webform_encrypt', 'element'); + + foreach ($data as $element_name => $value) { + $encryption_profile = isset($config[$element_name]) ? EncryptionProfile::load($config[$element_name]['encrypt_profile']) : FALSE; + // If the value is an array and we have a encryption profile. + if ($encryption_profile) { + if (is_array($value)) { + $this->encryptChildren($data[$element_name], $encryption_profile); + } + else { + if (is_null($value)) { + $data[$element_name] = $value; + } else { + $encrypted_value = $this->encrypt($value, $encryption_profile); + // Save the encrypted data value. + $data[$element_name] = $encrypted_value; + } + } + } + } + return $data; + } + + /** + * {@inheritdoc} + * + * Overwritten to avoid encryption, see saveData. + * + * @see Drupal\webform_encrypt\WebformEncryptSubmissionStorage::doPreSave + * @see Drupal\webform\WebformSubmissionStorage::doPreSave + * @see Drupal\Core\Entity\ContentEntityStorageBase::doPreSave + * @see Drupal\Core\Entity\EntityStorageBase::doPreSave + * + */ + protected function doPreSave(EntityInterface $entity) + { + /** @var \Drupal\webform\WebformSubmissionInterface $entity */ + + // From ContentEntityStorageBase.php. + + // Sync the changes made in the fields array to the internal values array. + $entity->updateOriginalValues(); + + if ($entity->getEntityType()->isRevisionable() && !$entity->isNew() && empty($entity->getLoadedRevisionId())) { + // Update the loaded revision id for rare special cases when no loaded + // revision is given when updating an existing entity. This for example + // happens when calling save() in hook_entity_insert(). + $entity->updateLoadedRevisionId(); + } + + $id = $entity->id(); + + // Track the original ID. + if ($entity->getOriginalId() !== NULL) { + $id = $entity->getOriginalId(); + } + + // Track if this entity exists already. + $id_exists = $this->has($id, $entity); + + // A new entity should not already exist. + if ($id_exists && $entity->isNew()) { + throw new EntityStorageException("'{$this->entityTypeId}' entity with ID '$id' already exists."); + } + + // Load the original entity, if any. + if ($id_exists && !isset($entity->original)) { + $entity->original = $this->loadUnchanged($id); + } + + // Allow code to run before saving. + $entity->preSave($this); + $this->invokeHook('presave', $entity); + + if (!$entity->isNew()) { + // If the ID changed then original can't be loaded, throw an exception + // in that case. + if (empty($entity->original) || $entity->id() != $entity->original->id()) { + throw new EntityStorageException("Update existing '{$this->entityTypeId}' entity while changing the ID is not supported."); + } + // Do not allow changing the revision ID when resaving the current + // revision. + if (!$entity->isNewRevision() && $entity->getRevisionId() != $entity->getLoadedRevisionId()) { + throw new EntityStorageException("Update existing '{$this->entityTypeId}' entity revision while changing the revision ID is not supported."); + } + } + + $this->invokeWebformElements('preSave', $entity); + $this->invokeWebformHandlers('preSave', $entity); + + return $id; + } + + /** + * {@inheritdoc} + * + * Overwritten to avoid encrypting null. + * + * @see Drupal\webform_encrypt\WebformEncryptSubmissionStorage::encryptChildren + */ + public function encryptChildren(array &$data, EncryptionProfileInterface $encryption_profile) { + foreach ($data as $key => $value) { + if (is_array($value)) { + $this->encryptChildren($data[$key], $encryption_profile); + } + elseif (is_null($value)) { + $data[$key] = $value; + } + else { + $encrypted_value = $this->encrypt($value, $encryption_profile); + $data[$key] = $encrypted_value; + } + } + } + } From 34432c565e06a9095d82d5ee9f34aa7d997dd7e0 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Tue, 25 Jun 2024 15:48:01 +0200 Subject: [PATCH 05/13] 1062: Encrypt computed elements --- composer.json | 3 +- .../os2forms_encrypt/os2forms_encrypt.module | 12 ++++ .../os2forms_encrypt.services.yml | 4 ++ .../src/Helper/Os2FormsEncryptor.php | 66 +++++++++++++++++++ ...ebformOs2FormsEncryptSubmissionStorage.php | 11 +--- 5 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 modules/os2forms_encrypt/os2forms_encrypt.services.yml create mode 100644 modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php diff --git a/composer.json b/composer.json index 15077228..38ab90b0 100644 --- a/composer.json +++ b/composer.json @@ -104,7 +104,8 @@ "2733781 - Add Export to Word Support": "https://www.drupal.org/files/issues/2019-11-22/2733781-47.patch" }, "drupal/webform": { - "Unlock possibility of using Entity print module export to Word": "https://www.drupal.org/files/issues/2020-02-29/3096552-6.patch" + "Unlock possibility of using Entity print module export to Word": "https://www.drupal.org/files/issues/2020-02-29/3096552-6.patch", + "Webform computed element post save alter": "https://www.drupal.org/files/issues/2024-06-25/webform_computed_post_save_field_alter.patch" }, "drupal/user_default_page": { "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" diff --git a/modules/os2forms_encrypt/os2forms_encrypt.module b/modules/os2forms_encrypt/os2forms_encrypt.module index 516a7fe4..9e43af31 100644 --- a/modules/os2forms_encrypt/os2forms_encrypt.module +++ b/modules/os2forms_encrypt/os2forms_encrypt.module @@ -33,3 +33,15 @@ function os2forms_encrypt_entity_type_alter(array &$entity_types): void { $entity_types['webform_submission']->setStorageClass('Drupal\os2forms_encrypt\WebformOs2FormsEncryptSubmissionStorage'); } } + +/** + * Implements hook_webform_computed_post_save_field_alter(). + * + * Ensure encryption of computed element values. + */ +function os2forms_encrypt_webform_computed_post_save_field_alter(array &$fields): void { + /** @var \Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor $os2formsEncryptor */ + $os2formsEncryptor = \Drupal::service('os2forms_encrypt.encryptor'); + + $fields['value'] = $os2formsEncryptor->encryptValue($fields['value'], $fields['name'], $fields['webform_id']); +} diff --git a/modules/os2forms_encrypt/os2forms_encrypt.services.yml b/modules/os2forms_encrypt/os2forms_encrypt.services.yml new file mode 100644 index 00000000..255a0ef5 --- /dev/null +++ b/modules/os2forms_encrypt/os2forms_encrypt.services.yml @@ -0,0 +1,4 @@ +services: + os2forms_encrypt.encryptor: + class: Drupal\os2forms_encrypt\Helper\Os2FormsEncryptor + arguments: ['@encryption', '@entity_type.manager'] diff --git a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php new file mode 100644 index 00000000..b254f33c --- /dev/null +++ b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php @@ -0,0 +1,66 @@ +encryptionService = $encryptService; + $this->entityTypeManager = $entityTypeManager; + } + + /** + * @param string $value + * The value that should be encrypted. + * @param string $element + * The element. + * @param string $webformId + * The webform id. + * + * @return string + * The encrypted string if element is configured to be encrypted. + */ + public function encryptValue(string $value, string $element, string $webformId): string { + /** @var WebformInterface $webform */ + $webform = $this->entityTypeManager->getStorage('webform')->load($webformId); + + $config = $webform->getThirdPartySetting('webform_encrypt', 'element'); + $encryption_profile = isset($config[$element]) ? EncryptionProfile::load($config[$element]['encrypt_profile']) : FALSE; + + if (!$encryption_profile) { + return $value; + } + + $encrypted_data = [ + 'data' => base64_encode($this->encryptionService->encrypt($value, $encryption_profile)), + 'encrypt_profile' => $encryption_profile->id(), + ]; + + return serialize($encrypted_data); + + } + +} diff --git a/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php b/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php index 0b0608fa..30242bec 100644 --- a/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php +++ b/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php @@ -342,7 +342,6 @@ protected function loadData(array &$webform_submissions) { } } - foreach ($submissions_data as $sid => $submission_data) { $this->decryptChildren($submission_data); $webform_submissions[$sid]->setData($submission_data); @@ -372,7 +371,8 @@ public function encryptElements(array $data, WebformInterface $webform) { else { if (is_null($value)) { $data[$element_name] = $value; - } else { + } + else { $encrypted_value = $this->encrypt($value, $encryption_profile); // Save the encrypted data value. $data[$element_name] = $encrypted_value; @@ -392,14 +392,9 @@ public function encryptElements(array $data, WebformInterface $webform) { * @see Drupal\webform\WebformSubmissionStorage::doPreSave * @see Drupal\Core\Entity\ContentEntityStorageBase::doPreSave * @see Drupal\Core\Entity\EntityStorageBase::doPreSave - * */ - protected function doPreSave(EntityInterface $entity) - { + protected function doPreSave(EntityInterface $entity) { /** @var \Drupal\webform\WebformSubmissionInterface $entity */ - - // From ContentEntityStorageBase.php. - // Sync the changes made in the fields array to the internal values array. $entity->updateOriginalValues(); From 717899bba15792877d89ff4cea1c826e966fdf48 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Tue, 25 Jun 2024 15:55:02 +0200 Subject: [PATCH 06/13] 1062: Code cleanup --- CHANGELOG.md | 3 +++ .../src/Helper/Os2FormsEncryptor.php | 14 +++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e020e664..4b552c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ before starting to add changes. Use example [placed in the end of the page](#exa ## [Unreleased] +- [#114](https://github.com/OS2Forms/os2forms/pull/114) + Encrypted computed elements. + ## [3.15.3] 2024-06-25 - [OS-74] Replacing DAWA matrikula select with Datafordeler select diff --git a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php index b254f33c..160e21a1 100644 --- a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php +++ b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php @@ -2,14 +2,13 @@ namespace Drupal\os2forms_encrypt\Helper; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\encrypt\EncryptServiceInterface; use Drupal\encrypt\Entity\EncryptionProfile; -use Drupal\webform\Entity\WebformSubmission; -use Drupal\webform\WebformInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; +/** + * The Os2FormsEncryptor class. + */ class Os2FormsEncryptor { /** @@ -26,13 +25,14 @@ class Os2FormsEncryptor { */ private EntityTypeManagerInterface $entityTypeManager; - public function __construct(EncryptServiceInterface $encryptService, EntityTypeManagerInterface $entityTypeManager) { $this->encryptionService = $encryptService; $this->entityTypeManager = $entityTypeManager; } /** + * Encrypts value if element is configured to be encrypted. + * * @param string $value * The value that should be encrypted. * @param string $element @@ -41,10 +41,10 @@ public function __construct(EncryptServiceInterface $encryptService, EntityTypeM * The webform id. * * @return string - * The encrypted string if element is configured to be encrypted. + * The resulting value. */ public function encryptValue(string $value, string $element, string $webformId): string { - /** @var WebformInterface $webform */ + /** @var \Drupal\webform\WebformInterface $webform */ $webform = $this->entityTypeManager->getStorage('webform')->load($webformId); $config = $webform->getThirdPartySetting('webform_encrypt', 'element'); From a6613f060994c0034cb307150d47b335962da93a Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Tue, 25 Jun 2024 16:23:19 +0200 Subject: [PATCH 07/13] 1062: Documentation --- modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php | 1 - .../src/WebformOs2FormsEncryptSubmissionStorage.php | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php index 160e21a1..365c0f08 100644 --- a/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php +++ b/modules/os2forms_encrypt/src/Helper/Os2FormsEncryptor.php @@ -60,7 +60,6 @@ public function encryptValue(string $value, string $element, string $webformId): ]; return serialize($encrypted_data); - } } diff --git a/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php b/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php index 30242bec..4416f4ff 100644 --- a/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php +++ b/modules/os2forms_encrypt/src/WebformOs2FormsEncryptSubmissionStorage.php @@ -13,11 +13,16 @@ use Drupal\webform_revisions\Controller\WebformRevisionsController; /** - * This class extension WebformEncryptSubmissionStorage. + * This class extends WebformEncryptSubmissionStorage. * * This is to encrypt just the data sent to database and check if webform * revisions are enabled. * + * The reason we need to tweak the encryption made by + * WebformEncryptSubmissionStorage is that the value of computed elements, + * e.g. Computed Twig, will be attempted computed AFTER encryptio. This can + * cause all sorts of exceptions and type errors on runtime. + * * This mostly runs the same code (copied here as multiple * inherits is not a thing in PHP), with minor tweaks. */ From 012d84886e0ff3f5767e1d9e4adcfed0a4b004c5 Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Thu, 27 Jun 2024 16:10:57 +0200 Subject: [PATCH 08/13] 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 09/13] 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 10/13] 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 11/13] 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 12/13] 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; } } } From 4359ca976dc8c0f070d5224f3516b496ca850bdf Mon Sep 17 00:00:00 2001 From: jekuaitk Date: Mon, 8 Jul 2024 16:40:00 +0200 Subject: [PATCH 13/13] Release 3.15.4 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3f76901..1901caaf 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] +## [3.15.4] 2024-07-08 + - [#117](https://github.com/OS2Forms/os2forms/pull/117) Encrypts all elements if encryption enabled. - [#114](https://github.com/OS2Forms/os2forms/pull/114) @@ -239,7 +241,9 @@ before starting to add changes. Use example [placed in the end of the page](#exa - Security in case of vulnerabilities. ``` -[Unreleased]: https://github.com/OS2Forms/os2forms/compare/3.15.2...HEAD +[Unreleased]: https://github.com/OS2Forms/os2forms/compare/3.15.4...HEAD +[3.15.4]: https://github.com/OS2Forms/os2forms/compare/3.15.3...3.15.4 +[3.15.3]: https://github.com/OS2Forms/os2forms/compare/3.15.2...3.15.3 [3.15.2]: https://github.com/OS2Forms/os2forms/compare/3.15.1...3.15.2 [3.15.1]: https://github.com/OS2Forms/os2forms/compare/3.15.0...3.15.1 [3.15.0]: https://github.com/OS2Forms/os2forms/compare/3.14.1...3.15.0