Skip to content

Commit

Permalink
move cryptography into hydrator
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Apr 11, 2024
1 parent 6279164 commit 2a2ccd8
Show file tree
Hide file tree
Showing 57 changed files with 53 additions and 2,339 deletions.
39 changes: 1 addition & 38 deletions baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,7 @@
<code><![CDATA[class DoctrineHelper]]></code>
</ClassNotFinal>
</file>
<file src="src/Cryptography/Cipher/OpensslCipherKeyFactory.php">
<ArgumentTypeCoercion>
<code><![CDATA[openssl_random_pseudo_bytes($this->ivLength)]]></code>
<code><![CDATA[openssl_random_pseudo_bytes($this->keyLength)]]></code>
</ArgumentTypeCoercion>
<LessSpecificReturnStatement>
<code><![CDATA[openssl_get_cipher_methods(true)]]></code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<code><![CDATA[list<string>]]></code>
</MoreSpecificReturnType>
</file>
<file src="src/Cryptography/EventPayloadCryptographer.php">
<MixedArgument>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
</MixedArgument>
<MixedAssignment>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
</MixedAssignment>
</file>
<file src="src/Cryptography/SnapshotPayloadCryptographer.php">
<MixedArgument>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
</MixedArgument>
<MixedAssignment>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
<code><![CDATA[$data[$propertyMetadata->fieldName]]]></code>
</MixedAssignment>
</file>
<file src="src/Cryptography/Store/DoctrineCipherKeyStore.php">
<file src="src/Cryptography/DoctrineCipherKeyStore.php">
<ArgumentTypeCoercion>
<code><![CDATA[base64_decode($result['crypto_iv'])]]></code>
<code><![CDATA[base64_decode($result['crypto_key'])]]></code>
Expand Down Expand Up @@ -219,11 +187,6 @@
<code><![CDATA[$name]]></code>
</PropertyNotSetInConstructor>
</file>
<file src="tests/Unit/Cryptography/Cipher/OpensslCipherTest.php">
<MixedAssignment>
<code><![CDATA[$return]]></code>
</MixedAssignment>
</file>
<file src="tests/Unit/Fixture/MessageNormalizer.php">
<MixedArgumentTypeCoercion>
<code><![CDATA[$value]]></code>
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"doctrine/dbal": "^3.8.1|^4.0.0",
"doctrine/migrations": "^3.3.2",
"patchlevel/hydrator": "^1.2.0",
"patchlevel/hydrator": "^1.3.1",
"patchlevel/worker": "^1.2.0",
"psr/cache": "^2.0.0|^3.0.0",
"psr/clock": "^1.0",
Expand Down
167 changes: 20 additions & 147 deletions docs/pages/personal_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The first solution is not to save the personal data in the Event Store at all
and use something different for this, for example a separate table or an ORM.

The other option the library offers is crypto shredding.
In this process, the personal data is encrypted with a key that is assigned to a subject (person).
In this process, the personal data is encrypted with a key that is assigned to a subject (like person).
When saving and reading the events, this key is then used to convert the data.
This key with the subject is saved in a database.

Expand All @@ -26,7 +26,7 @@ First of all, we have to mark the fields that contain personal data.
For our example, we use events, but you can do the same with aggregates.

```php
use Patchlevel\EventSourcing\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\PersonalData;

final class EmailChanged
{
Expand All @@ -47,7 +47,7 @@ You can change this by setting the `fallback` parameter.
In this case `unknown` is added:

```php
use Patchlevel\EventSourcing\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\PersonalData;

final class EmailChanged
{
Expand Down Expand Up @@ -76,8 +76,8 @@ In order for the correct key to be used, a subject ID must be defined.
Without Subject Id, no personal data can be encrypted or decrypted.

```php
use Patchlevel\EventSourcing\Attribute\DataSubjectId;
use Patchlevel\EventSourcing\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;

final class EmailChanged
{
Expand All @@ -102,55 +102,23 @@ final class EmailChanged

In order for the system to work, a few things have to be done.

!!! tip

You can use named constructor `EventPayloadCryptographer::createWithOpenssl` and `SnapshotPayloadCryptographer::createWithOpenssl` to skip some necessary setups.

### Cipher Key Factory

We need a factory to generate keys. We provide an openssl implementation by default.

```php
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipherKeyFactory;

$cipherKeyFactory = new OpensslCipherKeyFactory();
$cipherKey = $cipherKeyFactory();
```
You can change the algorithm by passing it as a parameter.

```php
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipherKeyFactory;

$cipherKeyFactory = new OpensslCipherKeyFactory('aes256');
$cipherKey = $cipherKeyFactory();
```
!!! tip

With `OpensslCipherKeyFactory::supportedMethods()` you can get a list of all available algorithms.

### Cipher Key Store

The keys must be stored somewhere. For this we provide a doctrine implementation.

```php
use Doctrine\DBAL\Connection;
use Patchlevel\EventSourcing\Cryptography\Cipher\CipherKey;
use Patchlevel\EventSourcing\Cryptography\Store\DoctrineCipherKeyStore;
use Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore;

/** @var Connection $dbalConnection */
$cipherKeyStore = new DoctrineCipherKeyStore($dbalConnection);

/** @var CipherKey $cipherKey */
$cipherKeyStore->store('personId', $cipherKey);
$cipherKey = $cipherKeyStore->get('personId');
$cipherKeyStore->remove('personId');
```
To use the `DoctrineCipherKeyStore` you need to register this service in Doctrine Schema Director.
Then the table will be added automatically.

```php
use Doctrine\DBAL\Connection;
use Patchlevel\EventSourcing\Cryptography\Store\DoctrineCipherKeyStore;
use Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore;
use Patchlevel\EventSourcing\Schema\ChainDoctrineSchemaConfigurator;
use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector;
use Patchlevel\EventSourcing\Store\Store;
Expand All @@ -168,121 +136,30 @@ $schemaDirector = new DoctrineSchemaDirector(
]),
);
```
### Cipher
### Personal Data Payload Cryptographer

The encryption and decryption is handled by the `Cipher`.
We offer an openssl implementation by default.
Now we have to put the whole thing together in a Personal Data Payload Cryptographer.

```php
use Patchlevel\EventSourcing\Cryptography\Cipher\CipherKey;
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipher;

