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

Support Doctrine/ORM 3.x & PHP 8.2 #58

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/.idea
/.idea
/vendor
composer.lock
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ Maker will have created a packages yaml file. The key is resolved in there.
spec_shaper_encrypt:
encrypt_key: '%env(SPEC_SHAPER_ENCRYPT_KEY)%'
is_disabled: false # Turn this to true to disable the encryption.
connections: # Optional, define the connection name(s) for the subscriber to listen to.
connections: # Optional, define the connection name(s) for the listener
- 'default'
- 'tenant'
subscriber_class: App\Subscriber\MyCustomSubscriber # Optional to override the bundle Doctrine event subscriber.
listener_class: App\EventListener\MyCustomListener # Optional to override the bundle Doctrine event listener.
encryptor_class: App\Encryptors\MyCustomEncryptor # Optional to override the bundle OpenSslEncryptor.
annotation_classes: # Optional to override the default annotation/Attribute object.
- App\Annotation\MyAttribute
Expand All @@ -104,8 +104,8 @@ spec_shaper_encrypt:
You can disable encryption by setting the 'is_disabled' option to true. Decryption still continues if any values
contain the \<ENC> suffix.

You can extend the EncryptBundle default Subscriber and override its methods. Use the 'subscriber_class' option
to point the bundle at your custom subscriber.
You can extend the EncryptBundle default Listener and override its methods. Use the 'listener_class' option
to point the bundle at your custom listener.

If you want to define your own annotation/attribute, then this can be used to trigger encryption by adding the annotation
class name to the 'annotation_classes' option array.
Expand Down Expand Up @@ -198,13 +198,13 @@ with the DateType form type.

## Step 4: General Use

The bundle comes with an DoctrineEncryptSubscriber. This subscriber catches the doctrine events
The bundle comes with an DoctrineEncryptListener. This listener catches the doctrine events
onLoad, onFlush and postFlush.

The onLoad event subscriber will decrypt your entity parameter at loading. This means that your forms
The onLoad event listener will decrypt your entity parameter at loading. This means that your forms
and form fields will already be decrypted.

The onFlush and postFlush event subscribers will check if encryption is enabled, and encrypt the data
The onFlush and postFlush event listeners will check if encryption is enabled, and encrypt the data
before entry to the database.

So, in normal CRUD operation you do not need to do anything in the controller for encrypting or decrypting
Expand Down Expand Up @@ -270,7 +270,7 @@ Or you can dispatch the EncryptEvent.
## Step 5: Decrypt in templates

If you query a repository using a select with an array result
then the doctrine onLoad event subscriber will not decrypt any encrypted
then the doctrine onLoad event listener will not decrypt any encrypted
values.

In this case, use the twig filter to decrypt your value when rendering.
Expand Down
11 changes: 5 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@
"MIT"
],
"require" : {
"php": ">=7.4|>=8",
"symfony/framework-bundle": "^5.4|^6.0|^7.0",
"symfony/console": "^5.4|^6.0|^7.0",
"doctrine/annotations": "^1.8|^2.0",
"doctrine/orm": "^2.11",
"doctrine/doctrine-bundle": "^2.5",
"php": ">=8.2",
"symfony/framework-bundle": "^7.0",
"symfony/console": "^7.0",
"doctrine/orm": "^3.2",
"doctrine/doctrine-bundle": "^2.12",
"symfony/monolog-bundle": "^3.7",
"ext-mbstring": "*",
"ext-openssl": "*",
Expand Down
16 changes: 8 additions & 8 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@ services:
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
# automatically registers your services as commands, event listeners, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false

# Subscriber Interface, default is DoctrineEncryptSubscriber but it can be overriden in the app config
# SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriberInterface:
# class: '%spec_shaper_encrypt.subscriber_class%'
# Listener Interface, default is DoctrineEncryptListener but it can be overriden in the app config
# SpecShaper\EncryptBundle\EventListener\DoctrineEncryptListenerInterface:
# class: '%spec_shaper_encrypt.listener_class%'
# arguments:
# $annotationArray: '%spec_shaper_encrypt.annotation_classes%'
# $isDisabled: '%spec_shaper_encrypt.is_disabled%'
# tags:
# - { name: doctrine.event_subscriber, connection: default }
# - { name: doctrine.event_listener, connection: default }

# Subscriber to catch any encrypt or decrypt events thrown
# SpecShaper\EncryptBundle\Subscribers\EncryptEventSubscriber:
# Listeners to catch any encrypt or decrypt events thrown
# SpecShaper\EncryptBundle\EventListener\EncryptEventListener:
# arguments:
# $isDisabled: '%spec_shaper_encrypt.is_disabled%'
# tags:
# - { name: kernel.event_subscriber, connection: default }
# - { name: kernel.event_listener, connection: default }

# Factory to create the encryptor/decryptor
SpecShaper\EncryptBundle\Encryptors\EncryptorFactory:
Expand Down
18 changes: 1 addition & 17 deletions src/Command/EncryptDatabaseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use Symfony\Component\Console\Command\Command;
use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
Expand All @@ -30,7 +29,6 @@ class EncryptDatabaseCommand extends Command
private array $encryptedFields = [];

public function __construct(
private readonly Reader $annotationReader,
private readonly EncryptorInterface $encryptor,
private readonly ManagerRegistry $registry,
private readonly array $annotationArray
Expand Down Expand Up @@ -154,7 +152,7 @@ private function getEncryptedFields(): array
return $this->encryptedFields;
}

private function isEncryptedProperty(\ReflectionProperty $refProperty)
private function isEncryptedProperty(\ReflectionProperty $refProperty): bool
{

foreach ($refProperty->getAttributes() as $refAttribute) {
Expand All @@ -164,20 +162,6 @@ private function isEncryptedProperty(\ReflectionProperty $refProperty)
}
}

foreach ($this->annotationReader->getPropertyAnnotations($refProperty) as $key => $annotation) {

if (in_array(get_class($annotation), $this->annotationArray)) {
$refProperty->setAccessible(true);

$this->logger->debug(sprintf('Use of @Encrypted property from SpecShaper/EncryptBundle in property %s is deprectated.
Please use #[Encrypted] attribute instead.',
$refProperty
));

return true;
}
}

return false;
}
}
4 changes: 2 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use SpecShaper\EncryptBundle\Annotations\Encrypted;
use SpecShaper\EncryptBundle\Encryptors\AesCbcEncryptor;
use SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriber;
use SpecShaper\EncryptBundle\EventListener\DoctrineEncryptListener;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

