You are here

WebformEncryptSubmissionStorage.php in Webform Encrypt 8

File

src/WebformEncryptSubmissionStorage.php
View source
<?php

namespace Drupal\webform_encrypt;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\encrypt\EncryptionProfileInterface;
use Drupal\encrypt\EncryptServiceInterface;
use Drupal\webform\WebformAccessRulesManagerInterface;
use Drupal\webform\WebformInterface;
use Drupal\webform\WebformSubmissionInterface;
use Drupal\webform\WebformSubmissionStorage;
use Drupal\encrypt\Entity\EncryptionProfile;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Alter webform submission storage definitions.
 */
class WebformEncryptSubmissionStorage extends WebformSubmissionStorage {

  /**
   * The encryption Service.
   *
   * @var \Drupal\encrypt\EncryptServiceInterface
   */
  protected $encryptionService;

  /**
   * WebformEncryptSubmissionStorage constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection to be used.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache backend to be used.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   Proxied implementation of AccountInterface, to access current user data.
   * @param \Drupal\webform\WebformAccessRulesManagerInterface $access_rules_manager
   *   The webform access rules manager.
   * @param \Drupal\encrypt\EncryptServiceInterface $encryptService
   *   The encryption Service.
   */
  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityTypeManagerInterface $entity_type_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, AccountProxyInterface $current_user, WebformAccessRulesManagerInterface $access_rules_manager, EncryptServiceInterface $encryptService) {
    parent::__construct($entity_type, $database, $entity_type_manager, $cache, $language_manager, $current_user, $access_rules_manager);
    $this->encryptionService = $encryptService;
  }

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    return new static($entity_type, $container
      ->get('database'), $container
      ->get('entity.manager'), $container
      ->get('cache.entity'), $container
      ->get('language_manager'), $container
      ->get('current_user'), $container
      ->get('webform.access_rules_manager'), $container
      ->get('encryption'));
  }

  /**
   * Helper function to recursively encrypt fields.
   *
   * @param array $data
   *   The current form data array.
   * @param \Drupal\webform\WebformInterface $webform
   *   The webform we are encrypting.
   *
   * @return array
   *   Array of form data with the value encrypted for those elements setup
   *   for being processed by an encryption profile.
   *
   * @throws \Drupal\encrypt\Exception\EncryptException
   */
  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 {
          $encrypted_value = $this
            ->encrypt($value, $encryption_profile);

          // Save the encrypted data value.
          $data[$element_name] = $encrypted_value;
        }
      }
    }
    return $data;
  }

  /**
   * Helper function to recursively encrypt children of fields.
   *
   * @param array $data
   *   Element data by reference.
   * @param \Drupal\encrypt\EncryptionProfileInterface $encryption_profile
   *   The encryption profile to be used on this element.
   *
   * @throws \Drupal\encrypt\Exception\EncryptException
   */
  public function encryptChildren(array &$data, EncryptionProfileInterface $encryption_profile) {
    foreach ($data as $key => $value) {
      if (is_array($value)) {
        $this
          ->encryptChildren($data[$key], $encryption_profile);
      }
      else {
        $encrypted_value = $this
          ->encrypt($value, $encryption_profile);
        $data[$key] = $encrypted_value;
      }
    }
  }

  /**
   * Encrypts a string.
   *
   * @param string $string
   *   The string to be decrypted.
   * @param \Drupal\encrypt\EncryptionProfileInterface $encryption_profile
   *   The encryption profile to be used to encrypt the string.
   *
   * @return string
   *   The serialized encrypted value.
   *
   * @throws \Drupal\encrypt\Exception\EncryptException
   */
  protected function encrypt($string, EncryptionProfileInterface $encryption_profile) {

    // Serialize the data to include the encryption profile.
    // This is used later to check for changes in the profile.
    $encrypted_data = [
      'data' => $this->encryptionService
        ->encrypt($string, $encryption_profile),
      'encrypt_profile' => $encryption_profile
        ->id(),
    ];
    return serialize($encrypted_data);
  }

  /**
   * Decrypts a string.
   *
   * @param string $data
   *   The serialized submission data to be decrypted.
   * @param bool $check_permissions
   *   Flag that controls permissions check.
   *
   * @return string
   *   The decrypted value.
   *
   * @throws \Drupal\encrypt\Exception\EncryptException
   */
  protected function decrypt($data, $check_permissions = TRUE) {
    if ($check_permissions && !$this->currentUser
      ->hasPermission('view encrypted values')) {
      return '[Value Encrypted]';
    }
    $unserialized = unserialize($data);
    if (isset($unserialized['data']) && isset($unserialized['encrypt_profile'])) {
      $encryption_profile = EncryptionProfile::load($unserialized['encrypt_profile']);
      return $this->encryptionService
        ->decrypt($unserialized['data'], $encryption_profile);
    }
    return $data;
  }

  /**
   * Helper function to recursively decrypt fields.
   *
   * @param \Drupal\webform\WebformSubmissionInterface $webform_submission
   *   The webform submission to work on.
   * @param bool $check_permissions
   *   Flag that controls permissions check.
   *
   * @return array
   *   Array of form data with the value now decrypted for those elements setup
   *   for being processed by an encryption profile.
   *
   * @throws \Drupal\encrypt\Exception\EncryptException
   */
  public function decryptElements(WebformSubmissionInterface $webform_submission, $check_permissions = TRUE) {

    // Load webform.
    $webform = $webform_submission
      ->getWebform();

    // Load submission data.
    $data = $webform_submission
      ->getData();

    // Load the configuration.
    $config = $webform
      ->getThirdPartySetting('webform_encrypt', 'element');
    foreach ($data as $element_name => $value) {
      if (isset($config[$element_name]) && $config[$element_name]['encrypt']) {
        if (is_array($value)) {
          $this
            ->decryptChildren($data[$element_name], $check_permissions);
        }
        else {
          $decrypted_value = $this
            ->decrypt($value, $check_permissions);

          // Save the decrypted data value.
          $data[$element_name] = $decrypted_value;
        }
      }
    }
    return $data;
  }

  /**
   * Helper function to recursively decrypt children of fields.
   *
   * @param array $data
   *   Element data by reference.
   * @param bool $check_permissions
   *   Flag that controls permissions check.
   *
   * @throws \Drupal\encrypt\Exception\EncryptException
   */
  public function decryptChildren(array &$data, $check_permissions = TRUE) {
    foreach ($data as $key => $value) {
      if (is_array($value)) {
        $this
          ->decryptChildren($data[$key], $check_permissions);
      }
      else {
        $decrypted_value = $this
          ->decrypt($value, $check_permissions);
        $data[$key] = $decrypted_value;
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function doPreSave(EntityInterface $entity) {

    /** @var \Drupal\webform\WebformSubmissionInterface $entity */
    $id = parent::doPreSave($entity);
    $data_original = $entity
      ->getData();
    $webform = $entity
      ->getWebform();
    $encrypted_data = $this
      ->encryptElements($data_original, $webform);
    $entity
      ->setData($encrypted_data);
    return $id;
  }

  /**
   * {@inheritdoc}
   */
  protected function doPostSave(EntityInterface $entity, $update) {

    /** @var \Drupal\webform\WebformSubmissionInterface $entity */

    // Decrypt and set data post save so it remains readable for multistep
    // webforms and/or any other process that may take place after saving.
    $data = $this
      ->decryptElements($entity, FALSE);
    $entity
      ->setData($data);
    parent::doPostSave($entity, $update);
  }

  /**
   * {@inheritdoc}
   */
  protected function loadData(array &$webform_submissions) {
    parent::loadData($webform_submissions);
    foreach ($webform_submissions as &$webform_submission) {
      $data = $this
        ->decryptElements($webform_submission);
      $webform_submission
        ->setData($data);
      $webform_submission
        ->setOriginalData($data);
    }
  }

}

Classes

Namesort descending Description
WebformEncryptSubmissionStorage Alter webform submission storage definitions.