$cipher = new OpensslCipher();

/**
* @var CipherKey $cipherKey
* @var mixed $value
*/
$encrypted = $cipher->encrypt($cipherKey, $value);
$value = $cipher->decrypt($cipherKey, $encrypted);
```
!!! note

If the encryption or decryption fails, an exception `EncryptionFailed` or `DecryptionFailed` is thrown.

### Event Payload Cryptographer

Now we have to put the whole thing together in an Event Payload Cryptographer.

```php
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipher;
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipherKeyFactory;
use Patchlevel\EventSourcing\Cryptography\EventPayloadCryptographer;
use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore;
use Patchlevel\EventSourcing\Metadata\Event\EventMetadataFactory;
use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;

/**
* @var EventMetadataFactory $eventMetadataFactory
* @var CipherKeyStore $cipherKeyStore
* @var OpensslCipherKeyFactory $cipherKeyFactory
* @var OpensslCipher $cipher
*/
$cryptographer = new EventPayloadCryptographer(
$eventMetadataFactory,
$cipherKeyStore,
$cipherKeyFactory,
$cipher,
);
/** @var CipherKeyStore $cipherKeyStore */
$cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl($cipherKeyStore);
```
You can also use the shortcut with openssl.

```php
use Patchlevel\EventSourcing\Cryptography\EventPayloadCryptographer;
use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore;
use Patchlevel\EventSourcing\Metadata\Event\EventMetadataFactory;

/**
* @var EventMetadataFactory $eventMetadataFactory
* @var CipherKeyStore $cipherKeyStore
*/
$cryptographer = EventPayloadCryptographer::createWithOpenssl(
$eventMetadataFactory,
$cipherKeyStore,
);
```
### Snapshot Payload Cryptographer

You can also use the cryptographer for snapshots.

```php
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipher;
use Patchlevel\EventSourcing\Cryptography\Cipher\OpensslCipherKeyFactory;
use Patchlevel\EventSourcing\Cryptography\SnapshotPayloadCryptographer;
use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore;
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootMetadataFactory;

/**
* @var AggregateRootMetadataFactory $aggregateRootMetadataFactory
* @var CipherKeyStore $cipherKeyStore
* @var OpensslCipherKeyFactory $cipherKeyFactory
* @var OpensslCipher $cipher
*/
$cryptographer = new SnapshotPayloadCryptographer(
$aggregateRootMetadataFactory,
$cipherKeyStore,
$cipherKeyFactory,
$cipher,
);
```
You can also use the shortcut with openssl.

```php
use Patchlevel\EventSourcing\Cryptography\SnapshotPayloadCryptographer;
use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore;
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootMetadataFactory;
!!! tip

/**
* @var AggregateRootMetadataFactory $aggregateRootMetadataFactory
* @var CipherKeyStore $cipherKeyStore
*/
$cryptographer = SnapshotPayloadCryptographer::createWithOpenssl(
$aggregateRootMetadataFactory,
$cipherKeyStore,
);
```
You can specify the cipher method with the second parameter.

### Event Serializer Integration

The last step is to integrate the cryptographer into the event store.

```php
use Patchlevel\EventSourcing\Cryptography\EventPayloadCryptographer;
use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;

/**
* @var Hydrator $hydrator
* @var EventPayloadCryptographer $cryptographer
*/
/** @var PersonalDataPayloadCryptographer $cryptographer */
DefaultEventSerializer::createFromPaths(
[__DIR__ . '/Events'],
cryptographer: $cryptographer,
Expand All @@ -297,14 +174,10 @@ DefaultEventSerializer::createFromPaths(
And for the snapshot store.

```php
use Patchlevel\EventSourcing\Cryptography\SnapshotPayloadCryptographer;
use Patchlevel\EventSourcing\Snapshot\DefaultSnapshotStore;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;

/**
* @var Hydrator $hydrator
* @var SnapshotPayloadCryptographer $cryptographer
*/
/** @var PersonalDataPayloadCryptographer $cryptographer */
$snapshotStore = DefaultSnapshotStore::createDefault(
[
/* adapters... */
Expand All @@ -327,8 +200,8 @@ To remove personal data, you can either remove the key manually or do it with a
```php
use Patchlevel\EventSourcing\Attribute\Processor;
use Patchlevel\EventSourcing\Attribute\Subscribe;
use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore;
use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore;

#[Processor('delete_personal_data')]
final class DeletePersonalDataProcessor
Expand Down
12 changes: 0 additions & 12 deletions src/Attribute/DataSubjectId.php

This file was deleted.

16 changes: 0 additions & 16 deletions src/Attribute/PersonalData.php

This file was deleted.

14 changes: 0 additions & 14 deletions src/Cryptography/Cipher/Cipher.php

This file was deleted.

20 changes: 0 additions & 20 deletions src/Cryptography/Cipher/CipherKey.php

This file was deleted.

11 changes: 0 additions & 11 deletions src/Cryptography/Cipher/CipherKeyFactory.php

This file was deleted.

Loading

0 comments on commit 2a2ccd8

Please sign in to comment.