Skip to content
Open
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
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.2.0 || ~8.3.0 || ~8.4.0",
"doctrine/dbal": "^4.0.0",
"doctrine/migrations": "^3.3.2",
"patchlevel/hydrator": "^1.8.0",
"patchlevel/hydrator": "^2.0.x-dev",
"patchlevel/worker": "^1.4.0",
"psr/cache": "^2.0.0 || ^3.0.0",
"psr/clock": "^1.0",
Expand Down
891 changes: 417 additions & 474 deletions composer.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ nav:
- Identifier: identifier.md
- Normalizer: normalizer.md
- Snapshots: snapshots.md
- Personal Data: personal_data.md
- Sensitive Data: sensitive_data.md
- Upcasting: upcasting.md
- Message Decorator: message_decorator.md
- Split Stream: split_stream.md
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ powered by the reliable Doctrine ecosystem and focused on developer experience.
* Automatic [snapshot](snapshots.md)-system to boost your performance
* [Split](split_stream.md) big aggregates into multiple streams
* Versioned and managed lifecycle of [subscriptions](subscription.md) like projections and processors
* Safe usage of [Personal Data](personal_data.md) with crypto-shredding
* Safe usage of [Personal Data](sensitive_data.md) with crypto-shredding
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Safe usage of [Personal Data](sensitive_data.md) with crypto-shredding
* Safe usage of [Sensitive Data](sensitive_data.md), like personal data, with crypto-shredding

