Skip to content

Commit 0a9aaec

Browse files
committed
Change the way data are stored to add the algorithm to be linked to the entity
1 parent 3507d71 commit 0a9aaec

16 files changed

+347
-256
lines changed

composer.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
"symfony/framework-bundle": "^4.4|^5.1",
2626
"doctrine/doctrine-bundle": "*",
2727
"doctrine/orm": "*",
28-
"symfony/security":"^4.4|^5.1"
28+
"symfony/security":"^4.4|^5.1",
29+
"symfony/monolog-bundle": "^3.6",
30+
"symfony/string": "^5.2"
2931
},
3032
"require-dev": {
3133
"symfony/stopwatch": "^4.4|^5.1",
32-
"phpunit/phpunit": "^9.4"
34+
"phpunit/phpunit": "^9.4",
35+
"phpseclib/mcrypt_compat": "^1.0"
3336
},
3437
"suggest": {
3538
"ext-sodium": "The Sodium library is native in PHP 7.2",
@@ -40,7 +43,10 @@
4043
"autoload": {
4144
"psr-4": {
4245
"Sidus\\EncryptionBundle\\": "src/"
43-
}
46+
},
47+
"exclude-from-classmap": [
48+
"/Tests/"
49+
]
4450
},
4551
"autoload-dev": {
4652
"psr-4": {

src/Doctrine/Connection/Factory/ConnectionFactory.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
use Doctrine\DBAL\Platforms\AbstractPlatform;
1212
use Doctrine\DBAL\Types\Type;
1313
use Sidus\EncryptionBundle\Doctrine\Type\EncryptTypeInterface;
14-
use Sidus\EncryptionBundle\Encryption\Enabler\EncryptionEnablerInterface;
15-
use Sidus\EncryptionBundle\Registry\EncryptionManagerRegistry;
14+
use Sidus\EncryptionBundle\Doctrine\ValueEncrypterInterface;
1615

1716
/**
1817
* The default Doctrine's connection factory should be override to allow injection the encryption manager into the type.
@@ -21,18 +20,14 @@ class ConnectionFactory
2120
{
2221
private array $typesConfig;
2322
private bool $initialized = false;
24-
25-
private EncryptionManagerRegistry $encryptionManager;
26-
private EncryptionEnablerInterface $encryptionEnabler;
23+
private ValueEncrypterInterface $valueEncrypter;
2724

2825
public function __construct(
2926
array $typesConfig,
30-
EncryptionManagerRegistry $encryptionManager,
31-
EncryptionEnablerInterface $encryptionEnabler
27+
ValueEncrypterInterface $valueEncrypter
3228
) {
3329
$this->typesConfig = $typesConfig;
34-
$this->encryptionManager = $encryptionManager;
35-
$this->encryptionEnabler = $encryptionEnabler;
30+
$this->valueEncrypter = $valueEncrypter;
3631
}
3732

3833
/**
@@ -136,8 +131,7 @@ private function initializeTypes() : void
136131
$type = Type::getType($typeName);
137132

138133
if ($type instanceof EncryptTypeInterface) {
139-
$type->setEncryptionManager($this->encryptionManager->getDefaultEncryptionManager());
140-
$type->setEncryptionEnabler($this->encryptionEnabler);
134+
$type->setValueEncrypter($this->valueEncrypter);
141135
}
142136
}
143137
$this->initialized = true;

src/Doctrine/Type/Behavior/EncryptType.php

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,30 @@
33
namespace Sidus\EncryptionBundle\Doctrine\Type\Behavior;
44

55
use Doctrine\DBAL\Platforms\AbstractPlatform;
6-
use Sidus\EncryptionBundle\Encryption\Enabler\EncryptionEnablerInterface;
7-
use Sidus\EncryptionBundle\Manager\EncryptionManagerInterface;
6+
use Sidus\EncryptionBundle\Doctrine\ValueEncrypterInterface;
87

98
trait EncryptType
109
{
11-
private EncryptionManagerInterface $encryptionManager;
12-
private EncryptionEnablerInterface $encryptionEnabler;
10+
private ValueEncrypterInterface $valueEncrypter;
1311

14-
public function convertToPHPValue($value, AbstractPlatform $platform)
12+
public function convertToPHPValue($value, AbstractPlatform $platform): string
1513
{
16-
// Allow to do not decrypt the value for the current request
17-
if (!$this->encryptionEnabler->isEncryptionEnabled()) {
18-
return $value;
19-
}
14+
// Decode value previously encoded in base64 for database storage
15+
$value = base64_decode($value);
2016

21-
return $this->encryptionManager->decryptString(base64_decode($value));
17+
return $this->valueEncrypter->decrypt($value);
2218
}
2319

24-
public function convertToDatabaseValue($value, AbstractPlatform $platform)
20+
public function convertToDatabaseValue($value, AbstractPlatform $platform): string
2521
{
26-
// Allow to do not decrypt the value for the current request
27-
if (!$this->encryptionEnabler->isEncryptionEnabled()) {
28-
return $value;
29-
}
30-
$value = $this->encryptionManager->encryptString($value);
31-
32-
return base64_encode($value);
33-
}
22+
$value = $this->valueEncrypter->encrypt($value);
3423

35-
public function setEncryptionManager(EncryptionManagerInterface $encryptionManager): void
36-
{
37-
$this->encryptionManager = $encryptionManager;
24+
// Encoding to base64 to avoid issue when storing binary strings
25+
return base64_encode($value);
3826
}
3927

40-
public function setEncryptionEnabler(EncryptionEnablerInterface $encryptionEnabler): void
28+
public function setValueEncrypter(ValueEncrypterInterface $valueEncrypter): void
4129
{
42-
$this->encryptionEnabler = $encryptionEnabler;
30+
$this->valueEncrypter = $valueEncrypter;
4331
}
4432
}

src/Doctrine/Type/EncryptStringType.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace Sidus\EncryptionBundle\Doctrine\Type;
44

5-
use Doctrine\DBAL\Types\StringType;
5+
use Doctrine\DBAL\Types\TextType;
66
use Sidus\EncryptionBundle\Doctrine\Type\Behavior\EncryptType;
77

8-
class EncryptStringType extends StringType implements EncryptTypeInterface
8+
class EncryptStringType extends TextType implements EncryptTypeInterface
99
{
1010
use EncryptType;
1111

src/Doctrine/Type/EncryptTypeInterface.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
namespace Sidus\EncryptionBundle\Doctrine\Type;
44

5-
use Sidus\EncryptionBundle\Encryption\Enabler\EncryptionEnablerInterface;
6-
use Sidus\EncryptionBundle\Manager\EncryptionManagerInterface;
5+
use Sidus\EncryptionBundle\Doctrine\ValueEncrypterInterface;
76

87
interface EncryptTypeInterface
98
{
10-
public function setEncryptionManager(EncryptionManagerInterface $encryptionManager): void;
11-
12-
public function setEncryptionEnabler(EncryptionEnablerInterface $encryptionEnabler): void;
9+
public function setValueEncrypter(ValueEncrypterInterface $valueEncrypter): void;
1310
}

src/Doctrine/ValueEncrypter.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace Sidus\EncryptionBundle\Doctrine;
4+
5+
use Exception;
6+
use Psr\Log\LoggerInterface;
7+
use Sidus\EncryptionBundle\Encryption\Enabler\EncryptionEnablerInterface;
8+
use Sidus\EncryptionBundle\Manager\EncryptionManagerInterface;
9+
use Sidus\EncryptionBundle\Registry\EncryptionManagerRegistryInterface;
10+
use Symfony\Component\String\ByteString;
11+
12+
class ValueEncrypter implements ValueEncrypterInterface
13+
{
14+
private EncryptionManagerRegistryInterface $registry;
15+
private LoggerInterface $logger;
16+
private EncryptionEnablerInterface $encryptionEnabler;
17+
18+
public function __construct(
19+
EncryptionManagerRegistryInterface $registry,
20+
LoggerInterface $logger,
21+
EncryptionEnablerInterface $encryptionEnabler
22+
) {
23+
$this->registry = $registry;
24+
$this->logger = $logger;
25+
$this->encryptionEnabler = $encryptionEnabler;
26+
}
27+
28+
public function encrypt(string $value): string
29+
{
30+
// Allow to do not encrypt the value for the current request
31+
if (!$this->encryptionEnabler->isEncryptionEnabled()) {
32+
return $value;
33+
}
34+
$manager = $this->registry->getDefaultEncryptionManager();
35+
$value = $manager->getEncryptionAdapter()::getCode().'.'.$manager->encryptString($value);
36+
37+
return $value;
38+
}
39+
40+
public function decrypt(string $value): string
41+
{
42+
// Allow to do not decrypt the value for the current request
43+
if (!$this->encryptionEnabler->isEncryptionEnabled()) {
44+
return $value;
45+
}
46+
$manager = $this->extractManagerFromEncryptedValue($value);
47+
$value = (new ByteString($value))->replace($manager->getEncryptionAdapter()::getCode().'.', '')->toByteString();
48+
49+
try {
50+
return $manager->decryptString($value);
51+
} catch (Exception $exception) {
52+
foreach ($this->registry->getEncryptionManagers() as $encryptionManager) {
53+
try {
54+
return $encryptionManager->decryptString($value);
55+
} catch (Exception $exception) {
56+
}
57+
}
58+
}
59+
60+
return $value;
61+
}
62+
63+
private function extractManagerFromEncryptedValue(string $value): EncryptionManagerInterface
64+
{
65+
$split = (new ByteString($value))->split('.');
66+
67+
if (count($split) < 3) {
68+
return $this->registry->getDefaultEncryptionManager();
69+
}
70+
$managerCode = $split[0].'.'.$split[1];
71+
72+
if ($this->registry->hasEncryptionManager($managerCode)) {
73+
return $this->registry->getEncryptionManager($managerCode);
74+
}
75+
76+
return $this->registry->getDefaultEncryptionManager();
77+
}
78+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Sidus\EncryptionBundle\Doctrine;
4+
5+
interface ValueEncrypterInterface
6+
{
7+
public function encrypt(string $value): string;
8+
9+
public function decrypt(string $value): string;
10+
}

src/Registry/EncryptionManagerRegistry.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
* @author Vincent Chalnot <[email protected]>
1313
*/
14-
class EncryptionManagerRegistry
14+
class EncryptionManagerRegistry implements EncryptionManagerRegistryInterface
1515
{
1616
private array $managers = [];
1717
private string $defaultCode;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Sidus\EncryptionBundle\Registry;
4+
5+
use Sidus\EncryptionBundle\Entity\CryptableInterface;
6+
use Sidus\EncryptionBundle\Entity\UserEncryptionProviderInterface;
7+
use Sidus\EncryptionBundle\Manager\EncryptionManagerInterface;
8+
9+
/**
10+
* Registry for all available encryption managers (one for each adapter).
11+
*/
12+
interface EncryptionManagerRegistryInterface
13+
{
14+
/**
15+
* @return EncryptionManagerInterface[]
16+
*/
17+
public function getEncryptionManagers(): array;
18+
19+
public function getEncryptionManagerForEntity(CryptableInterface $entity): EncryptionManagerInterface;
20+
21+
public function getEncryptionManagerForUser(UserEncryptionProviderInterface $user): EncryptionManagerInterface;
22+
23+
public function getEncryptionManager(string $code): EncryptionManagerInterface;
24+
25+
public function hasEncryptionManager(string $code): bool;
26+
27+
public function getDefaultEncryptionManager(): EncryptionManagerInterface;
28+
}

src/Resources/config/services/doctrine.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ services:
55
autoconfigure: true
66
arguments:
77
$typesConfig: "%doctrine.dbal.connection_factory.types%"
8-
$encryptionManager: '@Sidus\EncryptionBundle\Registry\EncryptionManagerRegistry'
8+
$valueEncrypter: '@Sidus\EncryptionBundle\Doctrine\ValueEncrypterInterface'

0 commit comments

Comments
 (0)