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

multiple encryptors declaration #2

Merged
merged 1 commit into from
Apr 7, 2016
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
18 changes: 13 additions & 5 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
;
Expand Down
43 changes: 35 additions & 8 deletions DependencyInjection/NmureEncryptorExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -14,6 +14,13 @@
*/
class NmureEncryptorExtension extends Extension
{
/**
* Indicates if the compilation of the container is required.
*
* @var boolean
*/
private $isCompilationRequired;

/**
* {@inheritdoc}
*/
Expand All @@ -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;
}
}
}
33 changes: 16 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand All @@ -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")
Expand Down
22 changes: 0 additions & 22 deletions Resources/config/services.xml

This file was deleted.

115 changes: 77 additions & 38 deletions Tests/DependencyInjection/NmureEncryptorExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -25,65 +53,76 @@ 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());
}

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 = <<<EOF
secret:
EOF;
$parser = new Parser();
return $parser->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 = <<<EOF
secret: iAmTheSecretKey
EOF;
$parser = new Parser();
return $parser->parse($yaml);
}
$first = $configuration->get('nmure_encryptor.first_encryptor');
$second = $configuration->get('nmure_encryptor.second_encryptor');

private function getOriginalEncryptorConfig()
{
$yaml = <<<EOF
secret: iAmTheSecretKey
prefer_base64: false
EOF;
$parser = new Parser();
return $parser->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));
}
}
1 change: 0 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
<directory>./</directory>
<exclude>
<directory>./build</directory>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
Expand Down