Expand All @@ -26,7 +26,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('encrypt_key')->end()
->scalarNode('default_associated_data')->defaultValue(null)->end()
->scalarNode('method')->defaultValue('OpenSSL')->end()
->scalarNode('subscriber_class')->defaultValue(DoctrineEncryptSubscriber::class)->end()
->scalarNode('listener_class')->defaultValue(DoctrineEncryptListener::class)->end()
->scalarNode('encryptor_class')->defaultValue(AesCbcEncryptor::class)->end()
->scalarNode('is_disabled')->defaultValue(false)->end()
->arrayNode('connections')
Expand Down
59 changes: 37 additions & 22 deletions src/DependencyInjection/SpecShaperEncryptExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

namespace SpecShaper\EncryptBundle\DependencyInjection;

use SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriber;
use SpecShaper\EncryptBundle\Subscribers\EncryptEventSubscriber;
use SpecShaper\EncryptBundle\Event\EncryptEvents;
use SpecShaper\EncryptBundle\EventListener\DoctrineEncryptListener;
use SpecShaper\EncryptBundle\EventListener\EncryptEventListener;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
Expand All @@ -17,7 +18,7 @@
*/
class SpecShaperEncryptExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
Expand All @@ -31,49 +32,63 @@ public function load(array $configs, ContainerBuilder $container)
} else {
$encryptKey = $config['encrypt_key'];
}
//
// dump($encryptKey);