* Smooth [upcasting](upcasting.md) of old events
* Simple setup with [scheme management](store.md) and [doctrine migration](store.md)
* Built in [cli commands](cli.md) with [symfony](https://symfony.com/)
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/normalizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ final class CreateHotel
```
!!! note

If you have personal data, you can use [crypto-shredding](personal_data.md).
If you have personal data, you can use [crypto-shredding](sensitive_data.md).

### Aggregate

Expand Down Expand Up @@ -459,4 +459,4 @@ final class DTO
* [How to define aggregates](aggregate.md)
* [How to define events](events.md)
* [How to snapshot aggregates](snapshots.md)
* [How to work with personal data](personal_data.md)
* [How to work with personal data](sensitive_data.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* [How to work with personal data](sensitive_data.md)
* [How to work with sensitive data](sensitive_data.md)

32 changes: 16 additions & 16 deletions docs/pages/personal_data.md → docs/pages/sensitive_data.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Personal Data (GDPR)
# Sensitive Data

According to GDPR, personal data must be able to be deleted upon request.
But here we have the problem that our events are immutable and we cannot easily manipulate the event store.
Expand Down Expand Up @@ -43,45 +43,45 @@ final class EmailChanged

You can use the `DataSubjectId` in aggregates for snapshots too.

### PersonalData
### SensitiveData

Next, you have to mark the properties that should be encrypted with the `#[PersonalData]` attribute.
Next, you have to mark the properties that should be encrypted with the `#[SensitiveData]` attribute.

```php
use Patchlevel\EventSourcing\Identifier\Uuid;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\SensitiveData;

final class EmailChanged
{
public function __construct(
#[DataSubjectId]
public readonly Uuid $profileId,
#[PersonalData]
#[SensitiveData]
public readonly string|null $email,
) {
}
}
```
!!! tip

You can use the `PersonalData` in aggregates for snapshots too.
You can use the `SensitiveData` in aggregates for snapshots too.

If the information could not be decrypted, then a fallback value will be used.
The default fallback value is `null`.
You can change this by setting the `fallback` parameter or using the `fallbackCallable` parameter.

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

final class ProfileChanged
{
public function __construct(
#[DataSubjectId]
public readonly Uuid $profileId,
#[PersonalData(fallback: 'unknown')]
#[SensitiveData(fallback: 'unknown')]
public readonly string $name,
#[PersonalData(fallbackCallable: [self::class, 'createAnonymousEmail'])]
#[SensitiveData(fallbackCallable: [self::class, 'createAnonymousEmail'])]
public readonly string $email,
) {
}
Expand Down Expand Up @@ -144,10 +144,10 @@ Now we have to put the whole thing together in a Personal Data Payload Cryptogra

```php
use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore;
use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;
use Patchlevel\Hydrator\Cryptography\SensitiveDataPayloadCryptographer;

/** @var CipherKeyStore $cipherKeyStore */
$cryptographer = PersonalDataPayloadCryptographer::createWithDefaultSettings($cipherKeyStore);
$cryptographer = SensitiveDataPayloadCryptographer::createWithDefaultSettings($cipherKeyStore);
```
!!! tip

Expand All @@ -159,9 +159,9 @@ The last step is to integrate the cryptographer into the event store.

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

/** @var PersonalDataPayloadCryptographer $cryptographer */
/** @var SensitiveDataPayloadCryptographer $cryptographer */
DefaultEventSerializer::createFromPaths(
[__DIR__ . '/Events'],
cryptographer: $cryptographer,
Expand All @@ -177,9 +177,9 @@ And for the snapshot store.

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

/** @var PersonalDataPayloadCryptographer $cryptographer */
/** @var SensitiveDataPayloadCryptographer $cryptographer */
$snapshotStore = DefaultSnapshotStore::createDefault(
[
/* adapters... */
Expand All @@ -206,7 +206,7 @@ use Patchlevel\EventSourcing\Message\Message;
use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore;

#[Processor('delete_personal_data')]
final class DeletePersonalDataProcessor
final class DeleteSensitiveDataProcessor
{
public function __construct(
private readonly CipherKeyStore $cipherKeyStore,
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/snapshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,4 @@ And if the version is no longer correct and the snapshot is therefore invalid, t
* [How to define aggregates](aggregate.md)
* [How to store and load aggregates](repository.md)
* [How to split streams](split_stream.md)
* [How to work with personal data](personal_data.md)
* [How to work with personal data](sensitive_data.md)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* [How to work with personal data](sensitive_data.md)
* [How to work with sensitive data](sensitive_data.md)

10 changes: 9 additions & 1 deletion src/Serializer/DefaultEventSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
use Patchlevel\EventSourcing\Serializer\Encoder\JsonEncoder;
use Patchlevel\EventSourcing\Serializer\Upcast\Upcast;
use Patchlevel\EventSourcing\Serializer\Upcast\Upcaster;
use Patchlevel\Hydrator\Cryptography\CryptographyMetadataFactory;
use Patchlevel\Hydrator\Cryptography\PayloadCryptographer;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
use Patchlevel\Hydrator\MetadataHydrator;

final class DefaultEventSerializer implements EventSerializer
Expand Down Expand Up @@ -59,9 +61,15 @@
Upcaster|null $upcaster = null,
PayloadCryptographer|null $cryptographer = null,
): static {
$metadataFactory = new AttributeMetadataFactory();

if ($cryptographer) {

Check warning on line 66 in src/Serializer/DefaultEventSerializer.php

View workflow job for this annotation

GitHub Actions / Mutation tests on diff (locked, 8.4, ubuntu-latest)

Escaped Mutant for Mutator "IfNegation": @@ @@ public static function createFromPaths(array $paths, Upcaster|null $upcaster = null, PayloadCryptographer|null $cryptographer = null): static { $metadataFactory = new AttributeMetadataFactory(); - if ($cryptographer) { + if (!$cryptographer) { $metadataFactory = new CryptographyMetadataFactory($metadataFactory); } return new self((new AttributeEventRegistryFactory())->create($paths), new MetadataHydrator($metadataFactory, $cryptographer), new JsonEncoder(), $upcaster); } }
$metadataFactory = new CryptographyMetadataFactory($metadataFactory);
}

return new self(
(new AttributeEventRegistryFactory())->create($paths),
new MetadataHydrator(cryptographer: $cryptographer),
new MetadataHydrator($metadataFactory, $cryptographer),
new JsonEncoder(),
$upcaster,
);
Expand Down
10 changes: 9 additions & 1 deletion src/Snapshot/DefaultSnapshotStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootMetadataAwareMetadataFactory;
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootMetadataFactory;
use Patchlevel\EventSourcing\Snapshot\Adapter\SnapshotAdapter;
use Patchlevel\Hydrator\Cryptography\CryptographyMetadataFactory;
use Patchlevel\Hydrator\Cryptography\PayloadCryptographer;
use Patchlevel\Hydrator\Hydrator;
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
use Patchlevel\Hydrator\MetadataHydrator;
use Throwable;

Expand Down Expand Up @@ -120,9 +122,15 @@ private function version(string $aggregateClass): string|null
/** @param array<string, SnapshotAdapter> $snapshotAdapters */
public static function createDefault(array $snapshotAdapters, PayloadCryptographer|null $cryptographer = null): self
{
$metadataFactory = new AttributeMetadataFactory();

if ($cryptographer) {
$metadataFactory = new CryptographyMetadataFactory($metadataFactory);
}

return new self(
new ArrayAdapterRepository($snapshotAdapters),
new MetadataHydrator(cryptographer: $cryptographer),
new MetadataHydrator($metadataFactory, $cryptographer),
);
}
}
4 changes: 2 additions & 2 deletions tests/Benchmark/BasicImplementation/Events/EmailChanged.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
use Patchlevel\EventSourcing\Attribute\Event;
use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\SensitiveData;

#[Event('profile.email_changed')]
final class EmailChanged
{
public function __construct(
#[DataSubjectId]
public ProfileId $profileId,
#[PersonalData]
#[SensitiveData]
public string|null $email,
) {
}
Expand Down
4 changes: 2 additions & 2 deletions tests/Benchmark/BasicImplementation/Events/ProfileCreated.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Patchlevel\EventSourcing\Attribute\EventTag;
use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\SensitiveData;

#[Event('profile.created')]
final class ProfileCreated
Expand All @@ -18,7 +18,7 @@ public function __construct(
#[EventTag(prefix: 'profile')]
public ProfileId $profileId,
public string $name,
#[PersonalData]
#[SensitiveData]
public string|null $email,
) {
}
Expand Down
4 changes: 2 additions & 2 deletions tests/Benchmark/PersonalDataBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Profile;
use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId;
use Patchlevel\EventSourcing\Tests\DbalManager;
use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer;
use Patchlevel\Hydrator\Cryptography\SensitiveDataPayloadCryptographer;
use PhpBench\Attributes as Bench;

#[Bench\BeforeMethods('setUp')]
Expand All @@ -34,7 +34,7 @@ public function setUp(): void

$cipherKeyStore = new DoctrineCipherKeyStore($connection);

$cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl(
$cryptographer = SensitiveDataPayloadCryptographer::createWithOpenssl(
$cipherKeyStore,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events;
namespace Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events;

use Patchlevel\EventSourcing\Attribute\Event;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\ProfileId;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\SensitiveData;

#[Event('profile.name_changed')]
final class NameChanged
{
public function __construct(
#[DataSubjectId]
public readonly ProfileId $aggregateId,
#[PersonalData(fallback: 'unknown')]
#[SensitiveData(fallback: 'unknown')]
public readonly string $name,
) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events;
namespace Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events;

use Patchlevel\EventSourcing\Attribute\Event;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\ProfileId;

#[Event('profile.personal_data_removed')]
final class PersonalDataRemoved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events;
namespace Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events;

use Patchlevel\EventSourcing\Attribute\Event;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\ProfileId;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\SensitiveData;

#[Event('profile.created')]
final class ProfileCreated
{
public function __construct(
#[DataSubjectId]
public ProfileId $profileId,
#[PersonalData(fallback: 'unknown')]
#[SensitiveData(fallback: 'unknown')]
public string $name,
) {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData\Processor;
namespace Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Processor;

use Patchlevel\EventSourcing\Attribute\Processor;
use Patchlevel\EventSourcing\Attribute\Subscribe;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events\PersonalDataRemoved;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events\PersonalDataRemoved;
use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore;

#[Processor('delete_personal_data')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData;
namespace Patchlevel\EventSourcing\Tests\Integration\SensitiveData;

use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot;
use Patchlevel\EventSourcing\Attribute\Aggregate;
use Patchlevel\EventSourcing\Attribute\Apply;
use Patchlevel\EventSourcing\Attribute\Id;
use Patchlevel\EventSourcing\Attribute\Snapshot;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events\NameChanged;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events\PersonalDataRemoved;
use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events\ProfileCreated;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events\NameChanged;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events\PersonalDataRemoved;
use Patchlevel\EventSourcing\Tests\Integration\SensitiveData\Events\ProfileCreated;
use Patchlevel\Hydrator\Attribute\DataSubjectId;
use Patchlevel\Hydrator\Attribute\PersonalData;
use Patchlevel\Hydrator\Attribute\SensitiveData;

#[Aggregate('profile')]
#[Snapshot('default', 2)]
Expand All @@ -23,7 +23,7 @@ final class Profile extends BasicAggregateRoot
#[DataSubjectId]
private ProfileId $id;

#[PersonalData(fallback: 'unknown')]
#[SensitiveData(fallback: 'unknown')]
private string $name;

public static function create(ProfileId $id, string $name): self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData;
namespace Patchlevel\EventSourcing\Tests\Integration\SensitiveData;

use Patchlevel\EventSourcing\Identifier\Identifier;
use Patchlevel\EventSourcing\Identifier\RamseyUuidV7Behaviour;
Expand Down
Loading
Loading