Skip to content

Commit

Permalink
Improved code documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
dev-marcel committed Mar 11, 2015
1 parent 55f2587 commit 7951d7c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 78 deletions.
2 changes: 1 addition & 1 deletion Configuration/Encrypted.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
* @Annotation
*/
class Encrypted {
// some parameters will be added
//Just an placeholder
}
4 changes: 1 addition & 3 deletions DependencyInjection/Compiler/RegisterServiceCompilerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;

/**
* Description of RegisterServiceCompilerPass
* The RegisterServiceCompilerPass class
*
* @author wpigott
*/
Expand All @@ -18,8 +18,6 @@ class RegisterServiceCompilerPass implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
//Nothing here
}


}

?>
22 changes: 2 additions & 20 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ class Configuration implements ConfigurationInterface {
* {@inheritDoc}
*/
public function getConfigTreeBuilder() {

//Create tree builder
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('ambta_doctrine_encrypt');
$supportedDrivers = array('orm');
$supportedEncryptors = array('aes256');

// Grammar of config tree
$rootNode
Expand All @@ -32,26 +32,8 @@ public function getConfigTreeBuilder() {
->thenInvalid('You must specifiy secret_key option')
->end()
->end()
->scalarNode('encryptor')
->validate()
->ifNotInArray($supportedEncryptors)
->thenInvalid('You must choose from one of provided encryptors or specify your own encryptor class through encryptor_class option')
->end()
->defaultValue($supportedEncryptors[0])
->end()
->scalarNode('encryptor_class')
->end()
->scalarNode('encryptor_service')
->end()
->scalarNode('db_driver')
->validate()
->ifNotInArray($supportedDrivers)
->thenInvalid('The driver %s is not supported. Please choose one of ' . json_encode($supportedDrivers))
->end()
->cannotBeOverwritten()
->defaultValue($supportedDrivers[0])
->cannotBeEmpty()
->end()
->end();

return $treeBuilder;
Expand Down
24 changes: 12 additions & 12 deletions DependencyInjection/DoctrineEncryptExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ class DoctrineEncryptExtension extends Extension {
*/
public function load(array $configs, ContainerBuilder $container) {

//Create configuration object
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

//Set orm-service in array of services
$services = array('orm' => 'orm-services');

//set supported encryptor classes
$supportedEncryptorClasses = array('aes256' => 'Ambta\DoctrineEncryptBundle\Encryptors\AES256Encryptor');

//If no secret key is set, check for framework secret, otherwise throw exception
if (empty($config['secret_key'])) {
if ($container->hasParameter('secret')) {
$config['secret_key'] = $container->getParameter('secret');
Expand All @@ -36,26 +41,21 @@ public function load(array $configs, ContainerBuilder $container) {
}
}

if (!empty($config['encryptor_class'])) {
$encryptorFullName = $config['encryptor_class'];
} else {
$encryptorFullName = $supportedEncryptorClasses[$config['encryptor']];
//If empty encryptor class, use AES256 encryptor
if (empty($config['encryptor_class'])) {
$config['encryptor_class'] = $supportedEncryptorClasses['aes256'];
}

$container->setParameter('ambta_doctrine_encrypt.encryptor_class_name', $encryptorFullName);
//Set parameters
$container->setParameter('ambta_doctrine_encrypt.encryptor_class_name', $config['encryptor_class']);
$container->setParameter('ambta_doctrine_encrypt.secret_key', $config['secret_key']);

if (!empty($config['encryptor_service'])) {
$container->setParameter('ambta_doctrine_encrypt.encryptor_service', $config['encryptor_service']);
}

//Load service file
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load(sprintf('%s.yml', $services[$config['db_driver']]));
$loader->load(sprintf('%s.yml', $services['orm']));
}


public function getAlias() {
return 'ambta_doctrine_encrypt';
}

}
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ More about us can be found on our website. [Ambta.com](https://ambta.com)

###What does it do exactly

It gives you the opportunity to add the @Encrypt annotation above each (string/text) field
It gives you the opportunity to add the @Encrypt annotation above each string property

```
/**
Expand Down Expand Up @@ -48,4 +48,18 @@ This bundle is under the MIT license. See the complete license in the bundle

###Versions

I'm using Semantic Versioning like described [here](http://semver.org)
I'm using Semantic Versioning like described [here](http://semver.org)

###Todos

The following items will be done in order

1. Review of complete code + fixes/improvements and inline documentation
2. Add support for the other doctrine relationships (manyToMany, ManyToOne)
3. Add "Encryption" (reformating based on key) of integers, data time object
4. Recreate documentation
5. Create example code
6. Create an function to encrypt unencrypted database and vice versa
7. Look for a posibility of automatic encryption of query parameters
8. Look for a positbility to override findOneBy for automatic encryption of parameters
9. Add "Encryption" (reformating based on key) on all other database types) [Doctrine documentation Types](http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html)
91 changes: 51 additions & 40 deletions Subscribers/DoctrineEncryptSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ class DoctrineEncryptSubscriber implements EventSubscriber {

/**
* Initialization of subscriber
*
* @param Reader $annReader
* @param string $encryptorClass The encryptor class. This can be empty if
* a service is being provided.
* @param string $encryptorClass The encryptor class. This can be empty if a service is being provided.
* @param string $secretKey The secret key.
* @param EncryptorInterface|NULL $service (Optional) An EncryptorInterface.
*
* This allows for the use of dependency injection for the encrypters.
*/
public function __construct(Reader $annReader, $encryptorClass, $secretKey, EncryptorInterface $service = NULL) {
Expand All @@ -64,8 +65,12 @@ public function __construct(Reader $annReader, $encryptorClass, $secretKey, Encr
}

/**
* Listen a postUpdate lifecycle event. Checking and encrypt entities
* which have @Encrypted annotation
* Listen a postUpdate lifecycle event.
* Decrypt entity's property's values when post updated.
*
* So for example after form submit the preUpdate encrypted the entity
* We have to decrypt them before showing them again.
*
* @param LifecycleEventArgs $args
*/
public function postUpdate(LifecycleEventArgs $args) {
Expand All @@ -76,9 +81,9 @@ public function postUpdate(LifecycleEventArgs $args) {
}

/**
* Listen a preUpdate lifecycle event. Checking and encrypt entities fields
* which have @Encrypted annotation. Using changesets to avoid preUpdate event
* restrictions
* Listen a preUpdate lifecycle event.
* Encrypt entity's property's values on preUpdate, so they will be stored encrypted
*
* @param PreUpdateEventArgs $args
*/
public function preUpdate(PreUpdateEventArgs $args) {
Expand All @@ -89,19 +94,22 @@ public function preUpdate(PreUpdateEventArgs $args) {
}

/**
* Listen a postLoad lifecycle event. Checking and decrypt entities
* which have @Encrypted annotations
* Listen a postLoad lifecycle event.
* Decrypt entity's property's values when loaded into the entity manger
*
* @param LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args) {

//Get entity and process fields
$entity = $args->getEntity();
$this->processFields($entity, false);

}

/**
* Realization of EventSubscriber interface method.
*
* @return Array Return all events which this subscriber is listening
*/
public function getSubscribedEvents() {
Expand All @@ -112,65 +120,70 @@ public function getSubscribedEvents() {
);
}

/**
* Capitalize string
* @param string $word
* @return string
*/
public static function capitalize($word) {
if (is_array($word)) {
$word = $word[0];
}

return str_replace(' ', '', ucwords(str_replace(array('-', '_'), ' ', $word)));
}

/**
* Process (encrypt/decrypt) entities fields
* @param Obj $entity Some doctrine entity
*
* @param Object $entity doctrine entity
* @param Boolean $isEncryptOperation If true - encrypt, false - decrypt entity
*
* @throws \RuntimeException
*
* @return boolean
*/
private function processFields($entity, $isEncryptOperation = true) {

//Check which operation to be used
$encryptorMethod = $isEncryptOperation ? 'encrypt' : 'decrypt';


//Get the real class, we don't want to use the proxy classes
$realClass = \Doctrine\Common\Util\ClassUtils::getClass($entity);

//Get ReflectionClass of our entity
$reflectionClass = new ReflectionClass($realClass);

$properties = $reflectionClass->getProperties();

$withAnnotation = false;

//Foreach property in the reflection class
foreach ($properties as $refProperty) {

/**
* If followed standards, method name is getPropertyName, the propertyName is lowerCamelCase
* So just uppercase first character of the property, later on get and set{$methodName} wil be used
*/
$methodName = ucfirst($refProperty->getName());

/**
* Lazy loading, check if the property has an manyToOne relationship.
* if it has look if the set/get exists and recursively call this function based on the entity inside. Only if not empty ofcourse
*/
if($this->annReader->getPropertyAnnotation($refProperty, 'Doctrine\ORM\Mapping\ManyToOne')) {
$getter = "get".ucfirst($refProperty->getName());
$this->processFields($entity->$getter(), $isEncryptOperation);
if ($reflectionClass->hasMethod($getter = 'get' . $methodName) && $reflectionClass->hasMethod($setter = 'set' . $methodName)) {
$entity = $entity->$getter();
if(!empty($entity)) {
$this->processFields($entity, $isEncryptOperation);
}
}
}

/**
* If property is an normal value and contains the Encrypt tag, lets encrypt/decrypt that property
*/
if ($this->annReader->getPropertyAnnotation($refProperty, self::ENCRYPTED_ANN_NAME)) {

$withAnnotation = true;

// we have annotation and if it decrypt operation, we must avoid double decryption
$propName = $refProperty->getName();

/**
* If it is public lets not use the getter/setter
*/
if ($refProperty->isPublic()) {
$propName = $refProperty->getName();
$entity->$propName = $this->encryptor->$encryptorMethod($refProperty->getValue());

} else {

$methodName = self::capitalize($propName);

//If private or protected check if there is an getter/setter for the property, based on the $methodName
if ($reflectionClass->hasMethod($getter = 'get' . $methodName) && $reflectionClass->hasMethod($setter = 'set' . $methodName)) {

//Get the information (value) of the property
$getInformation = $entity->$getter();

//Then decrypt, encrypt the information if not empty en the <ENC> tag is there or not
//The <ENC> will be added at the end of an encrypted string so it is marked as encrypted
if($encryptorMethod == "decrypt") {
if(!is_null($getInformation) and !empty($getInformation)) {
if(substr($entity->$getter(), -5) == "<ENC>") {
Expand All @@ -194,8 +207,6 @@ private function processFields($entity, $isEncryptOperation = true) {
}
}


return $withAnnotation;
}

/**
Expand Down

0 comments on commit 7951d7c

Please sign in to comment.