if (array_key_exists('subscriber_class', $config)) {
trigger_deprecation('SpecShaperEncryptBundle', 'v4.0.0', 'DoctrineSubscribers will be deprecated in version 4. If you
have a custom subscriber in the encrypt bundle then this will need to be changed to a DoctrineListener.');
}

$container->setParameter($this->getAlias().'.encrypt_key', $encryptKey);
$container->setParameter($this->getAlias().'.default_associated_data', $config['default_associated_data']);
$container->setParameter($this->getAlias().'.method', $config['method']);
$container->setParameter($this->getAlias().'.subscriber_class', $config['subscriber_class']);
$container->setParameter($this->getAlias().'.listener_class', $config['listener_class']);
$container->setParameter($this->getAlias().'.encryptor_class', $config['encryptor_class']);
$container->setParameter($this->getAlias().'.annotation_classes', $config['annotation_classes']);
$container->setParameter($this->getAlias().'.is_disabled', $config['is_disabled']);

$doctrineSubscriber = new Definition($config['subscriber_class']);
$doctrineSubscriber
$doctrineListener = new Definition($config['listener_class']);
$doctrineListener
->setAutowired(true)
->setArgument(3, $config['annotation_classes'])
->setArgument(4, $config['is_disabled'])
->setArgument('$annotationArray', $config['annotation_classes'])
->setArgument('$isDisabled', $config['is_disabled'])
;

$encryptEventSubscriber = new Definition(EncryptEventSubscriber::class);
$encryptEventSubscriber
$encryptEventListener = new Definition(EncryptEventListener::class);
$encryptEventListener
->setAutowired(true)
->setArgument(1, $config['is_disabled'])
->setArgument('$isDisabled', $config['is_disabled'])
;

foreach ($config['connections'] as $connectionName) {
$doctrineSubscriber->addTag('doctrine.event_subscriber', [
$doctrineListener->addTag('doctrine.event_listener', [
'event' => 'postLoad',
'priority' => 500,
'connection' => $connectionName,
]);

$encryptEventSubscriber->addTag('kernal.event_subscriber', [
$doctrineListener->addTag('doctrine.event_listener', [
'event' => 'postUpdate',
'priority' => 500,
'connection' => $connectionName,
]);

$doctrineListener->addTag('doctrine.event_listener', [
'event' => 'onFlush',
'priority' => 500,
'connection' => $connectionName,
]);

$encryptEventListener->addTag('kernel.event_listener', [
'event' => EncryptEvents::ENCRYPT,
'method' => 'encrypt',
'connection' => $connectionName,
]);

$encryptEventListener->addTag('kernel.event_listener', [
'event' => EncryptEvents::DECRYPT,
'method' => 'decrypt',
'connection' => $connectionName,
]);
}

$container->addDefinitions([
DoctrineEncryptSubscriber::class => $doctrineSubscriber,
EncryptEventSubscriber::class => $encryptEventSubscriber
DoctrineEncryptListener::class => $doctrineListener,
EncryptEventListener::class => $encryptEventListener
]);

// Check if Twig is available
Expand Down
8 changes: 4 additions & 4 deletions src/Encryptors/AesCbcEncryptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use SpecShaper\EncryptBundle\Event\EncryptKeyEvent;
use SpecShaper\EncryptBundle\Event\EncryptKeyEvents;
use SpecShaper\EncryptBundle\Exception\EncryptException;
use SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriberInterface;
use SpecShaper\EncryptBundle\EventListener\DoctrineEncryptListenerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
Expand Down Expand Up @@ -53,7 +53,7 @@ public function encrypt(?string $data, ?string $columnName = null): ?string
}

// If the value already has the suffix <ENC> then ignore.
if (DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX === substr($data, -5)) {
if (DoctrineEncryptListenerInterface::ENCRYPTED_SUFFIX === substr($data, -5)) {
return $data;
}

Expand All @@ -73,7 +73,7 @@ public function encrypt(?string $data, ?string $columnName = null): ?string
);

// Prefix the encoded text with the iv and encode it to base 64. Append the encoded suffix.
return base64_encode($iv.$ciphertext).DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX;
return base64_encode($iv.$ciphertext).DoctrineEncryptListenerInterface::ENCRYPTED_SUFFIX;
}

/**
Expand All @@ -87,7 +87,7 @@ public function decrypt(?string $data, ?string $columnName = null): ?string
}

// If the value does not have the suffix <ENC> then ignore.
if (DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX !== substr($data, -5)) {
if (DoctrineEncryptListenerInterface::ENCRYPTED_SUFFIX !== substr($data, -5)) {
return $data;
}

Expand Down
8 changes: 4 additions & 4 deletions src/Encryptors/AesGcmEncryptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use SpecShaper\EncryptBundle\Event\EncryptKeyEvent;
use SpecShaper\EncryptBundle\Event\EncryptKeyEvents;
use SpecShaper\EncryptBundle\Exception\EncryptException;
use SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriberInterface;
use SpecShaper\EncryptBundle\EventListener\DoctrineEncryptListenerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Service\Attribute\Required;

Expand Down Expand Up @@ -54,7 +54,7 @@ public function encrypt(?string $data, ?string $columnName): ?string
return null;
}

if (DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX === substr($data, -5)) {
if (DoctrineEncryptListenerInterface::ENCRYPTED_SUFFIX === substr($data, -5)) {
return $data;
}

Expand All @@ -78,7 +78,7 @@ public function encrypt(?string $data, ?string $columnName): ?string
throw new EncryptException('Encryption failed.');
}

return base64_encode($iv.$tag.$ciphertext).DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX;
return base64_encode($iv.$tag.$ciphertext).DoctrineEncryptListenerInterface::ENCRYPTED_SUFFIX;
}

/**
Expand All @@ -90,7 +90,7 @@ public function decrypt(?string $data, ?string $columnName): ?string
return null;
}

if (DoctrineEncryptSubscriberInterface::ENCRYPTED_SUFFIX !== substr($data, -5)) {
if (DoctrineEncryptListenerInterface::ENCRYPTED_SUFFIX !== substr($data, -5)) {
return $data;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Encryptors/EncryptorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function setSecretKey(string $key): void;
*
* @return string|null Encrypted string
*/
public function encrypt(?string $data, ?string $columnName): ?string;
public function encrypt(?string $data, ?string $columnName = null): ?string;

/**
* Must accept data and return decrypted data.
Expand All @@ -25,5 +25,5 @@ public function encrypt(?string $data, ?string $columnName): ?string;
*
* @return string Unencrypted string
*/
public function decrypt(?string $data, ?string $columnName): ?string;
public function decrypt(?string $data, ?string $columnName = null): ?string;
}
Loading