diff --git a/includes/webform_encrypt.theme.inc b/includes/webform_encrypt.theme.inc deleted file mode 100644 index 6a5140c3ac39c4aae3a6f5f84a059b146314ae78..0000000000000000000000000000000000000000 --- a/includes/webform_encrypt.theme.inc +++ /dev/null @@ -1,183 +0,0 @@ -getViewBuilder('webform_submission'); - - $webform = $webform_submission->getWebform(); - $data = $webform_submission->getData(); - $elements = $webform->getElementsInitialized(); - $data_original = $view_builder->buildElements($elements, $data); - $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); - - foreach ($data_original as $key => &$element) { - if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { - $user = \Drupal::currentUser(); - if ($user->hasPermission('view encrypted values')) { - $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$key]['encrypt_profile']); - $element['#value']['#markup'] = Drupal::service('encryption')->decrypt($element['#value']['#markup'], $encryption_profile); - } - else { - $element['#value']['#markup'] = t('[Value Encrypted]'); - } - } - } - - $variables['data'] = $data_original; -} - -/** - * Prepares variables for webform submission plain text template. - * - * Default template: webform-submission-text.html.twig. - * - * @param array $variables - * An associative array containing the following key: - * - webform_submission: A webform submission. - */ -function webform_encrypt_preprocess_webform_submission_text(array &$variables) { - /** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */ - $webform_submission = $variables['webform_submission']; - - $variables['sid'] = $webform_submission->id(); - $variables['uuid'] = $webform_submission->uuid(); - $variables['is_draft'] = $webform_submission->isDraft() ? t('Yes') : t('No'); - $variables['current_page'] = $webform_submission->getCurrentPage(); - $variables['remote_addr'] = $webform_submission->getRemoteAddr(); - $variables['submitted_by'] = $webform_submission->getOwner()->label(); - $variables['webform'] = $webform_submission->getWebform()->label(); - $variables['created'] = \Drupal\webform\Utility\WebformDateHelper::format($webform_submission->getCreatedTime()); - $variables['completed'] = \Drupal\webform\Utility\WebformDateHelper::format($webform_submission->getCompletedTime()); - $variables['changed'] = \Drupal\webform\Utility\WebformDateHelper::format($webform_submission->getChangedTime()); - - // @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\LanguageFormatter::viewValue() - $languages = \Drupal::languageManager()->getNativeLanguages(); - $langcode = $webform_submission->get('langcode')->value; - $variables['language'] = isset($languages[$langcode]) ? $languages[$langcode]->getName() : $langcode; - - if ($source_url = $webform_submission->getSourceUrl()) { - $variables['uri'] = $source_url->toString(); - } - - if ($source_entity = $webform_submission->getSourceEntity()) { - $variables['submitted_to'] = $source_entity->label(); - } - - /** @var \Drupal\webform\WebformSubmissionViewBuilderInterface $view_builder */ - $view_builder = \Drupal::entityTypeManager()->getViewBuilder('webform_submission'); - - $webform = $webform_submission->getWebform(); - $data = $webform_submission->getData(); - $elements = $webform->getElementsInitialized(); - - $data_original = $view_builder->buildElements($elements, $data, [], 'text'); - $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); - - foreach ($data_original as $key => &$element) { - if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { - $user = \Drupal::currentUser(); - if ($user->hasPermission('view encrypted values')) { - $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$key]['encrypt_profile']); - $element['#value']['#markup'] = Drupal::service('encryption')->decrypt($element['#value']['#markup'], $encryption_profile); - } - else { - $element['#value']['#markup'] = t('[Value Encrypted]'); - } - } - } - - $variables['data'] = $data_original; -} - -/** - * Prepares variables for webform submission YAML template. - * - * Default template: webform-submission-yaml.html.twig. - * - * @param array $variables - * An associative array containing the following key: - * - webform_submission: A webform submission. - */ -function webform_encrypt_preprocess_webform_submission_yaml(array &$variables) { - /** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */ - $webform_submission = $variables['webform_submission']; - - $data = $webform_submission->toArray(TRUE); - $webform = \Drupal\webform\Entity\Webform::load($data['webform_id']); - $elements = $webform->getElementsInitializedAndFlattened(); - $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); - - foreach ($elements as $key => $element) { - if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { - $user = \Drupal::currentUser(); - if ($user->hasPermission('view encrypted values')) { - $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$key]['encrypt_profile']); - $data['data'][$key] = Drupal::service('encryption')->decrypt($data['data'][$key], $encryption_profile); - } - else { - $data['data'][$key] = t('[Value Encrypted]'); - } - } - } - - $yaml = \Drupal\Component\Serialization\Yaml::encode($data); - $yaml = \Drupal\webform\Utility\WebformTidy::tidy($yaml); - $variables['yaml'] = [ - '#markup' => $yaml, - '#allowed_tags' => \Drupal\Component\Utility\Xss::getAdminTagList(), - ];; -} - -/** - * Prepares variables for webform submission table template. - * - * Default template: webform-submission-table.html.twig. - * - * @param array $variables - * An associative array containing the following key: - * - webform_submission: A webform submission. - */ -function webform_encrypt_preprocess_webform_submission_table(array &$variables) { - /** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */ - $webform_submission = $variables['webform_submission']; - - /** @var \Drupal\webform\WebformSubmissionViewBuilderInterface $view_builder */ - $view_builder = \Drupal::entityTypeManager()->getViewBuilder('webform_submission'); - - $webform = $webform_submission->getWebform(); - $data = $webform_submission->getData(); - $elements = $webform->getElementsFlattenedAndHasValue(); - $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); - - foreach ($elements as $key => $element) { - if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { - $user = \Drupal::currentUser(); - if ($user->hasPermission('view encrypted values')) { - $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$key]['encrypt_profile']); - $data[$key] = Drupal::service('encryption')->decrypt($data[$key], $encryption_profile); - } - else { - $data[$key] = t('[Value Encrypted]'); - } - } - } - - $variables['table'] = $view_builder->buildTable($elements, $data); -} diff --git a/src/Element/WebformElementEncrypt.php b/src/Element/WebformElementEncrypt.php index f3b45e0d6d5d48a30dd1d8b160dff31afb82ded6..2ea13f7ce9f0161f7e11d81e5e1c7d3c3d42d4a6 100644 --- a/src/Element/WebformElementEncrypt.php +++ b/src/Element/WebformElementEncrypt.php @@ -49,7 +49,7 @@ class WebformElementEncrypt extends FormElement { '#type' => 'checkbox', '#title' => t('Encrypt this field\'s value'), '#description' => t('Click here to edit encryption settings.', array(':link' => Url::fromRoute('entity.encryption_profile.collection')->toString())), - '#default_value' => $config[$field_name]['encrypt'] ? $config[$field_name]['encrypt'] : 0, + '#default_value' => isset($config[$field_name]['encrypt']) ? $config[$field_name]['encrypt'] : 0, ]; $element['element_encrypt']['encrypt_profile'] = [ diff --git a/src/WebformEncryptSubmissionStorage.php b/src/WebformEncryptSubmissionStorage.php new file mode 100644 index 0000000000000000000000000000000000000000..a9571f89c641427ee77b17bdc06622c3932b5b52 --- /dev/null +++ b/src/WebformEncryptSubmissionStorage.php @@ -0,0 +1,111 @@ +hasPermission('view encrypted values')) { + return '[Value Encrypted]'; + } + + $decrypted_value = \Drupal::service('encryption')->decrypt($string, $encryption_profile); + if ($decrypted_value === FALSE) { + return $string; + } + + return $decrypted_value; + } + + /** + * Returns the Webform Submission data decrypted. + * + * @param \Drupal\webform\Entity\WebformSubmission $webform_submission + * The Webform Submission entity object. + * @param bool $check_permissions + * Flag that controls permissions check. + * + * @return array + * An array containing the Webform Submission decrypted data. + */ + protected function getDecryptedData(WebformSubmission $webform_submission, $check_permissions = TRUE) { + $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); + $webform = $webform_submission->getWebform(); + $elements = $webform->getElementsInitializedFlattenedAndHasValue(); + $data = $webform_submission->getData(); + + foreach ($elements as $element_name => $element) { + // Skip elements that have no data. + if (!isset($data[$element_name])) { + continue; + } + + // Skip elements that are not encrypted. + if (empty($config[$element_name]['encrypt'])) { + continue; + } + + $encryption_profile = EncryptionProfile::load($config[$element_name]['encrypt_profile']); + + // Checks whether is an element with multiple values. + if (is_array($data[$element_name])) { + foreach ($data[$element_name] as $element_value_key => $element_value) { + $data[$element_name][$element_value_key] = $this->decrypt($element_value, $encryption_profile, $check_permissions); + } + + continue; + } + + // Single element value. + $data[$element_name] = $this->decrypt($data[$element_name], $encryption_profile, $check_permissions); + } + + return $data; + } + + /** + * {@inheritdoc} + */ + protected function doPostSave(EntityInterface $entity, $update) { + $data = $this->getDecryptedData($entity, FALSE); + $entity->setData($data); + + /** @var \Drupal\webform\WebformSubmissionInterface $entity */ + parent::doPostSave($entity, $update); + } + + /** + * {@inheritdoc} + */ + protected function loadData(array &$webform_submissions) { + parent::loadData($webform_submissions); + + foreach ($webform_submissions as &$webform_submission) { + $data = $this->getDecryptedData($webform_submission); + $webform_submission->setData($data); + $webform_submission->setOriginalData($data); + } + } + +} diff --git a/tests/Functional/WebformEncryptFunctionalTest.php b/tests/Functional/WebformEncryptFunctionalTest.php new file mode 100644 index 0000000000000000000000000000000000000000..93d4863d6e940d166f9b3983f9f10584dcf48180 --- /dev/null +++ b/tests/Functional/WebformEncryptFunctionalTest.php @@ -0,0 +1,121 @@ +adminUser = $this->drupalCreateUser($this->permissions); + } + + /** + * Test webform field encryption. + */ + public function testFieldEncryption() { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + $this->drupalLogin($this->adminUser); + $encrypted_value = '[Value Encrypted]'; + + // Test admin functionality. + $this->drupalGet('admin/structure/webform/manage/test_encryption'); + // Add a new element and set encryption on it. + $page->clickLink('Add element'); + $page->clickLink('Date'); + $edit = [ + 'key' => 'test_date', + 'title' => 'Test date', + 'encrypt' => 1, + 'encrypt_profile' => 'test_encryption_profile', + ]; + $this->submitForm($edit, 'Save'); + $assert_session->responseContains('Test date has been created'); + + // Make a submission. + $edit = [ + 'test_text_field' => 'Test text field value', + 'test_text_area' => 'Test text area value', + 'test_not_encrypted' => 'Test not encrypted value', + 'test_date' => '2017-08-14', + ]; + $this->drupalPostForm('/webform/test_encryption', $edit, 'Submit'); + $assert_session->responseContains('New submission added to Test encryption.'); + + // Ensure encrypted fields do not show values. + $this->drupalGet('admin/structure/webform/manage/test_encryption/results/submissions'); + $assert_session->responseNotContains($edit['test_text_field']); + $assert_session->responseNotContains($edit['test_text_field']); + $assert_session->responseContains($edit['test_not_encrypted']); + $assert_session->responseNotContains($edit['test_date']); + $submission_path = 'admin/structure/webform/manage/test_encryption/submission/1'; + $this->drupalGet($submission_path); + $text_selector = '.form-item-test-text-field'; + $area_selector = '.form-item-test-text-area'; + $not_encrypted_selector = '.form-item-test-not-encrypted'; + $date_selector = '.form-item-test-date'; + $assert_session->elementTextContains('css', $text_selector, $encrypted_value); + $assert_session->elementTextNotContains('css', $text_selector, $edit['test_text_field']); + $assert_session->elementTextContains('css', $area_selector, $encrypted_value); + $assert_session->elementTextNotContains('css', $area_selector, $edit['test_text_area']); + $assert_session->elementTextContains('css', $not_encrypted_selector, $edit['test_not_encrypted']); + $assert_session->elementTextNotContains('css', $not_encrypted_selector, $encrypted_value); + $assert_session->elementTextContains('css', $date_selector, $encrypted_value); + $assert_session->elementTextNotContains('css', $date_selector, $edit['test_date']); + + // Grant user access to view encrypted values and check again. + $this->grantPermissions(Role::load($this->adminUser->getRoles()[0]), ['view encrypted values']); + $this->drupalGet($submission_path); + $assert_session->responseNotContains($encrypted_value); + $assert_session->elementTextContains('css', $text_selector, $edit['test_text_field']); + $assert_session->elementTextContains('css', $area_selector, $edit['test_text_area']); + $assert_session->elementTextContains('css', $date_selector, '08/14/2017'); + } + +} diff --git a/tests/modules/webform_encrypt_test/config/install/encrypt.profile.test_encryption_profile.yml b/tests/modules/webform_encrypt_test/config/install/encrypt.profile.test_encryption_profile.yml new file mode 100644 index 0000000000000000000000000000000000000000..80fb1f989d1de7111f00b4489d31191f84c9e241 --- /dev/null +++ b/tests/modules/webform_encrypt_test/config/install/encrypt.profile.test_encryption_profile.yml @@ -0,0 +1,12 @@ +langcode: en +status: true +dependencies: + config: + - key.key.test + module: + - encrypt_test +id: test_encryption_profile +label: 'Test encryption profile' +encryption_method: test_encryption_method +encryption_key: test +encryption_method_configuration: { } diff --git a/tests/modules/webform_encrypt_test/config/install/key.key.test.yml b/tests/modules/webform_encrypt_test/config/install/key.key.test.yml new file mode 100644 index 0000000000000000000000000000000000000000..c4b56c389800cb93d01824a197ba1ec1e22beb38 --- /dev/null +++ b/tests/modules/webform_encrypt_test/config/install/key.key.test.yml @@ -0,0 +1,16 @@ +langcode: en +status: true +dependencies: { } +id: test +label: 'Test key' +description: '' +key_type: encryption +key_type_settings: + key_size: 128 +key_provider: config +key_provider_settings: + base64_encoded: false + key_value: 1E265D6FA8C7B333 +key_input: text_field +key_input_settings: + base64_encoded: false diff --git a/tests/modules/webform_encrypt_test/config/install/webform.encrypt.yml b/tests/modules/webform_encrypt_test/config/install/webform.encrypt.yml new file mode 100644 index 0000000000000000000000000000000000000000..148bf386c2b2fef5ab4bb6daf5e393aa31b21fbd --- /dev/null +++ b/tests/modules/webform_encrypt_test/config/install/webform.encrypt.yml @@ -0,0 +1,11 @@ +element: + settings: + test_text_field: + encrypt: '1' + encrypt_profile: test_encryption_profile + test_text_area: + encrypt: '1' + encrypt_profile: test_encryption_profile + test_not_encrypted: + encrypt: '' + encrypt_profile: test_encryption_profile diff --git a/tests/modules/webform_encrypt_test/config/install/webform.webform.test_encryption.yml b/tests/modules/webform_encrypt_test/config/install/webform.webform.test_encryption.yml new file mode 100644 index 0000000000000000000000000000000000000000..fe69e795ee84e69d46007de3ec11fbf00a7d04ad --- /dev/null +++ b/tests/modules/webform_encrypt_test/config/install/webform.webform.test_encryption.yml @@ -0,0 +1,114 @@ +langcode: en +status: open +dependencies: { } +open: null +close: null +uid: 1 +template: false +id: test_encryption +title: 'Test encryption' +description: '' +category: '' +elements: "test_text_field:\n '#type': textfield\n '#title': 'Test text field'\ntest_text_area:\n '#type': textarea\n '#title': 'Test text area'\ntest_not_encrypted:\n '#type': textfield\n '#title': 'Test not encrypted'\n" +css: '' +javascript: '' +settings: + ajax: false + page: true + page_submit_path: '' + page_confirm_path: '' + form_submit_once: false + form_exception_message: '' + form_open_message: '' + form_close_message: '' + form_previous_submissions: true + form_confidential: false + form_confidential_message: '' + form_convert_anonymous: false + form_prepopulate: false + form_prepopulate_source_entity: false + form_prepopulate_source_entity_required: false + form_prepopulate_source_entity_type: '' + form_reset: false + form_disable_autocomplete: false + form_novalidate: false + form_unsaved: false + form_disable_back: false + form_autofocus: false + form_details_toggle: false + submission_label: '' + submission_log: false + submission_user_columns: { } + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_start_label: '' + wizard_complete: true + wizard_complete_label: '' + preview: 0 + preview_label: '' + preview_title: '' + preview_message: '' + preview_attributes: { } + preview_excluded_elements: { } + preview_exclude_empty: true + draft: none + draft_multiple: false + draft_auto_save: false + draft_saved_message: '' + draft_loaded_message: '' + confirmation_type: page + confirmation_title: '' + confirmation_message: '' + confirmation_url: '' + confirmation_attributes: { } + confirmation_back: true + confirmation_back_label: '' + confirmation_back_attributes: { } + limit_total: null + limit_total_message: '' + limit_user: null + limit_user_message: '' + purge: none + purge_days: null + entity_limit_total: null + entity_limit_user: null + results_disabled: false + results_disabled_ignore: false + token_update: false +access: + create: + roles: + - anonymous + - authenticated + users: { } + permissions: { } + view_any: + roles: { } + users: { } + permissions: { } + update_any: + roles: { } + users: { } + permissions: { } + delete_any: + roles: { } + users: { } + permissions: { } + purge_any: + roles: { } + users: { } + permissions: { } + view_own: + roles: { } + users: { } + permissions: { } + update_own: + roles: { } + users: { } + permissions: { } + delete_own: + roles: { } + users: { } + permissions: { } +handlers: { } diff --git a/tests/modules/webform_encrypt_test/webform_encrypt_test.info.yml b/tests/modules/webform_encrypt_test/webform_encrypt_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..96505b830b653d3563b7dd65b78e5547074952c5 --- /dev/null +++ b/tests/modules/webform_encrypt_test/webform_encrypt_test.info.yml @@ -0,0 +1,8 @@ +name: 'Webform Encrypt Test' +type: module +description: 'Provides test config for webform_encrypt module tests.' +package: 'Webform' +core: 8.x +dependencies: + - webform_encrypt:webform_encrypt + - encrypt:encrypt_test diff --git a/webform_encrypt.module b/webform_encrypt.module index 6d76e2d0d0e081e46c1d3331f36b84e48cb82f51..22c6ee17c29e75c465d19e0d64710562c5812df1 100644 --- a/webform_encrypt.module +++ b/webform_encrypt.module @@ -5,23 +5,25 @@ * Main module file for the Webform Encrypt module. */ +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\encrypt\Entity\EncryptionProfile; -use Drupal\Core\Entity\EntityInterface; use Drupal\webform\Entity\WebformSubmission; -module_load_include('inc', 'webform_encrypt', 'includes/webform_encrypt.theme'); - /** - * Implementation of hook_form_FORM_ID_alter(). + * Implements hook_form_FORM_ID_alter(). */ function webform_encrypt_form_webform_ui_element_form_alter(&$form, FormStateInterface $form_state) { - $webform = $form_state->getBuildInfo()['args']['0']; - $element_id = $form_state->getBuildInfo()['args']['1']; - $webform_elements = $webform->getElementsInitializedAndFlattened(); + /** @var \Drupal\webform\WebformElementManagerInterface $element_manager */ + $element_manager = \Drupal::service('plugin.manager.webform.element'); + + $element = $form_state->getBuildInfo()['callback_object']->getElement(); + $element_handler = $element_manager->getElementInstance($element); - $allowed_types = array('textfield', 'textarea'); - if (in_array($webform_elements[$element_id]['#type'], $allowed_types) || in_array($element_id, $allowed_types)) { + // Checks whether the element carries a value. + // Only input elements are allowed to be encrypted. + $is_input_element = $element_handler->isInput($element); + if ($is_input_element) { // To provide the fieldset for encryption fields on element config form. $form['element_encrypt'] = [ '#type' => 'details', @@ -37,43 +39,49 @@ function webform_encrypt_form_webform_ui_element_form_alter(&$form, FormStateInt } } -/** - * Implementation of hook_form_alter(). - */ -function webform_encrypt_form_alter(&$form, &$form_state, $form_id) { - // When we are editing a webform submission. - if (strpos($form_id, 'webform_submission_') === 0) { - $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); - $elements = $form['elements']; - - foreach ($elements as $key => &$element) { - if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { - $encryption_profile = EncryptionProfile::load($config[$key]['encrypt_profile']); - $form['elements'][$key]['#default_value'] = Drupal::service('encryption')->decrypt($element['#default_value'], $encryption_profile); - } - } - } -} - /** * Implements hook_entity_presave(). - * - * @param \Drupal\Core\Entity\EntityInterface $entity */ function webform_encrypt_entity_presave(EntityInterface $entity) { if ($entity instanceof WebformSubmission) { $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); $data_original = $entity->getData(); + $encryption = Drupal::service('encryption'); + foreach ($data_original as $key => $value) { - if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { - $encryption_profile = EncryptionProfile::load($config[$key]['encrypt_profile']); - $data[$key] = Drupal::service('encryption')->encrypt($value, $encryption_profile); - } - else { + if (empty($config[$key]['encrypt'])) { + // Encryption is disabled to this field. $data[$key] = $value; + + continue; + } + + $encryption_profile = EncryptionProfile::load($config[$key]['encrypt_profile']); + + // Checks whether is an element with multiple values. + if (is_array($value)) { + $multiple_values = []; + foreach ($value as $multiple_values_key => $multiple_values_value) { + $multiple_values[$multiple_values_key] = $encryption->encrypt($multiple_values_value, $encryption_profile); + } + $data[$key] = $multiple_values; + + continue; } + + // Single element value. + $data[$key] = $encryption->encrypt($value, $encryption_profile); } + $entity->setData($data); } } + +/** + * Implements hook_entity_type_alter(). + */ +function webform_encrypt_entity_type_alter(array &$entity_types) { + /* @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ + $entity_types['webform_submission']->setStorageClass('Drupal\webform_encrypt\WebformEncryptSubmissionStorage'); +}