forked from ambta/DoctrineEncryptBundle
-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathDoctrineDecryptDatabaseCommand.php
More file actions
152 lines (124 loc) · 6.3 KB
/
DoctrineDecryptDatabaseCommand.php
File metadata and controls
152 lines (124 loc) · 6.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<?php
namespace Ambta\DoctrineEncryptBundle\Command;
use Ambta\DoctrineEncryptBundle\DependencyInjection\DoctrineEncryptExtension;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Decrypt whole database on tables which are encrypted
*
* @author Marcel van Nuil <marcel@ambta.com>
* @author Michael Feinbier <michael@feinbier.net>
*/
class DoctrineDecryptDatabaseCommand extends AbstractCommand
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('doctrine:decrypt:database')
->setDescription('Decrypt whole database on tables which are encrypted')
->addArgument('encryptor', InputArgument::OPTIONAL, 'The encryptor you want to decrypt the database with')
->addArgument('batchSize', InputArgument::OPTIONAL, 'The update/flush batch size', 20);
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// Get entity manager, question helper, subscriber service and annotation reader
$question = $this->getHelper('question');
// Get list of supported encryptors
$supportedExtensions = DoctrineEncryptExtension::SupportedEncryptorClasses;
$batchSize = $input->getArgument('batchSize');
// If encryptor has been set use that encryptor else use default
if ($input->getArgument('encryptor')) {
if (isset($supportedExtensions[$input->getArgument('encryptor')])) {
$reflection = new \ReflectionClass($supportedExtensions[$input->getArgument('encryptor')]);
$encryptor = $reflection->newInstance();
$this->subscriber->setEncryptor($encryptor);
} else {
if (class_exists($input->getArgument('encryptor'))) {
$this->subscriber->setEncryptor($input->getArgument('encryptor'));
} else {
$output->writeln('Given encryptor does not exists');
return $output->writeln('Supported encryptors: ' . implode(', ', array_keys($supportedExtensions)));
}
}
}
// Get entity manager metadata
$metaDataArray = $this->entityManager->getMetadataFactory()->getAllMetadata();
// Set counter and loop through entity manager meta data
$propertyCount = 0;
foreach ($metaDataArray as $metaData) {
if ($metaData instanceof ClassMetadataInfo and $metaData->isMappedSuperclass) {
continue;
}
$countProperties = count($this->getEncryptionableProperties($metaData));
$propertyCount += $countProperties;
}
$confirmationQuestion = new ConfirmationQuestion(
'<question>' . count($metaDataArray) . ' entities found which are containing ' . $propertyCount . ' properties with the encryption tag. ' . PHP_EOL . '' .
'Which are going to be decrypted with [' . get_class($this->subscriber->getEncryptor()) . ']. ' . PHP_EOL . '' .
'Wrong settings can mess up your data and it will be unrecoverable. ' . PHP_EOL . '' .
'I advise you to make <bg=yellow;options=bold>a backup</bg=yellow;options=bold>. ' . PHP_EOL . '' .
'Continue with this action? (y/yes)</question>', false
);
if (!$question->ask($input, $output, $confirmationQuestion)) {
return AbstractCommand::FAILURE;
}
// Start decrypting database
$output->writeln('' . PHP_EOL . 'Decrypting all fields. This can take up to several minutes depending on the database size.');
$valueCounter = 0;
// Loop through entity manager meta data
foreach ($this->getEncryptionableEntityMetaData() as $metaData) {
$i = 0;
$iterator = $this->getEntityIterator($metaData->name);
$totalCount = $this->getTableCount($metaData->name);
$output->writeln(sprintf('Processing <comment>%s</comment>', $metaData->name));
$progressBar = new ProgressBar($output, $totalCount);
foreach ($iterator as $row) {
$entity = $row[0];
// Create reflectionClass for each entity
$entityReflectionClass = new \ReflectionClass($entity);
//Get the current encryptor used
$encryptorUsed = $this->subscriber->getEncryptor();
//Loop through the property's in the entity
foreach ($this->getEncryptionableProperties($metaData) as $property) {
$methodeName = ucfirst($property->getName());
$getter = 'get' . $methodeName;
$setter = 'set' . $methodeName;
//Check if getter and setter are set
if ($entityReflectionClass->hasMethod($getter) && $entityReflectionClass->hasMethod($setter)) {
$unencrypted = $entity->$getter();
$entity->$setter($unencrypted);
$valueCounter++;
}
}
$this->subscriber->setEncryptor(null);
$this->entityManager->persist($entity);
if (($i % $batchSize) === 0) {
$this->entityManager->flush();
$this->entityManager->clear();
}
$progressBar->advance(1);
$i++;
$this->subscriber->setEncryptor($encryptorUsed);
}
$progressBar->finish();
$output->writeln('');
$encryptorUsed = $this->subscriber->getEncryptor();
$this->subscriber->setEncryptor(null);
$this->entityManager->flush();
$this->entityManager->clear();
$this->subscriber->setEncryptor($encryptorUsed);
}
$output->writeln('' . PHP_EOL . 'Decryption finished values found: <info>' . $valueCounter . '</info>, decrypted: <info>' . $this->subscriber->decryptCounter . '</info>.' . PHP_EOL . 'All values are now decrypted.');
return AbstractCommand::SUCCESS;
}
}