Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide migration path from mcrypt #210

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
137 changes: 137 additions & 0 deletions Command/ReencryptDataCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

namespace JMS\Payment\CoreBundle\Command;

use Doctrine\ORM\Tools\Pagination\Paginator;
use JMS\Payment\CoreBundle\Cryptography\DefusePhpEncryptionService;
use JMS\Payment\CoreBundle\Cryptography\MCryptEncryptionService;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ReencryptDataCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('jms_payment_core:reencrypt-data')
->setDescription('Re-encrypt encrypted database data')
->addArgument(
'src',
InputArgument::REQUIRED,
'The cryptography provider with which data currently in the database was encrypted'
)
->addArgument(
'src-secret',
InputArgument::REQUIRED,
'The current encryption key'
)
->addArgument(
'dest',
InputArgument::REQUIRED,
'The new cryptography provider to use for encrypting data'
)
->addArgument(
'dest-secret',
InputArgument::REQUIRED,
'The new encryption key'
)
->addOption(
'src-mcrypt-cipher',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt cipher for the src provider',
'rijndael-256'
)
->addOption(
'src-mcrypt-mode',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt mode for the src provider',
'ctr'
)
->addOption(
'dest-mcrypt-cipher',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt cipher for the dest provider',
'rijndael-256'
)
->addOption(
'dest-mcrypt-mode',
null,
InputOption::VALUE_OPTIONAL,
'The mcrypt mode for the dest provider',
'ctr'
)
->addOption(
'em',
null,
InputOption::VALUE_OPTIONAL,
'The entity manager to use',
'default'
);
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$providers = $this->getProviders($input);

$em = $this->getContainer()->get('doctrine')->getManager($input->getOption('em'));

$query = $em->createQuery('SELECT pi from JMSPaymentCoreBundle:PaymentInstruction pi')
->setFirstResult(0)
->setMaxResults(128);

$paginator = new Paginator($query, $fetchJoinCollection = false);

foreach ($paginator as $pi) {
var_dump($pi->getExtendedData());
}
}

private function getProviders(InputInterface $input)
{
$supportedProviders = array(
'mcrypt' => MCryptEncryptionService::class,
'defuse_php_encryption' => DefusePhpEncryptionService::class,
);

foreach ([$input->getArgument('src'), $input->getArgument('dest')] as $provider) {
if (!array_key_exists($provider, $supportedProviders)) {
throw new \InvalidArgumentException("Unsupported cryptography provider: $provider");
}
}

$providers = array();

foreach (array('src', 'dest') as $providerType) {
foreach (($options = $input->getOptions()) as $key => $value) {
$options[str_replace("$providerType-", '', $key)] = $value;
}

foreach ($supportedProviders as $name => $class) {
if ($name !== $input->getArgument($providerType)) {
continue;
}

switch ($input->getArgument($providerType)) {
case 'mcrypt':
$providers[$providerType] = new MCryptEncryptionService(
$input->getArgument("$providerType-secret"),
$options['mcrypt-cipher'],
$options['mcrypt-mode']
);
break;
case 'defuse_php_encryption':
$providers[$providerType] = new DefusePhpEncryptionService($secret);
break;
}
}
}

return $providers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use JMS\Payment\CoreBundle\Cryptography\MCryptEncryptionService;

class MCryptEncryptionServiceTest extends \PHPUnit_Framework_TestCase
class LegacyMCryptEncryptionServiceTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function testNoSecret()
);
}

public function testSecret()
public function testLegacySecret()
{
$this->assertConfigurationIsValid(array('secret' => 'foo'));

Expand Down
128 changes: 128 additions & 0 deletions Tests/Functional/Command/ReencryptDataCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace JMS\Payment\CoreBundle\Tests\Command;

use JMS\Payment\CoreBundle\Command\ReencryptDataCommand;
use JMS\Payment\CoreBundle\Tests\Functional\BaseTestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

class ReencryptDataCommandTest extends BaseTestCase
{
public function setUp()
{
self::bootKernel();

$application = new Application(self::$kernel);
$application->add(new ReencryptDataCommand());

$this->command = $application->find('jms_payment_core:reencrypt-data');

parent::setUp();
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage foo
*/
public function testUnsupportedSourceProvider()
{
$this->execute(array(
'src' => 'foo',
'src-secret' => 'foo-secret',
'dest' => 'defuse_php_encryption',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage bar
*/
public function testUnsupportedDestProvider()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'dest' => 'bar',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The cipher "foo" is not supported.
*/
public function testMcryptSrcCipher()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'--src-mcrypt-cipher' => 'foo',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The cipher "bar" is not supported.
*/
public function testMcryptDestCipher()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
'--dest-mcrypt-cipher' => 'bar',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The mode "foo" is not supported.
*/
public function testMcryptSrcMode()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'--src-mcrypt-mode' => 'foo',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
));
}

/**
* @runInSeparateProcess
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The mode "bar" is not supported.
*/
public function testMcryptDestMode()
{
$this->execute(array(
'src' => 'mcrypt',
'src-secret' => 'foo-secret',
'dest' => 'mcrypt',
'dest-secret' => 'bar-secret',
'--dest-mcrypt-mode' => 'bar',
));
}

private function execute(array $input)
{
$commandTester = new CommandTester($this->command);

$commandTester->execute(array_merge(array(
'command' => $this->command->getName(),
), $input));

return $commandTester->getDisplay();
}
}