Skip to content
Snippets Groups Projects

#2990881: Bulk encryption service

13 files
+ 2050
3
Compare changes
  • Side-by-side
  • Inline
Files
13
+ 341
0
<?php
namespace Drupal\webform_encrypt\BulkEncryption;
use Drupal\Core\Database\Connection;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Url;
use Drupal\encrypt\EncryptServiceInterface;
use Drupal\encrypt\Entity\EncryptionProfile;
use Drupal\encrypt\Exception\EncryptException;
/**
* Service functions related to encrypting data in batches.
*/
class BulkEncryption {
use DependencySerializationTrait;
/**
* The encryption service.
*
* @var \Drupal\encrypt\EncryptServiceInterface
*/
protected $encryptService;
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* BulkEncryption constructor.
*
* @param \Drupal\encrypt\EncryptServiceInterface $encrypt_service
* Encryption methods.
* @param \Drupal\Core\Database\Connection $connection
* Database connection.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* Drupal message API.
*/
public function __construct(EncryptServiceInterface $encrypt_service, Connection $connection, MessengerInterface $messenger) {
$this->encryptService = $encrypt_service;
$this->connection = $connection;
$this->messenger = $messenger;
}
/**
* Encrypt all items with a specific profile.
*
* @param string $profile
* The profile to encrypt submissions under.
* @param string $field
* The field to encrypt.
* @param array $options
* Options related to progressive batching.
*/
public function encryptByProfile($profile, $field = NULL, array $options = []) {
// Query for the submission data.
$submission_data = $this->getSubmissionData([
'name' => $field,
]);
// Create and process the batch.
$this->batchProcess('encryptElement', $submission_data, [$profile], $options);
}
/**
* Assign a new encryption profile to submission data.
*
* @param string $from_profile
* The profile that's being encrypted from.
* @param string $to_profile
* The new profile to encrypt with.
* @param string $field
* The field to change profile of.
* @param array $options
* Options related to progressive batching.
*/
public function changeProfile($from_profile, $to_profile, $field, array $options = []) {
// Query for the submission data.
$submission_data = $this->getSubmissionData([
'profile' => $from_profile,
'name' => $field,
]);
// Create and process the batch.
$this->batchProcess('encryptElement', $submission_data, [$to_profile], $options);
}
/**
* Decrypt all items with a specific profile.
*
* @param string $profile
* The profile to decrypt submissions under.
* @param string $field
* The field to decrypt.
* @param array $options
* Options related to progressive batching.
*/
public function decryptByProfile($profile, $field = NULL, array $options = []) {
// Query for the submission data.
$submission_data = $this->getSubmissionData([
'profile' => $profile,
'name' => $field,
]);
// Create and process the batch.
$this->batchProcess('decryptElement', $submission_data, [], $options);
}
/**
* Decrypt a specific field.
*
* @param string $webform_id
* Webform the field belongs to.
* @param string $name
* The field name.
* @param array $options
* Options related to progressive batching.
*/
public function decryptByName($webform_id, $name, array $options = []) {
// Query for the submission data.
$submission_data = $this->getSubmissionData([
'webform_id' => $webform_id,
'name' => $name,
]);
// Create and process the batch.
$this->batchProcess('decryptElement', $submission_data, [], $options);
}
/**
* Decrypt all webform submissions.
*
* @param array $options
* Options related to progressive batching.
*/
public function decryptAll(array $options = []) {
// Query for the submission data.
$submission_data = $this->getSubmissionData();
// Create and process the batch.
$this->batchProcess('decryptElement', $submission_data, [], $options);
}
/**
* Run a batch process of passed in data.
*
* @param string $callback
* The function to call during the batch.
* @param array $submission_data
* The submission data to run a batch against.
* @param array $context
* Optional parameters to be used in the $callback.
* @param array $options
* Options related to progressive batching.
*/
public function batchProcess($callback, array $submission_data, array $context = [], array $options = []) {
$batch = [
'title' => t('Processing'),
'operations' => [],
];
foreach ($submission_data as $data) {
$batch['operations'][] = [
[$this, $callback],
array_merge([$data], $context),
];
}
$batch['finished'] = 'batchFinished';
batch_set($batch);
if (isset($options['progressive']) && $options['progressive'] === TRUE) {
$redirect = batch_process($options['batch_url'], NULL, $options['form_callback']);
$options['form_state']->setRedirectUrl(Url::fromUri($redirect->getTargetUrl()));
}
else {
$batch = &batch_get();
$batch['progressive'] = FALSE;
batch_process();
}
}
/**
* Handle the completion of the batch.
*
* @param bool $success
* Whether the batch succeeded with no errors.
* @param array $results
* An array of batch results.
*/
public function batchFinished($success, array $results) {
$batch = &batch_get();
// This is a progressive batch and there was an error.
if ($batch['progressive'] !== FALSE && $success === FALSE) {
// Print all the errors that occurred.
foreach ($results as $result) {
$this->messenger->addMessage($result, 'error');
}
}
}
/**
* Save the data of a newly encrypted/decrypted submission.
*
* @param object $submission_data
* The submission data to be saved.
*/
public function saveSubmissionData($submission_data) {
$this->connection->update('webform_submission_data')
->fields(['value' => $submission_data->value])
->condition('sid', $submission_data->sid)
->condition('name', $submission_data->name)
->condition('property', $submission_data->property)
->condition('delta', $submission_data->delta)
->execute();
}
/**
* Retrieve submissions data from the database.
*
* @param array $options
* Optional filters.
*
* @return array
* Array of submission data.
*/
public function getSubmissionData(array $options = []) {
// Setup the query.
$query = $this->connection->select('webform_submission_data', 'wsd')
->fields('wsd', []);
// Filter by webform_id.
if (isset($options['webform_id'])) {
$query->condition('webform_id', $options['webform_id']);
}
// Filter by name.
if (isset($options['name']) && !is_null($options['name'])) {
$query->condition('name', $options['name']);
}
// Query the data.
$submission_data = $query->execute()->fetchAll();
// Filter by profile.
if (isset($options['profile'])) {
$submission_data = array_filter($submission_data, function ($value) use ($options) {
$data = @unserialize($value->value, ['allowed_classes' => FALSE]);
return is_array($data) && $data['encrypt_profile'] == $options['profile'];
});
}
return $submission_data;
}
/**
* Swap encryption profile of specific element.
*
* @param object $submission_data
* The submission element.
* @param string $profile
* The profile to encrypt with.
* @param array $context
* Batch context variable.
*/
public function encryptElement($submission_data, $profile, array &$context) {
$value = $submission_data->value;
$unserialized_submission = @unserialize($value, ['allowed_classes' => FALSE]);
$submission_profile = $unserialized_submission ? $unserialized_submission["encrypt_profile"] : NULL;
// If already encrypted then try decrypt.
if ($submission_profile) {
try {
$value = $this->encryptService->decrypt($unserialized_submission["data"], EncryptionProfile::load($unserialized_submission["encrypt_profile"]));
}
catch (EncryptException $e) {
$message = $e->getMessage();
watchdog_exception('webform_encrypt_bulk_encryption', $e, $message);
$context['results'][] = $message;
$context['sandbox']['messages'][] = $message;
}
}
// Encrypt with new profile.
try {
$to_encryption_profile = EncryptionProfile::load($profile);
$submission_data->value = serialize([
'data' => $this->encryptService->encrypt($value, $to_encryption_profile),
'encrypt_profile' => $profile,
]);
$this->saveSubmissionData($submission_data);
}
catch (EncryptException $e) {
$message = $e->getMessage();
watchdog_exception('webform_encrypt_bulk_encryption', $e, $message);
$context['results'][] = $message;
$context['sandbox']['messages'][] = $message;
}
}
/**
* Decrypt an individual element.
*
* @param object $submission_data
* The submission to be decrypted.
* @param array $context
* Batch context variable.
*/
public function decryptElement($submission_data, array &$context) {
$unserialized_submission = @unserialize($submission_data->value, ['allowed_classes' => FALSE]);
$submission_profile = $unserialized_submission ? $unserialized_submission["encrypt_profile"] : NULL;
// Only try and decrypt if this field is actually encrypted.
if ($submission_profile) {
$encryption_profile = EncryptionProfile::load($submission_profile);
try {
$submission_data->value = $this->encryptService->decrypt($unserialized_submission["data"], $encryption_profile);
$this->saveSubmissionData($submission_data);
}
catch (EncryptException $e) {
$message = $e->getMessage();
watchdog_exception('webform_encrypt_bulk_encryption', $e, $message);
$context['results'][] = $message;
$context['sandbox']['messages'][] = $message;
}
}
}
}
Loading