diff --git a/includes/webform_encrypt.theme.inc b/includes/webform_encrypt.theme.inc new file mode 100644 index 0000000000000000000000000000000000000000..6a5140c3ac39c4aae3a6f5f84a059b146314ae78 --- /dev/null +++ b/includes/webform_encrypt.theme.inc @@ -0,0 +1,183 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..f3b45e0d6d5d48a30dd1d8b160dff31afb82ded6 --- /dev/null +++ b/src/Element/WebformElementEncrypt.php @@ -0,0 +1,94 @@ + TRUE, + '#process' => [ + [$class, 'processWebformElementEncrypt'], + ], + '#theme_wrappers' => ['form_element'], + ); + } + + /** + * {@inheritdoc} + */ + public static function valueCallback(&$element, $input, FormStateInterface $form_state) { + return; + } + + /** + * Processes element attributes. + */ + public static function processWebformElementEncrypt(&$element, FormStateInterface $form_state, &$complete_form) { + $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); + $values = $form_state->getValues(); + $field_name = $values['key']; + + $encryption_options = \Drupal::service('encrypt.encryption_profile.manager')->getEncryptionProfileNamesAsOptions(); + + if (count($encryption_options) > 0) { + $element['element_encrypt']['encrypt'] = [ + '#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, + ]; + + $element['element_encrypt']['encrypt_profile'] = [ + '#type' => 'select', + '#title' => t('Select Encryption Profile'), + '#options' => $encryption_options, + '#default_value' => isset($config[$field_name]['encrypt_profile']) ? $config[$field_name]['encrypt_profile'] : NULL, + '#states' => [ + 'visible' => [ + [':input[name="properties[encrypt]"]' => ['checked' => TRUE]], + ] + ] + ]; + + $element['#element_validate'] = [[get_called_class(), 'validateWebformElementEncrypt']]; + } + else { + $element['element_encrypt']['message'] = array( + '#markup' => t('Please configure the encryption profile to enable encryption for the element.'), + ); + } + + return $element; + } + + /** + * Validates element attributes. + */ + public static function validateWebformElementEncrypt(&$element, FormStateInterface $form_state, &$complete_form) { + $config = \Drupal::service('config.factory')->getEditable('webform.encrypt')->get('element.settings'); + $values = $form_state->getValues(); + + $field_name = $values['key']; + $config[$field_name] = array( + 'encrypt' => $values['encrypt'], + 'encrypt_profile' => $values['encrypt_profile'], + ); + + \Drupal::service('config.factory')->getEditable('webform.encrypt')->set('element.settings', $config)->save(); + } +} + diff --git a/webform_encrypt.info b/webform_encrypt.info deleted file mode 100644 index b4ff17f5bed6f3bbf4f5a9166d8780bb663d9f1a..0000000000000000000000000000000000000000 --- a/webform_encrypt.info +++ /dev/null @@ -1,10 +0,0 @@ -name = Webform Encrypt -description = Provides encryption for webform components -core = 7.x -package = Webform - -files[] = webform_encrypt.module -files[] = webform_encrypt.install - -dependencies[] = encrypt -dependencies[] = webform diff --git a/webform_encrypt.info.yml b/webform_encrypt.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..f031dc257602b4796dcc9e6f46022a8d1d9a71e5 --- /dev/null +++ b/webform_encrypt.info.yml @@ -0,0 +1,8 @@ +name: 'Webform Encrypt' +type: module +description: 'Provides encryption for webform components.' +package: 'Webform' +core: 8.x +dependencies: + - 'webform' + - 'encrypt' diff --git a/webform_encrypt.install b/webform_encrypt.install index dc37dffdd30187af5432df821beb837ad3286442..244ad4066aca287c2266f12cacc1b298b09be48f 100644 --- a/webform_encrypt.install +++ b/webform_encrypt.install @@ -6,39 +6,27 @@ * module. */ -/** - * Implementation of hook_disable(). - */ -function webform_encrypt_disable() { - drupal_set_message(t('Webform Encrypt has been disabled. However, all submitted data is still encrypted. Please !link to decrypt all data.', array( - '!link' => l(t('uninstall the module'), 'admin/modules/uninstall'), - ))); -} - /** * Implementation of hook_uninstall(). */ function webform_encrypt_uninstall() { - variable_del('webform_encrypt_match_user'); - // Decrypt all encrypted form values. - $components = array(); - $results = db_query('SELECT nid, cid, extra FROM {webform_component}')->fetchAll(); - foreach ($results as $row) { - $components[$row->nid . ':' . $row->cid] = unserialize($row->extra); - } - $data = db_query('SELECT nid, sid, cid, data FROM {webform_submitted_data}')->fetchAll(); - foreach ($data as $row) { - $key = $row->nid . ':' . $row->cid; - if (!empty($components[$key]['encrypt']) && is_array(@unserialize($row->data))) { - db_update('webform_submitted_data') - ->fields(array( - 'data' => decrypt($row->data, array('base64' => TRUE)), - )) - ->condition('nid', $row->nid) - ->condition('sid', $row->sid) - ->condition('cid', $row->cid) + $submissions = \Drupal::database()->select('webform_submission_data', 'wsd') + ->fields('wsd', array()) + ->execute() + ->fetchAll(); + $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); + + foreach ($submissions as $submission) { + if (isset($config[$submission->name]['encrypt']) && $config[$submission->name]['encrypt']) { + $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$submission->name]['encrypt_profile']); + $value = Drupal::service('encryption')->decrypt($submission->value, $encryption_profile); + \Drupal::database()->update('webform_submission_data') + ->fields(array('value' => $value)) + ->condition('sid', $submission->sid) ->execute(); } } + + \Drupal::configFactory()->getEditable('webform.encrypt')->delete(); } diff --git a/webform_encrypt.module b/webform_encrypt.module index 7c50e256d59f0334d35c3b25f38bdfabbbbff476..9eeb4fb9be9b0334d7d52611ce4009d8ef130fae 100644 --- a/webform_encrypt.module +++ b/webform_encrypt.module @@ -5,60 +5,30 @@ * Main module file for the Webform Encrypt module. */ -/** - * Implementation of hook_permission(). - */ -function webform_encrypt_permission() { - return array( - 'view encrypted values' => array( - 'title' => t('View Encrypted Values in Webform Results'), - 'description' => t('Users that do not have this permission will see placeholder text.'), - ), - ); -} - -/** - * Implementation of hook_form_FORM_ID_alter(). - */ -function webform_encrypt_form_webform_admin_settings_alter(&$form, $form_state) { - // Add our config options to the webform settings page. - $form['encrypt'] = array( - '#type' => 'fieldset', - '#title' => t('Webform Encrypt'), - '#collapsible' => TRUE, - '#collapsed' => FALSE, - ); - $form['encrypt']['webform_encrypt_match_user'] = array( - '#type' => 'checkbox', - '#title' => t('Enable email to user matching'), - '#description' => t('If enabled, every time webform sends an email, it will attempt to find a user that matches the email address the mail will be sent to in order to correctly determine permissions.'), - '#default_value' => variable_get('webform_encrypt_match_user', 0), - ); -} +module_load_include('inc', 'webform_encrypt', 'includes/webform_encrypt.theme'); /** * Implementation of hook_form_FORM_ID_alter(). */ -function webform_encrypt_form_webform_component_edit_form_alter(&$form, $form_state) { - // Add our fields to the component add/edit form. - $component = $form_state['build_info']['args'][1]; +function webform_encrypt_form_webform_ui_element_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) { + $webform = $form_state->getBuildInfo()['args']['0']; + $element_id = $form_state->getBuildInfo()['args']['1']; + $webform_elements = $webform->getElementsInitializedAndFlattened(); - // Exclude webform component types that don't make sense to encrypt. - $excluded_types = array('fieldset', 'file', 'markup', 'pagebreak'); - if (!in_array($form['type']['#value'], $excluded_types)) { + $allowed_types = array('textfield', 'textarea'); + if (in_array($webform_elements[$element_id]['#type'], $allowed_types)) { + // To provide the fieldset for encryption fields on element config form. + $form['element_encrypt'] = [ + '#type' => 'details', + '#title' => t('Encryption'), + ]; - // Add settings for encryption. - $form['encryption'] = array( - '#type' => 'fieldset', + // To provide the encryption fields for webform elements. + $form['element_encrypt']['encrypt'] = [ + '#type' => 'webform_element_encrypt', '#title' => t('Encryption'), - '#tree' => TRUE, - ); - $form['encryption']['encrypt'] = array( - '#type' => 'checkbox', - '#title' => t('Encrypt this field\'s value'), - '#description' => t('!link to edit encryption settings.', array('!link' => l('Click here', 'admin/config/system/encrypt'))), - '#default_value' => isset($component['extra']['encrypt']) ? $component['extra']['encrypt'] : 0, - ); + '#title_display' => 'hidden', + ]; } } @@ -66,96 +36,39 @@ function webform_encrypt_form_webform_component_edit_form_alter(&$form, $form_st * 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_client_form_') === 0 && $form['details']['sid']['#value']) { - _webform_encrypt_decrypt_nested_values($form['submitted']); - } -} - -/** - * Implementation of hook_webform_component_presave(). - * Save encryption settings for a component. - */ -function webform_encrypt_webform_component_presave(&$component) { - if (!empty($component['encryption'])) { - $component['extra'] = array_merge($component['extra'], $component['encryption']); - unset($component['encryption']); - } -} + // 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']; -/** - * Implementation of hook_webform_submission_presave(). - * Encrypt the value if the component has been marked as such. - */ -function webform_encrypt_webform_submission_presave($node, &$submission) { - foreach ($submission->data as $cid => $entry) { - if (!empty($node->webform['components'][$cid]['extra']['encrypt'])) { - foreach ($submission->data[$cid]['value'] as $delta => $value) { - $submission->data[$cid]['value'][$delta] = encrypt($entry['value'][$delta], array('base64' => TRUE)); + foreach ($elements as $key => &$element) { + if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { + $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$key]['encrypt_profile']); + $form['elements'][$key]['#default_value'] = Drupal::service('encryption')->decrypt($element['#default_value'], $encryption_profile); } } } } /** - * Implementation of hook_webform_submission_render_alter(). - * Decrypt values when displaying webform submissions. - */ -function webform_encrypt_webform_submission_render_alter(&$renderable) { - // First, determine if 1) if we are dealing with an email or a page view, and 2) if user matching - // is enabled. - if (!empty($renderable['#email']) && variable_get('webform_encrypt_match_user', 0)) { - // If we are, then try to match a user to the email address we are sending to. - $uid = db_query('SELECT uid FROM {users} WHERE mail = ?', array($renderable['#email']['email']))->fetchField(); - $account = $uid ? user_load($uid) : NULL; - } else { - $account = NULL; - } - - // Next, we loop through components and decrypt as necessary. - _webform_encrypt_decrypt_nested_values($renderable, '#value', array('account' => $account)); -} - -/** - * Preprocess for theme('webform_results_table'). + * Implements hook_entity_presave(). * - * Decrypt webform values in the table display. + * @param \Drupal\Core\Entity\EntityInterface $entity */ -function webform_encrypt_preprocess_webform_results_table(&$vars) { - foreach ($vars['submissions'] as $sid => &$submission) { - foreach ($submission->data as $cid => &$item) { - $component = $vars['components'][$cid]; - if (!empty($component['extra']['encrypt'])) { - foreach ($item['value'] as &$value) { - $value = user_access('view encrypted values') ? decrypt($value, array('base64' => TRUE)) : t('[Value Encrypted]'); - } - } - } - } -} +function webform_encrypt_entity_presave(Drupal\Core\Entity\EntityInterface $entity) { + if ($entity instanceof Drupal\webform\Entity\WebformSubmission) { + $config = \Drupal::service('config.factory')->get('webform.encrypt')->get('element.settings'); + $data_original = $entity->getData(); -/** - * Helper function to recursively decrypt values in a webform structure. - */ -function _webform_encrypt_decrypt_nested_values(&$element, $key = '#default_value', $restrict = array()) { - // Determine if we are checking access. - $access = empty($restrict) ? TRUE : user_access('view encrypted values', $restrict['account']); - - // Loop through each item and decrypt the value. - foreach (element_children($element) as $name) { - $component = &$element[$name]; - if (!empty($component['#webform_component']['extra']['encrypt'])) { - if (is_array($component[$key])) { - foreach ($component[$key] as &$value) { - $value = $access ? decrypt($value, array('base64' => TRUE)) : t('[Value Encrypted]'); - } + foreach ($data_original as $key => $value) { + if (isset($config[$key]['encrypt']) && $config[$key]['encrypt']) { + $encryption_profile = \Drupal\encrypt\Entity\EncryptionProfile::load($config[$key]['encrypt_profile']); + $data[$key] = Drupal::service('encryption')->encrypt($value, $encryption_profile); } else { - $component[$key] = $access ? decrypt($component[$key], array('base64' => TRUE)) : t('[Value Encrypted]'); + $data[$key] = $value; } } - - // Recurse if the current item has children. - _webform_encrypt_decrypt_nested_values($component, $key, $restrict); + $entity->setData($data); } } diff --git a/webform_encrypt.permissions.yml b/webform_encrypt.permissions.yml new file mode 100644 index 0000000000000000000000000000000000000000..c500e248578ae09179d140e4b354f55ec83a1187 --- /dev/null +++ b/webform_encrypt.permissions.yml @@ -0,0 +1,4 @@ +'view encrypted values': + title: 'View Encrypted Values in Webform Results' + description: 'Users that do not have this permission will see placeholder text.' + \ No newline at end of file