From 795f81dc8f514e20525fbf7477fa913e6e2f08a5 Mon Sep 17 00:00:00 2001 From: Nicolas MURE Date: Tue, 5 Apr 2016 23:40:40 +0200 Subject: [PATCH] multiple encryptors declaration --- DependencyInjection/Configuration.php | 18 ++- .../NmureEncryptorExtension.php | 43 +++++-- README.md | 33 +++-- Resources/config/services.xml | 22 ---- .../NmureEncryptorExtensionTest.php | 115 ++++++++++++------ phpunit.xml.dist | 1 - 6 files changed, 141 insertions(+), 91 deletions(-) delete mode 100644 Resources/config/services.xml diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 76b668a..a8070f9 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -22,12 +22,20 @@ public function getConfigTreeBuilder() $rootNode ->children() - ->scalarNode('secret') + ->arrayNode('encryptors') ->isRequired() - ->cannotBeEmpty() - ->end() - ->booleanNode('prefer_base64') - ->defaultTrue() + ->requiresAtLeastOneElement() + ->prototype('array') + ->children() + ->scalarNode('secret') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->booleanNode('prefer_base64') + ->defaultTrue() + ->end() + ->end() + ->end() ->end() ->end() ; diff --git a/DependencyInjection/NmureEncryptorExtension.php b/DependencyInjection/NmureEncryptorExtension.php index a0e31dc..6bcab80 100644 --- a/DependencyInjection/NmureEncryptorExtension.php +++ b/DependencyInjection/NmureEncryptorExtension.php @@ -5,7 +5,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\DependencyInjection\Reference; /** * This is the class that loads and manages your bundle configuration. @@ -14,6 +14,13 @@ */ class NmureEncryptorExtension extends Extension { + /** + * Indicates if the compilation of the container is required. + * + * @var boolean + */ + private $isCompilationRequired; + /** * {@inheritdoc} */ @@ -22,15 +29,35 @@ public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $container->setParameter('nmure_encryptor.secret', $config['secret']); + foreach ($config['encryptors'] as $name => $settings) { + $this->configureEncryptor($name, $settings, $container); + } + + // resolving decorated services if needed + if ($this->isCompilationRequired) { + $container->compile(); + } + } + + /** + * @param string $name Encryptor's name + * @param array $settings Encryptor's settings + * @param ContainerBuilder $container + */ + private function configureEncryptor($name, array $settings, ContainerBuilder $container) + { + $serviceName = sprintf('nmure_encryptor.%s', $name); + $container->register($serviceName, 'Nmure\EncryptorBundle\Encryptor\Encryptor') + ->addArgument($settings['secret']); - $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('services.xml'); + if ($settings['prefer_base64']) { + $decoratorServiceName = sprintf('nmure_encryptor.adapter.base64.%s', $name); + $container->register($decoratorServiceName, 'Nmure\EncryptorBundle\Adapter\Base64Adapter') + ->addArgument(new Reference(sprintf('%s.inner', $decoratorServiceName))) + ->setPublic(false) + ->setDecoratedService($serviceName); - if ($config['prefer_base64']) { - $container->setAlias('nmure_encryptor.encryptor', 'nmure_encryptor.adapter.base64'); - } else { - $container->setAlias('nmure_encryptor.encryptor', 'nmure_encryptor.encryptor.original'); + $this->isCompilationRequired = true; } } } diff --git a/README.md b/README.md index aaf5e91..6d4e141 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle: ```bash -$ composer require nmure/encryptor-bundle "~0.1.0" +$ composer require nmure/encryptor-bundle "~0.2.0" ``` This command requires you to have Composer installed globally, as explained @@ -45,17 +45,25 @@ Add the following configuration to your `config.yml` file : ```yaml # app/config/config.yml nmure_encryptor: - secret: theSecretKeyGoesHere # should be a complex key defined in your parameters.yml file - prefer_base64: true # optional, default true. Indicates if the encrypted data should be converted to base64 + encryptors: + my_encryptor: + secret: theSecretKeyGoesHere # should be a complex key defined in your parameters.yml file + # you can add as many encryptors as you want + my_other_encryptor: + secret: myOtherSecretKey # you should use one unique secret key by encryptor + prefer_base64: false # optional, default true. + # Indicates if the encrypted data should be converted to base64 ``` ## Usage -The encryptor is defined as a service under the `nmure_encryptor.encryptor` key. +You can access to the encryptors defined in the `config.yml` file by specifying your encryptor's name, e.g. : +accessing to `nmure_encryptor.my_encryptor` will return the encryptor defined under the `my_encryptor` key. -Simply access to this service and call the `encrypt` / `decrypt` functions : +All the encryptors are implementing the `Nmure\EncryptorBundle\Encryptor\EncryptorInterface`. +To use them, call the `encrypt` / `decrypt` functions : ```php // from a controller : -$encryptor = $this->get('nmure_encryptor.encryptor'); +$encryptor = $this->get('nmure_encryptor.my_encryptor'); $encrypted = $encryptor->encrypt('hello world'); // ... $decrypted = $encryptor->decrypt($encrypted); @@ -70,8 +78,8 @@ otherwise, your data won't be readable. You can access to the initialization vector on the encryptor using these functions: ```php -string Encryptor::getIv(); -void Encryptor::setIv(string $iv); +string EncryptorInterface::getIv(); +void EncryptorInterface::setIv(string $iv); ``` Be sure to store the initialization vector used to crypt data along side to the crypted data to be able to decrypt it later. @@ -80,15 +88,6 @@ If the `prefer_base64` config setting is set to `true`, the encrypted data will instead of staying a binary string. The itinialization vector will also be converted to a base64 string. -## Services -This Bundle exposes the following services which are returning a `Nmure\EncryptorBundle\Encryptor\EncryptorInterface`: -- `nmure_encryptor.encryptor`: alias for `nmure_encryptor.encryptor.original` or `nmure_encryptor.adapter.base64` -(depends on the value of the `prefer_base64` config parameter). -- `nmure_encryptor.encryptor.original`: the original encryptor which returns encrypted data as a binary string -(`Nmure\EncryptorBundle\Encryptor\Encryptor`). -- `nmure_encryptor.adapter.base64`: the base64 adapter which returns encrypted data as a MIME base64 string -(`Nmure\EncryptorBundle\Adapter\Base64Adapter`). - ## Informations Useful informations about: - [PHP's openssl](http://thefsb.tumblr.com/post/110749271235/using-opensslendecrypt-in-php-instead-of "Using openssl_en/decrypt() in PHP instead of Mcrypt") diff --git a/Resources/config/services.xml b/Resources/config/services.xml deleted file mode 100644 index 17fd2ad..0000000 --- a/Resources/config/services.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Nmure\EncryptorBundle\Encryptor\Encryptor - - Nmure\EncryptorBundle\Adapter\Base64Adapter - - - - - %nmure_encryptor.secret% - - - - - - - diff --git a/Tests/DependencyInjection/NmureEncryptorExtensionTest.php b/Tests/DependencyInjection/NmureEncryptorExtensionTest.php index 5b9f47b..155367e 100644 --- a/Tests/DependencyInjection/NmureEncryptorExtensionTest.php +++ b/Tests/DependencyInjection/NmureEncryptorExtensionTest.php @@ -9,13 +9,41 @@ class NmureEncryptorExtensionTest extends TestCase { + /** + * @expectedException Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testEncryptorsArrayMustBeDefined() + { + $loader = new NmureEncryptorExtension(); + $config = array(); + $loader->load(array($config), new ContainerBuilder()); + } + + /** + * @expectedException Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testEncryptorsArrayMustNotBeEmpty() + { + $loader = new NmureEncryptorExtension(); + $config = array( + 'encryptors' => array(), + ); + $loader->load(array($config), new ContainerBuilder()); + } + /** * @expectedException Symfony\Component\Config\Definition\Exception\InvalidConfigurationException */ public function testSecretMustNotBeEmpty() { $loader = new NmureEncryptorExtension(); - $config = $this->getEmptySecretConfig(); + $config = array( + 'encryptors' => array( + 'first_encryptor' => array( + 'secret' => null + ), + ), + ); $loader->load(array($config), new ContainerBuilder()); } @@ -25,8 +53,11 @@ public function testSecretMustNotBeEmpty() public function testSecretMustBeDefined() { $loader = new NmureEncryptorExtension(); - $config = $this->getEmptySecretConfig(); - unset($config['secret']); + $config = array( + 'encryptors' => array( + 'first_encryptor' => array(), + ), + ); $loader->load(array($config), new ContainerBuilder()); } @@ -34,56 +65,64 @@ public function testValidConfiguration() { $configuration = new ContainerBuilder(); $loader = new NmureEncryptorExtension(); - $config = $this->getValidConfig(); + $config = array( + 'encryptors' => array( + 'first_encryptor' => array( + 'secret' => 'iAmTheFirstSecretKey', + ), + ), + ); $loader->load(array($config), $configuration); - $this->assertEquals($configuration->getParameter('nmure_encryptor.secret'), 'iAmTheSecretKey'); - - $this->assertInstanceOf('Nmure\EncryptorBundle\Encryptor\EncryptorInterface', $configuration->get('nmure_encryptor.encryptor')); + $this->assertInstanceOf('Nmure\EncryptorBundle\Encryptor\EncryptorInterface', $configuration->get('nmure_encryptor.first_encryptor')); // default setting - $this->assertInstanceOf('Nmure\EncryptorBundle\Adapter\Base64Adapter', $configuration->get('nmure_encryptor.encryptor')); - // assert same instance (alias) - $this->assertTrue($configuration->get('nmure_encryptor.encryptor') === $configuration->get('nmure_encryptor.adapter.base64')); + $this->assertInstanceOf('Nmure\EncryptorBundle\Adapter\Base64Adapter', $configuration->get('nmure_encryptor.first_encryptor')); } public function testPreferOriginalEncryptor() { $configuration = new ContainerBuilder(); $loader = new NmureEncryptorExtension(); - $config = $this->getOriginalEncryptorConfig(); + $config = array( + 'encryptors' => array( + 'first_encryptor' => array( + 'secret' => 'iAmTheFirstSecretKey', + 'prefer_base64' => false, + ), + ), + ); $loader->load(array($config), $configuration); - $this->assertInstanceOf('Nmure\EncryptorBundle\Encryptor\EncryptorInterface', $configuration->get('nmure_encryptor.encryptor')); - $this->assertInstanceOf('Nmure\EncryptorBundle\Encryptor\Encryptor', $configuration->get('nmure_encryptor.encryptor')); - // assert same instance (alias) - $this->assertTrue($configuration->get('nmure_encryptor.encryptor') === $configuration->get('nmure_encryptor.encryptor.original')); + $this->assertInstanceOf('Nmure\EncryptorBundle\Encryptor\EncryptorInterface', $configuration->get('nmure_encryptor.first_encryptor')); + $this->assertInstanceOf('Nmure\EncryptorBundle\Encryptor\Encryptor', $configuration->get('nmure_encryptor.first_encryptor')); } - private function getEmptySecretConfig() + public function testMultipleEncryptorsDeclaration() { - $yaml = <<parse($yaml); - } + $configuration = new ContainerBuilder(); + $loader = new NmureEncryptorExtension(); + $config = array( + 'encryptors' => array( + 'first_encryptor' => array( + 'secret' => 'iAmTheFirstSecretKey', + ), + 'second_encryptor' => array( + 'secret' => 'iAmTheSecondSecretKey', + ), + ), + ); + $loader->load(array($config), $configuration); - private function getValidConfig() - { - $yaml = <<parse($yaml); - } + $first = $configuration->get('nmure_encryptor.first_encryptor'); + $second = $configuration->get('nmure_encryptor.second_encryptor'); - private function getOriginalEncryptorConfig() - { - $yaml = <<parse($yaml); + // assert two encryptor instances of the same class + $this->assertFalse($first === $second); + $this->assertTrue(get_class($first) === get_class($second)); + + // assert secret key is different + $data = 'Lorem ipsum dolor'; + $second->setIv($first->getIv()); + $this->assertNotEquals($first->encrypt($data), $second->encrypt($data)); } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 20f04c6..e5ab995 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -17,7 +17,6 @@ ./ ./build - ./Resources ./Tests ./vendor