Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 39 additions & 12 deletions Console/ReencryptColumn.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Magento\Framework\App\CacheInterface;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Serialize\Serializer\Json as JsonSerializer;
use Magento\Framework\Console\Cli;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
Expand All @@ -21,14 +22,17 @@ class ReencryptColumn extends Command
public const INPUT_KEY_COLUMN = 'column';

/**
* @param DeploymentConfig $deploymentConfig
* @param ResourceConnection $resourceConnection
* @param EncryptorInterface $encryptor
* @param CacheInterface $cache
* @param Magento\Framework\App\DeploymentConfig $deploymentConfig
* @param Magento\Framework\App\ResourceConnection $resourceConnection
* @param Magento\Framework\Serialize\Serializer\Json $jsonSerializer
* @param Magento\Framework\Encryption\EncryptorInterface $encryptor
* @param Magento\Framework\App\CacheInterface $cache
* @return void
*/
public function __construct(
private readonly DeploymentConfig $deploymentConfig,
private readonly ResourceConnection $resourceConnection,
private readonly JsonSerializer $jsonSerializer,
private readonly EncryptorInterface $encryptor,
private readonly CacheInterface $cache
) {
Expand Down Expand Up @@ -105,20 +109,30 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if (!strlen($column)) {
throw new \Exception('Provide an column');
}
$output->writeln("Looking for '$column' in '$table', identified by '$identifier'");

$jsonField = null;
if (strpos($column, '.') !== false) {
list($column, $jsonField) = explode('.', $column);
$output->writeln(
"Looking for JSON field '$jsonField.$column' in '$table', identified by '$identifier'"
);
} else {
$output->writeln("Looking for '$column' in '$table', identified by '$identifier'");
}
/**
* @see \Magento\Framework\Model\ResourceModel\Db\AbstractDb::_getLoadSelect()
*/
$tableName = $this->resourceConnection->getTableName($table);
$connection = $this->resourceConnection->getConnection();
$field = $connection->quoteIdentifier(sprintf('%s.%s', $tableName, $column));

$select = $connection->select()
->from($tableName, [$identifier, "$column"])
->where("($field LIKE '_:_:____%' OR $field LIKE '__:_:____%')")
->where("$field NOT LIKE ?", "$latestKeyNumber:_:__%");

->from($tableName, [$identifier, "$column"]);
if ($jsonField === null) {
$select = $select->where("($field LIKE '_:_:____%' OR $field LIKE '__:_:____%')")
->where("$field NOT LIKE ?", "$latestKeyNumber:_:__%");
} else {
$select = $select->where("($field LIKE '{%_:_:____%}' OR $field LIKE '{%__:_:____%}')")
->where("$field NOT LIKE ?", "{%$latestKeyNumber:_:__%}");
}
$result = $connection->fetchAll($select);
if (empty($result)) {
$output->writeln('No old entries found');
Expand All @@ -127,14 +141,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$connection->beginTransaction();
foreach ($result as $row) {
$output->writeln(str_pad('', 120, '#'));
$output->writeln("$identifier: {$row[$identifier]}");
$value = $row[$column];
$fieldData = [];
if ($jsonField !== null) {
$fieldData = $this->jsonSerializer->unserialize($value);
$value = $fieldData[$jsonField] ?? '';
}
if ($value === '') {
continue;
}
$output->writeln("$identifier: {$row[$identifier]}");
$output->writeln("ciphertext_old: " . $value);
$valueDecrypted = $this->encryptor->decrypt($value);
$output->writeln("plaintext: " . $valueDecrypted);
$valueEncrypted = $this->encryptor->encrypt($valueDecrypted);
$output->writeln("ciphertext_new: " . $valueEncrypted);

if ($jsonField !== null) {
$fieldData[$jsonField] = $valueEncrypted;
$valueEncrypted = $this->jsonSerializer->serialize($fieldData);
}

if ($input->getOption(self::INPUT_KEY_FORCE)) {
$connection->update(
$tableName,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Done

## bin/magento gene:encryption-key-manager:reencrypt-column

This allows you to target a specific column for re-encryption.
This allows you to target a specific column for re-encryption. If the column contains JSON, you can target it using dot notation: `column.field`.

This command runs in dry run mode by default, do that as a first pass to see the changes that will be made. When you are happy run with `--force`.

Expand Down