From 84d3372f70a704a52130f18641c4e5960d406b47 Mon Sep 17 00:00:00 2001 From: David Badura Date: Thu, 11 Apr 2024 12:58:43 +0200 Subject: [PATCH] move cryptography into hydrator --- baseline.xml | 39 +-- composer.json | 2 +- composer.lock | 193 ++++++------ docs/pages/personal_data.md | 167 ++--------- phpstan-baseline.neon | 28 +- src/Attribute/DataSubjectId.php | 12 - src/Attribute/PersonalData.php | 16 - src/Cryptography/Cipher/Cipher.php | 14 - src/Cryptography/Cipher/CipherKey.php | 20 -- src/Cryptography/Cipher/CipherKeyFactory.php | 11 - .../Cipher/CreateCipherKeyFailed.php | 15 - src/Cryptography/Cipher/DecryptionFailed.php | 15 - src/Cryptography/Cipher/EncryptionFailed.php | 15 - .../Cipher/MethodNotSupported.php | 17 -- src/Cryptography/Cipher/OpensslCipher.php | 67 ----- .../Cipher/OpensslCipherKeyFactory.php | 65 ----- src/Cryptography/CryptographicHydrator.php | 39 --- .../{Store => }/DoctrineCipherKeyStore.php | 6 +- .../EventPayloadCryptographer.php | 145 --------- src/Cryptography/MissingSubjectId.php | 15 - src/Cryptography/PayloadCryptographer.php | 24 -- .../SnapshotPayloadCryptographer.php | 157 ---------- src/Cryptography/Store/CipherKeyNotExists.php | 17 -- src/Cryptography/Store/CipherKeyStore.php | 17 -- .../Store/InMemoryCipherKeyStore.php | 33 --- src/Cryptography/UnsupportedClass.php | 17 -- src/Cryptography/UnsupportedSubjectId.php | 18 -- .../AggregateRoot/AggregateRootMetadata.php | 3 - .../AttributeAggregateRootMetadataFactory.php | 88 ------ .../AggregateRoot/PropertyMetadata.php | 16 - .../Event/AttributeEventMetadataFactory.php | 88 ------ src/Metadata/Event/EventMetadata.php | 3 - src/Metadata/Event/PropertyMetadata.php | 16 - src/Serializer/DefaultEventSerializer.php | 11 +- src/Snapshot/DefaultSnapshotStore.php | 14 +- .../Events/EmailChanged.php | 4 +- .../Events/ProfileCreated.php | 4 +- tests/Benchmark/PersonalDataBench.php | 8 +- .../PersonalData/Events/NameChanged.php | 4 +- .../PersonalData/Events/ProfileCreated.php | 4 +- .../PersonalData/PersonalDataTest.php | 32 +- .../Processor/DeletePersonalDataProcessor.php | 2 +- tests/Integration/PersonalData/Profile.php | 4 +- .../Cipher/CreateCipherKeyFailedTest.php | 19 -- .../Cipher/DecryptionFailedTest.php | 19 -- .../Cipher/EncryptionFailedTest.php | 19 -- .../Cipher/OpensslCipherKeyFactoryTest.php | 33 --- .../Cryptography/Cipher/OpensslCipherTest.php | 78 ----- .../CryptographicHydratorTest.php | 74 ----- .../EventPayloadCryptographerTest.php | 274 ------------------ .../Cryptography/MissingSubjectIdTest.php | 19 -- .../SnapshotPayloadCryptographerTest.php | 274 ------------------ .../Store/CipherKeyNotExistsTest.php | 19 -- .../Store/InMemoryCipherKeyStoreTest.php | 76 ----- .../Cryptography/UnsupportedSubjectIdTest.php | 19 -- tests/Unit/Fixture/EmailChanged.php | 4 - tests/Unit/Fixture/ProfileWithSnapshot.php | 4 - .../AttributeAggregateMetadataFactoryTest.php | 99 ------- .../AttributeEventMetadataFactoryTest.php | 98 ------- 59 files changed, 155 insertions(+), 2458 deletions(-) delete mode 100644 src/Attribute/DataSubjectId.php delete mode 100644 src/Attribute/PersonalData.php delete mode 100644 src/Cryptography/Cipher/Cipher.php delete mode 100644 src/Cryptography/Cipher/CipherKey.php delete mode 100644 src/Cryptography/Cipher/CipherKeyFactory.php delete mode 100644 src/Cryptography/Cipher/CreateCipherKeyFailed.php delete mode 100644 src/Cryptography/Cipher/DecryptionFailed.php delete mode 100644 src/Cryptography/Cipher/EncryptionFailed.php delete mode 100644 src/Cryptography/Cipher/MethodNotSupported.php delete mode 100644 src/Cryptography/Cipher/OpensslCipher.php delete mode 100644 src/Cryptography/Cipher/OpensslCipherKeyFactory.php delete mode 100644 src/Cryptography/CryptographicHydrator.php rename src/Cryptography/{Store => }/DoctrineCipherKeyStore.php (92%) delete mode 100644 src/Cryptography/EventPayloadCryptographer.php delete mode 100644 src/Cryptography/MissingSubjectId.php delete mode 100644 src/Cryptography/PayloadCryptographer.php delete mode 100644 src/Cryptography/SnapshotPayloadCryptographer.php delete mode 100644 src/Cryptography/Store/CipherKeyNotExists.php delete mode 100644 src/Cryptography/Store/CipherKeyStore.php delete mode 100644 src/Cryptography/Store/InMemoryCipherKeyStore.php delete mode 100644 src/Cryptography/UnsupportedClass.php delete mode 100644 src/Cryptography/UnsupportedSubjectId.php delete mode 100644 src/Metadata/AggregateRoot/PropertyMetadata.php delete mode 100644 src/Metadata/Event/PropertyMetadata.php delete mode 100644 tests/Unit/Cryptography/Cipher/CreateCipherKeyFailedTest.php delete mode 100644 tests/Unit/Cryptography/Cipher/DecryptionFailedTest.php delete mode 100644 tests/Unit/Cryptography/Cipher/EncryptionFailedTest.php delete mode 100644 tests/Unit/Cryptography/Cipher/OpensslCipherKeyFactoryTest.php delete mode 100644 tests/Unit/Cryptography/Cipher/OpensslCipherTest.php delete mode 100644 tests/Unit/Cryptography/CryptographicHydratorTest.php delete mode 100644 tests/Unit/Cryptography/EventPayloadCryptographerTest.php delete mode 100644 tests/Unit/Cryptography/MissingSubjectIdTest.php delete mode 100644 tests/Unit/Cryptography/SnapshotPayloadCryptographerTest.php delete mode 100644 tests/Unit/Cryptography/Store/CipherKeyNotExistsTest.php delete mode 100644 tests/Unit/Cryptography/Store/InMemoryCipherKeyStoreTest.php delete mode 100644 tests/Unit/Cryptography/UnsupportedSubjectIdTest.php diff --git a/baseline.xml b/baseline.xml index 76dfb3a9..b5d658ce 100644 --- a/baseline.xml +++ b/baseline.xml @@ -24,39 +24,7 @@ - - - ivLength)]]> - keyLength)]]> - - - - - - ]]> - - - - - fieldName]]]> - - - fieldName]]]> - fieldName]]]> - fieldName]]]> - - - - - fieldName]]]> - - - fieldName]]]> - fieldName]]]> - fieldName]]]> - - - + @@ -219,11 +187,6 @@ - - - - - diff --git a/composer.json b/composer.json index 5a4c9782..b97516b6 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/composer.lock b/composer.lock index e4e54bf8..afd7a5e8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "261a3a90df7128eab9af6bde5b094bcc", + "content-hash": "9668b91958d5be53bf23a03ba3ec9317", "packages": [ { "name": "brick/math", @@ -411,19 +411,20 @@ }, { "name": "patchlevel/hydrator", - "version": "1.2.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/patchlevel/hydrator.git", - "reference": "c9f1166513c55c9e659925dcbdfc693ac57ecae4" + "reference": "2797c421e4b601a3b1fc6ad1ed3e11ddfaddbba8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/c9f1166513c55c9e659925dcbdfc693ac57ecae4", - "reference": "c9f1166513c55c9e659925dcbdfc693ac57ecae4", + "url": "https://api.github.com/repos/patchlevel/hydrator/zipball/2797c421e4b601a3b1fc6ad1ed3e11ddfaddbba8", + "reference": "2797c421e4b601a3b1fc6ad1ed3e11ddfaddbba8", "shasum": "" }, "require": { + "ext-openssl": "*", "php": "~8.1.0 || ~8.2.0 || ~8.3.0" }, "require-dev": { @@ -434,7 +435,7 @@ "phpspec/prophecy-phpunit": "^2.1.0", "phpstan/phpstan": "^1.10.49", "phpunit/phpunit": "^10.5.2", - "psalm/plugin-phpunit": "^0.18.4", + "psalm/plugin-phpunit": "^0.19.0", "psr/cache": "^2.0.0|^3.0.0", "psr/simple-cache": "^2.0.0|^3.0.0", "roave/infection-static-analysis-plugin": "^1.34.0", @@ -469,9 +470,9 @@ ], "support": { "issues": "https://github.com/patchlevel/hydrator/issues", - "source": "https://github.com/patchlevel/hydrator/tree/1.2.0" + "source": "https://github.com/patchlevel/hydrator/tree/1.3.1" }, - "time": "2024-02-26T08:32:12+00:00" + "time": "2024-04-11T10:31:49+00:00" }, { "name": "patchlevel/worker", @@ -1020,16 +1021,16 @@ }, { "name": "symfony/console", - "version": "v7.0.4", + "version": "v7.0.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" + "reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", + "url": "https://api.github.com/repos/symfony/console/zipball/fde915cd8e7eb99b3d531d3d5c09531429c3f9e5", + "reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5", "shasum": "" }, "require": { @@ -1093,7 +1094,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.4" + "source": "https://github.com/symfony/console/tree/v7.0.6" }, "funding": [ { @@ -1109,7 +1110,7 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2024-04-01T11:04:53+00:00" }, { "name": "symfony/event-dispatcher", @@ -1193,16 +1194,16 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.4.0", + "version": "v3.4.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + "reference": "4e64b49bf370ade88e567de29465762e316e4224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", - "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/4e64b49bf370ade88e567de29465762e316e4224", + "reference": "4e64b49bf370ade88e567de29465762e316e4224", "shasum": "" }, "require": { @@ -1249,7 +1250,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.2" }, "funding": [ { @@ -1265,7 +1266,7 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-01-23T14:51:35+00:00" }, { "name": "symfony/finder", @@ -1651,16 +1652,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.4.1", + "version": "v3.4.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e", + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e", "shasum": "" }, "require": { @@ -1713,7 +1714,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.2" }, "funding": [ { @@ -1729,7 +1730,7 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2023-12-19T21:51:00+00:00" }, { "name": "symfony/stopwatch", @@ -1881,22 +1882,24 @@ }, { "name": "symfony/var-exporter", - "version": "v7.0.4", + "version": "v7.0.6", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41" + "reference": "c74c568d2a15a1d407cf40d61ea82bc2d521e27b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", - "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/c74c568d2a15a1d407cf40d61ea82bc2d521e27b", + "reference": "c74c568d2a15a1d407cf40d61ea82bc2d521e27b", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", @@ -1935,7 +1938,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.0.4" + "source": "https://github.com/symfony/var-exporter/tree/v7.0.6" }, "funding": [ { @@ -1951,7 +1954,7 @@ "type": "tidelift" } ], - "time": "2024-02-26T10:35:24+00:00" + "time": "2024-03-20T21:25:22+00:00" } ], "packages-dev": [ @@ -2439,16 +2442,16 @@ }, { "name": "composer/xdebug-handler", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" + "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/4f988f8fdf580d53bdb2d1278fe93d1ed5462255", + "reference": "4f988f8fdf580d53bdb2d1278fe93d1ed5462255", "shasum": "" }, "require": { @@ -2459,7 +2462,7 @@ "require-dev": { "phpstan/phpstan": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -2483,9 +2486,9 @@ "performance" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.4" }, "funding": [ { @@ -2501,7 +2504,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T21:32:43+00:00" + "time": "2024-03-26T18:29:49+00:00" }, { "name": "cspray/phinal", @@ -4509,25 +4512,25 @@ }, { "name": "patchlevel/event-sourcing-psalm-plugin", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/patchlevel/event-sourcing-psalm-plugin.git", - "reference": "348fc7d5c39557509767e769a39f194c6faa9cfe" + "reference": "e9d4bec1aa41731749b81967e49534334080a7fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/patchlevel/event-sourcing-psalm-plugin/zipball/348fc7d5c39557509767e769a39f194c6faa9cfe", - "reference": "348fc7d5c39557509767e769a39f194c6faa9cfe", + "url": "https://api.github.com/repos/patchlevel/event-sourcing-psalm-plugin/zipball/e9d4bec1aa41731749b81967e49534334080a7fc", + "reference": "e9d4bec1aa41731749b81967e49534334080a7fc", "shasum": "" }, "require": { - "php": "^8.1", + "php": "~8.1 || ~8.2 || ~8.3", "vimeo/psalm": "^4.23.0 || ^5.0.0" }, "require-dev": { "patchlevel/coding-standard": "^1.1.1", - "patchlevel/event-sourcing": "2.0.x-dev", + "patchlevel/event-sourcing": "^2.3.2", "roave/security-advisories": "dev-master", "symfony/var-dumper": "^6.1.0" }, @@ -4559,9 +4562,9 @@ ], "support": { "issues": "https://github.com/patchlevel/event-sourcing-psalm-plugin/issues", - "source": "https://github.com/patchlevel/event-sourcing-psalm-plugin/tree/2.1.0" + "source": "https://github.com/patchlevel/event-sourcing-psalm-plugin/tree/2.2.0" }, - "time": "2023-01-25T10:32:23+00:00" + "time": "2024-03-26T10:14:20+00:00" }, { "name": "phar-io/manifest", @@ -5172,16 +5175,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.27.0", + "version": "1.28.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757" + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/86e4d5a4b036f8f0be1464522f4c6b584c452757", - "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", "shasum": "" }, "require": { @@ -5213,22 +5216,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.27.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" }, - "time": "2024-03-21T13:14:53+00:00" + "time": "2024-04-03T18:51:33+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.64", + "version": "1.10.66", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb" + "reference": "94779c987e4ebd620025d9e5fdd23323903950bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fb9f270daffedcb5ff46275dcafe92538b1bc4bb", - "reference": "fb9f270daffedcb5ff46275dcafe92538b1bc4bb", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/94779c987e4ebd620025d9e5fdd23323903950bd", + "reference": "94779c987e4ebd620025d9e5fdd23323903950bd", "shasum": "" }, "require": { @@ -5277,7 +5280,7 @@ "type": "tidelift" } ], - "time": "2024-03-21T09:57:47+00:00" + "time": "2024-03-28T16:17:31+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5602,16 +5605,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.15", + "version": "10.5.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "86376e05e8745ed81d88232ff92fee868247b07b" + "reference": "c1f736a473d21957ead7e94fcc029f571895abf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/86376e05e8745ed81d88232ff92fee868247b07b", - "reference": "86376e05e8745ed81d88232ff92fee868247b07b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1f736a473d21957ead7e94fcc029f571895abf5", + "reference": "c1f736a473d21957ead7e94fcc029f571895abf5", "shasum": "" }, "require": { @@ -5683,7 +5686,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.15" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.17" }, "funding": [ { @@ -5699,7 +5702,7 @@ "type": "tidelift" } ], - "time": "2024-03-22T04:17:47+00:00" + "time": "2024-04-05T04:39:01+00:00" }, { "name": "psalm/plugin-phpunit", @@ -6313,16 +6316,16 @@ }, { "name": "sebastian/environment", - "version": "6.0.1", + "version": "6.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { @@ -6337,7 +6340,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -6365,7 +6368,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -6373,7 +6376,7 @@ "type": "github" } ], - "time": "2023-04-11T05:39:26+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", @@ -7051,16 +7054,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.9.0", + "version": "3.9.1", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b" + "reference": "267a4405fff1d9c847134db3a3c92f1ab7f77909" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/267a4405fff1d9c847134db3a3c92f1ab7f77909", + "reference": "267a4405fff1d9c847134db3a3c92f1ab7f77909", "shasum": "" }, "require": { @@ -7127,7 +7130,7 @@ "type": "open_collective" } ], - "time": "2024-02-16T15:06:51+00:00" + "time": "2024-03-31T21:03:09+00:00" }, { "name": "symfony/clock", @@ -7272,16 +7275,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.0.3", + "version": "v7.0.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" + "reference": "408105dff4c104454100730bdfd1a9cdd993f04d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", - "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/408105dff4c104454100730bdfd1a9cdd993f04d", + "reference": "408105dff4c104454100730bdfd1a9cdd993f04d", "shasum": "" }, "require": { @@ -7315,7 +7318,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.0.3" + "source": "https://github.com/symfony/filesystem/tree/v7.0.6" }, "funding": [ { @@ -7331,20 +7334,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T15:02:46+00:00" + "time": "2024-03-21T19:37:36+00:00" }, { "name": "symfony/messenger", - "version": "v7.0.4", + "version": "v7.0.6", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "804a8997f93313a8f7ed19e8cca3b44fdd18bdec" + "reference": "4e281ef8bf5397be36fe14d64eb665fa12a945ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/804a8997f93313a8f7ed19e8cca3b44fdd18bdec", - "reference": "804a8997f93313a8f7ed19e8cca3b44fdd18bdec", + "url": "https://api.github.com/repos/symfony/messenger/zipball/4e281ef8bf5397be36fe14d64eb665fa12a945ad", + "reference": "4e281ef8bf5397be36fe14d64eb665fa12a945ad", "shasum": "" }, "require": { @@ -7401,7 +7404,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v7.0.4" + "source": "https://github.com/symfony/messenger/tree/v7.0.6" }, "funding": [ { @@ -7417,7 +7420,7 @@ "type": "tidelift" } ], - "time": "2024-02-26T07:52:39+00:00" + "time": "2024-03-19T11:57:22+00:00" }, { "name": "symfony/options-resolver", @@ -7706,16 +7709,16 @@ }, { "name": "symfony/var-dumper", - "version": "v7.0.4", + "version": "v7.0.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670" + "reference": "66d13dc207d5dab6b4f4c2b5460efe1bea29dbfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670", - "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/66d13dc207d5dab6b4f4c2b5460efe1bea29dbfb", + "reference": "66d13dc207d5dab6b4f4c2b5460efe1bea29dbfb", "shasum": "" }, "require": { @@ -7769,7 +7772,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.0.4" + "source": "https://github.com/symfony/var-dumper/tree/v7.0.6" }, "funding": [ { @@ -7785,7 +7788,7 @@ "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2024-03-19T11:57:22+00:00" }, { "name": "thecodingmachine/safe", diff --git a/docs/pages/personal_data.md b/docs/pages/personal_data.md index 8e8021e9..62083087 100644 --- a/docs/pages/personal_data.md +++ b/docs/pages/personal_data.md @@ -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. @@ -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 { @@ -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 { @@ -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 { @@ -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; @@ -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, @@ -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... */ @@ -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 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3e7c689d..b58925af 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6,34 +6,14 @@ parameters: path: src/Console/DoctrineHelper.php - - message: "#^Parameter \\#1 \\$key of class Patchlevel\\\\EventSourcing\\\\Cryptography\\\\Cipher\\\\CipherKey constructor expects non\\-empty\\-string, string given\\.$#" + message: "#^Parameter \\#1 \\$key of class Patchlevel\\\\Hydrator\\\\Cryptography\\\\Cipher\\\\CipherKey constructor expects non\\-empty\\-string, string given\\.$#" count: 1 - path: src/Cryptography/Cipher/OpensslCipherKeyFactory.php + path: src/Cryptography/DoctrineCipherKeyStore.php - - message: "#^Parameter \\#3 \\$iv of class Patchlevel\\\\EventSourcing\\\\Cryptography\\\\Cipher\\\\CipherKey constructor expects non\\-empty\\-string, string given\\.$#" + message: "#^Parameter \\#3 \\$iv of class Patchlevel\\\\Hydrator\\\\Cryptography\\\\Cipher\\\\CipherKey constructor expects non\\-empty\\-string, string given\\.$#" count: 1 - path: src/Cryptography/Cipher/OpensslCipherKeyFactory.php - - - - message: "#^Parameter \\#2 \\$data of method Patchlevel\\\\EventSourcing\\\\Cryptography\\\\Cipher\\\\Cipher\\:\\:decrypt\\(\\) expects string, mixed given\\.$#" - count: 1 - path: src/Cryptography/EventPayloadCryptographer.php - - - - message: "#^Parameter \\#2 \\$data of method Patchlevel\\\\EventSourcing\\\\Cryptography\\\\Cipher\\\\Cipher\\:\\:decrypt\\(\\) expects string, mixed given\\.$#" - count: 1 - path: src/Cryptography/SnapshotPayloadCryptographer.php - - - - message: "#^Parameter \\#1 \\$key of class Patchlevel\\\\EventSourcing\\\\Cryptography\\\\Cipher\\\\CipherKey constructor expects non\\-empty\\-string, string given\\.$#" - count: 1 - path: src/Cryptography/Store/DoctrineCipherKeyStore.php - - - - message: "#^Parameter \\#3 \\$iv of class Patchlevel\\\\EventSourcing\\\\Cryptography\\\\Cipher\\\\CipherKey constructor expects non\\-empty\\-string, string given\\.$#" - count: 1 - path: src/Cryptography/Store/DoctrineCipherKeyStore.php + path: src/Cryptography/DoctrineCipherKeyStore.php - message: "#^Parameter \\#2 \\$data of method Patchlevel\\\\Hydrator\\\\Hydrator\\:\\:hydrate\\(\\) expects array\\, mixed given\\.$#" diff --git a/src/Attribute/DataSubjectId.php b/src/Attribute/DataSubjectId.php deleted file mode 100644 index e4100521..00000000 --- a/src/Attribute/DataSubjectId.php +++ /dev/null @@ -1,12 +0,0 @@ -dataEncode($data), - $key->method, - $key->key, - 0, - $key->iv, - ); - - if ($encryptedData === false) { - throw new EncryptionFailed(); - } - - return base64_encode($encryptedData); - } - - public function decrypt(CipherKey $key, string $data): mixed - { - $data = @openssl_decrypt( - base64_decode($data), - $key->method, - $key->key, - 0, - $key->iv, - ); - - if ($data === false) { - throw new DecryptionFailed(); - } - - try { - return $this->dataDecode($data); - } catch (JsonException) { - throw new DecryptionFailed(); - } - } - - private function dataEncode(mixed $data): string - { - return json_encode($data, JSON_THROW_ON_ERROR); - } - - private function dataDecode(string $data): mixed - { - return json_decode($data, true, 512, JSON_THROW_ON_ERROR); - } -} diff --git a/src/Cryptography/Cipher/OpensslCipherKeyFactory.php b/src/Cryptography/Cipher/OpensslCipherKeyFactory.php deleted file mode 100644 index 9f6bf091..00000000 --- a/src/Cryptography/Cipher/OpensslCipherKeyFactory.php +++ /dev/null @@ -1,65 +0,0 @@ -method)) { - throw new MethodNotSupported($this->method); - } - - $keyLength = 16; - - if (function_exists('openssl_cipher_key_length')) { - $keyLength = @openssl_cipher_key_length($this->method); - } - - $ivLength = @openssl_cipher_iv_length($this->method); - - if ($keyLength === false || $ivLength === false) { - throw new MethodNotSupported($this->method); - } - - $this->keyLength = $keyLength; - $this->ivLength = $ivLength; - } - - public function __invoke(): CipherKey - { - return new CipherKey( - openssl_random_pseudo_bytes($this->keyLength), - $this->method, - openssl_random_pseudo_bytes($this->ivLength), - ); - } - - /** @return list */ - public static function supportedMethods(): array - { - return openssl_get_cipher_methods(true); - } - - public static function methodSupported(string $method): bool - { - return in_array($method, self::supportedMethods(), true); - } -} diff --git a/src/Cryptography/CryptographicHydrator.php b/src/Cryptography/CryptographicHydrator.php deleted file mode 100644 index b09dd4da..00000000 --- a/src/Cryptography/CryptographicHydrator.php +++ /dev/null @@ -1,39 +0,0 @@ - $class - * @param array $data - * - * @return T - * - * @template T of object - */ - public function hydrate(string $class, array $data): object - { - $data = $this->cryptographer->decrypt($class, $data); - - return $this->hydrator->hydrate($class, $data); - } - - /** @return array */ - public function extract(object $object): array - { - $data = $this->hydrator->extract($object); - - return $this->cryptographer->encrypt($object::class, $data); - } -} diff --git a/src/Cryptography/Store/DoctrineCipherKeyStore.php b/src/Cryptography/DoctrineCipherKeyStore.php similarity index 92% rename from src/Cryptography/Store/DoctrineCipherKeyStore.php rename to src/Cryptography/DoctrineCipherKeyStore.php index 11476cd4..2bfb00da 100644 --- a/src/Cryptography/Store/DoctrineCipherKeyStore.php +++ b/src/Cryptography/DoctrineCipherKeyStore.php @@ -2,12 +2,14 @@ declare(strict_types=1); -namespace Patchlevel\EventSourcing\Cryptography\Store; +namespace Patchlevel\EventSourcing\Cryptography; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\Schema; -use Patchlevel\EventSourcing\Cryptography\Cipher\CipherKey; use Patchlevel\EventSourcing\Schema\DoctrineSchemaConfigurator; +use Patchlevel\Hydrator\Cryptography\Cipher\CipherKey; +use Patchlevel\Hydrator\Cryptography\Store\CipherKeyNotExists; +use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore; use function array_key_exists; use function base64_decode; diff --git a/src/Cryptography/EventPayloadCryptographer.php b/src/Cryptography/EventPayloadCryptographer.php deleted file mode 100644 index ddb020d2..00000000 --- a/src/Cryptography/EventPayloadCryptographer.php +++ /dev/null @@ -1,145 +0,0 @@ - $data - * - * @return array - */ - public function encrypt(string $class, array $data): array - { - $subjectId = $this->subjectId($class, $data); - - if ($subjectId === null) { - return $data; - } - - try { - $cipherKey = $this->cipherKeyStore->get($subjectId); - } catch (CipherKeyNotExists) { - $cipherKey = ($this->cipherKeyFactory)(); - $this->cipherKeyStore->store($subjectId, $cipherKey); - } - - $metadata = $this->metadataFactory->metadata($class); - - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if (!$propertyMetadata->isPersonalData) { - continue; - } - - $data[$propertyMetadata->fieldName] = $this->cipher->encrypt( - $cipherKey, - $data[$propertyMetadata->fieldName], - ); - } - - return $data; - } - - /** - * @param class-string $class - * @param array $data - * - * @return array - */ - public function decrypt(string $class, array $data): array - { - $subjectId = $this->subjectId($class, $data); - - if ($subjectId === null) { - return $data; - } - - try { - $cipherKey = $this->cipherKeyStore->get($subjectId); - } catch (CipherKeyNotExists) { - $cipherKey = null; - } - - $metadata = $this->metadataFactory->metadata($class); - - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if (!$propertyMetadata->isPersonalData) { - continue; - } - - if (!$cipherKey) { - $data[$propertyMetadata->fieldName] = $propertyMetadata->personalDataFallback; - continue; - } - - try { - $data[$propertyMetadata->fieldName] = $this->cipher->decrypt( - $cipherKey, - $data[$propertyMetadata->fieldName], - ); - } catch (DecryptionFailed) { - $data[$propertyMetadata->fieldName] = $propertyMetadata->personalDataFallback; - } - } - - return $data; - } - - /** - * @param class-string $class - * @param array $data - */ - private function subjectId(string $class, array $data): string|null - { - $metadata = $this->metadataFactory->metadata($class); - - if ($metadata->dataSubjectIdField === null) { - return null; - } - - if (!array_key_exists($metadata->dataSubjectIdField, $data)) { - throw new MissingSubjectId(); - } - - $subjectId = $data[$metadata->dataSubjectIdField]; - - if (!is_string($subjectId)) { - throw new UnsupportedSubjectId($subjectId); - } - - return $subjectId; - } - - public static function createWithOpenssl(EventMetadataFactory $metadataFactory, CipherKeyStore $cryptoStore): static - { - return new self( - $metadataFactory, - $cryptoStore, - new OpensslCipherKeyFactory(), - new OpensslCipher(), - ); - } -} diff --git a/src/Cryptography/MissingSubjectId.php b/src/Cryptography/MissingSubjectId.php deleted file mode 100644 index 37dc7c91..00000000 --- a/src/Cryptography/MissingSubjectId.php +++ /dev/null @@ -1,15 +0,0 @@ - $data - * - * @return array - */ - public function encrypt(string $class, array $data): array; - - /** - * @param class-string $class - * @param array $data - * - * @return array - */ - public function decrypt(string $class, array $data): array; -} diff --git a/src/Cryptography/SnapshotPayloadCryptographer.php b/src/Cryptography/SnapshotPayloadCryptographer.php deleted file mode 100644 index 61a6a140..00000000 --- a/src/Cryptography/SnapshotPayloadCryptographer.php +++ /dev/null @@ -1,157 +0,0 @@ - $data - * - * @return array - */ - public function encrypt(string $class, array $data): array - { - if (!is_a($class, AggregateRoot::class, true)) { - throw UnsupportedClass::fromClass($class); - } - - $subjectId = $this->subjectId($class, $data); - - if ($subjectId === null) { - return $data; - } - - try { - $cipherKey = $this->cipherKeyStore->get($subjectId); - } catch (CipherKeyNotExists) { - $cipherKey = ($this->cipherKeyFactory)(); - $this->cipherKeyStore->store($subjectId, $cipherKey); - } - - $metadata = $this->metadataFactory->metadata($class); - - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if (!$propertyMetadata->isPersonalData) { - continue; - } - - $data[$propertyMetadata->fieldName] = $this->cipher->encrypt( - $cipherKey, - $data[$propertyMetadata->fieldName], - ); - } - - return $data; - } - - /** - * @param class-string $class - * @param array $data - * - * @return array - */ - public function decrypt(string $class, array $data): array - { - if (!is_a($class, AggregateRoot::class, true)) { - throw UnsupportedClass::fromClass($class); - } - - $subjectId = $this->subjectId($class, $data); - - if ($subjectId === null) { - return $data; - } - - try { - $cipherKey = $this->cipherKeyStore->get($subjectId); - } catch (CipherKeyNotExists) { - $cipherKey = null; - } - - $metadata = $this->metadataFactory->metadata($class); - - foreach ($metadata->propertyMetadata as $propertyMetadata) { - if (!$propertyMetadata->isPersonalData) { - continue; - } - - if (!$cipherKey) { - $data[$propertyMetadata->fieldName] = $propertyMetadata->personalDataFallback; - continue; - } - - try { - $data[$propertyMetadata->fieldName] = $this->cipher->decrypt( - $cipherKey, - $data[$propertyMetadata->fieldName], - ); - } catch (DecryptionFailed) { - $data[$propertyMetadata->fieldName] = $propertyMetadata->personalDataFallback; - } - } - - return $data; - } - - /** - * @param class-string $class - * @param array $data - */ - private function subjectId(string $class, array $data): string|null - { - $metadata = $this->metadataFactory->metadata($class); - - if ($metadata->dataSubjectIdField === null) { - return null; - } - - if (!array_key_exists($metadata->dataSubjectIdField, $data)) { - throw new MissingSubjectId(); - } - - $subjectId = $data[$metadata->dataSubjectIdField]; - - if (!is_string($subjectId)) { - throw new UnsupportedSubjectId($subjectId); - } - - return $subjectId; - } - - public static function createWithOpenssl( - AggregateRootMetadataFactory $metadataFactory, - CipherKeyStore $cryptoStore, - ): static { - return new self( - $metadataFactory, - $cryptoStore, - new OpensslCipherKeyFactory(), - new OpensslCipher(), - ); - } -} diff --git a/src/Cryptography/Store/CipherKeyNotExists.php b/src/Cryptography/Store/CipherKeyNotExists.php deleted file mode 100644 index 79995f01..00000000 --- a/src/Cryptography/Store/CipherKeyNotExists.php +++ /dev/null @@ -1,17 +0,0 @@ - */ - private array $keys = []; - - public function get(string $id): CipherKey - { - return $this->keys[$id] ?? throw new CipherKeyNotExists($id); - } - - public function store(string $id, CipherKey $key): void - { - $this->keys[$id] = $key; - } - - public function remove(string $id): void - { - unset($this->keys[$id]); - } - - public function clear(): void - { - $this->keys = []; - } -} diff --git a/src/Cryptography/UnsupportedClass.php b/src/Cryptography/UnsupportedClass.php deleted file mode 100644 index 0b30bee6..00000000 --- a/src/Cryptography/UnsupportedClass.php +++ /dev/null @@ -1,17 +0,0 @@ - */ - public readonly array $propertyMetadata = [], ) { } } diff --git a/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php b/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php index 91a5942e..7dff4ce1 100644 --- a/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php +++ b/src/Metadata/AggregateRoot/AttributeAggregateRootMetadataFactory.php @@ -7,17 +7,13 @@ use Patchlevel\EventSourcing\Aggregate\AggregateRoot; use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Id; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Attribute\Snapshot as AttributeSnapshot; use Patchlevel\EventSourcing\Attribute\SuppressMissingApply; -use Patchlevel\Hydrator\Attribute\NormalizedName; use ReflectionClass; use ReflectionIntersectionType; use ReflectionMethod; use ReflectionNamedType; -use ReflectionProperty; use ReflectionUnionType; use function array_key_exists; @@ -51,29 +47,6 @@ public function metadata(string $aggregate): AggregateRootMetadata $applyMethods = $this->findApplyMethods($reflectionClass, $aggregate); $snapshot = $this->findSnapshot($reflectionClass); - $propertyMetadataList = []; - $hasPersonalData = false; - - $subjectId = $this->subjectIdField($reflectionClass); - - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - $propertyMetadata = $this->propertyMetadata($reflectionProperty); - - if ($propertyMetadata->isPersonalData) { - if ($subjectId === $propertyMetadata->fieldName) { - throw new SubjectIdAndPersonalDataConflict($aggregate, $propertyMetadata->fieldName); - } - - $hasPersonalData = true; - } - - $propertyMetadataList[$reflectionProperty->getName()] = $propertyMetadata; - } - - if ($hasPersonalData && $subjectId === null) { - throw new MissingDataSubjectId($aggregate); - } - $metadata = new AggregateRootMetadata( $aggregate, $aggregateName, @@ -82,8 +55,6 @@ public function metadata(string $aggregate): AggregateRootMetadata $suppressEvents, $suppressAll, $snapshot, - $subjectId, - $propertyMetadataList, ); $this->aggregateMetadata[$aggregate] = $metadata; @@ -263,63 +234,4 @@ static function (ReflectionNamedType|ReflectionIntersectionType $reflectionType) return []; } - - private function propertyMetadata(ReflectionProperty $reflectionProperty): PropertyMetadata - { - $attributeReflectionList = $reflectionProperty->getAttributes(PersonalData::class); - - if (!$attributeReflectionList) { - return new PropertyMetadata( - $reflectionProperty->getName(), - $this->fieldName($reflectionProperty), - ); - } - - $attribute = $attributeReflectionList[0]->newInstance(); - - return new PropertyMetadata( - $reflectionProperty->getName(), - $this->fieldName($reflectionProperty), - true, - $attribute->fallback, - ); - } - - private function subjectIdField(ReflectionClass $reflectionClass): string|null - { - $property = null; - - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - $attributeReflectionList = $reflectionProperty->getAttributes(DataSubjectId::class); - - if (!$attributeReflectionList) { - continue; - } - - if ($property !== null) { - throw new MultipleDataSubjectId($property->getName(), $reflectionProperty->getName()); - } - - $property = $reflectionProperty; - } - - if ($property === null) { - return null; - } - - return $this->fieldName($property); - } - - private function fieldName(ReflectionProperty $reflectionProperty): string - { - $attributeReflectionList = $reflectionProperty->getAttributes(NormalizedName::class); - - if (!$attributeReflectionList) { - return $reflectionProperty->getName(); - } - - $attribute = $attributeReflectionList[0]->newInstance(); - - return $attribute->name(); - } } diff --git a/src/Metadata/AggregateRoot/PropertyMetadata.php b/src/Metadata/AggregateRoot/PropertyMetadata.php deleted file mode 100644 index 9986c171..00000000 --- a/src/Metadata/AggregateRoot/PropertyMetadata.php +++ /dev/null @@ -1,16 +0,0 @@ -subjectIdField($reflectionClass); - - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - $propertyMetadata = $this->propertyMetadata($reflectionProperty); - - if ($propertyMetadata->isPersonalData) { - if ($subjectId === $propertyMetadata->fieldName) { - throw new SubjectIdAndPersonalDataConflict($event, $propertyMetadata->fieldName); - } - - $hasPersonalData = true; - } - - $propertyMetadataList[$reflectionProperty->getName()] = $propertyMetadata; - } - - if ($hasPersonalData && $subjectId === null) { - throw new MissingDataSubjectId($event); - } - $eventAttribute = $attributeReflectionList[0]->newInstance(); $this->eventMetadata[$event] = new EventMetadata( $eventAttribute->name, $this->splitStream($reflectionClass), - $subjectId, - $propertyMetadataList, ); return $this->eventMetadata[$event]; @@ -74,63 +45,4 @@ private function splitStream(ReflectionClass $reflectionClass): bool { return count($reflectionClass->getAttributes(SplitStream::class)) !== 0; } - - private function propertyMetadata(ReflectionProperty $reflectionProperty): PropertyMetadata - { - $attributeReflectionList = $reflectionProperty->getAttributes(PersonalData::class); - - if (!$attributeReflectionList) { - return new PropertyMetadata( - $reflectionProperty->getName(), - $this->fieldName($reflectionProperty), - ); - } - - $attribute = $attributeReflectionList[0]->newInstance(); - - return new PropertyMetadata( - $reflectionProperty->getName(), - $this->fieldName($reflectionProperty), - true, - $attribute->fallback, - ); - } - - private function subjectIdField(ReflectionClass $reflectionClass): string|null - { - $property = null; - - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - $attributeReflectionList = $reflectionProperty->getAttributes(DataSubjectId::class); - - if (!$attributeReflectionList) { - continue; - } - - if ($property !== null) { - throw new MultipleDataSubjectId($property->getName(), $reflectionProperty->getName()); - } - - $property = $reflectionProperty; - } - - if ($property === null) { - return null; - } - - return $this->fieldName($property); - } - - private function fieldName(ReflectionProperty $reflectionProperty): string - { - $attributeReflectionList = $reflectionProperty->getAttributes(NormalizedName::class); - - if (!$attributeReflectionList) { - return $reflectionProperty->getName(); - } - - $attribute = $attributeReflectionList[0]->newInstance(); - - return $attribute->name(); - } } diff --git a/src/Metadata/Event/EventMetadata.php b/src/Metadata/Event/EventMetadata.php index 4c2018b5..e5064109 100644 --- a/src/Metadata/Event/EventMetadata.php +++ b/src/Metadata/Event/EventMetadata.php @@ -9,9 +9,6 @@ final class EventMetadata public function __construct( public readonly string $name, public readonly bool $splitStream = false, - public readonly string|null $dataSubjectIdField = null, - /** @var array */ - public readonly array $propertyMetadata = [], ) { } } diff --git a/src/Metadata/Event/PropertyMetadata.php b/src/Metadata/Event/PropertyMetadata.php deleted file mode 100644 index aefd10ae..00000000 --- a/src/Metadata/Event/PropertyMetadata.php +++ /dev/null @@ -1,16 +0,0 @@ -create($paths), - $hydrator, + new MetadataHydrator(cryptographer: $cryptographer), new JsonEncoder(), $upcaster, ); diff --git a/src/Snapshot/DefaultSnapshotStore.php b/src/Snapshot/DefaultSnapshotStore.php index f2650407..6a071630 100644 --- a/src/Snapshot/DefaultSnapshotStore.php +++ b/src/Snapshot/DefaultSnapshotStore.php @@ -6,11 +6,10 @@ use Patchlevel\EventSourcing\Aggregate\AggregateRoot; use Patchlevel\EventSourcing\Aggregate\AggregateRootId; -use Patchlevel\EventSourcing\Cryptography\CryptographicHydrator; -use Patchlevel\EventSourcing\Cryptography\PayloadCryptographer; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootMetadataAwareMetadataFactory; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootMetadataFactory; use Patchlevel\EventSourcing\Snapshot\Adapter\SnapshotAdapter; +use Patchlevel\Hydrator\Cryptography\PayloadCryptographer; use Patchlevel\Hydrator\Hydrator; use Patchlevel\Hydrator\MetadataHydrator; use Throwable; @@ -116,12 +115,9 @@ private function version(string $aggregateClass): string|null /** @param array $snapshotAdapters */ public static function createDefault(array $snapshotAdapters, PayloadCryptographer|null $cryptographer = null): self { - $hydrator = new MetadataHydrator(); - - if ($cryptographer) { - $hydrator = new CryptographicHydrator($hydrator, $cryptographer); - } - - return new self($snapshotAdapters, $hydrator); + return new self( + $snapshotAdapters, + new MetadataHydrator(cryptographer: $cryptographer), + ); } } diff --git a/tests/Benchmark/BasicImplementation/Events/EmailChanged.php b/tests/Benchmark/BasicImplementation/Events/EmailChanged.php index d0f642cf..3177185d 100644 --- a/tests/Benchmark/BasicImplementation/Events/EmailChanged.php +++ b/tests/Benchmark/BasicImplementation/Events/EmailChanged.php @@ -4,11 +4,11 @@ namespace Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Event; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId; +use Patchlevel\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\PersonalData; #[Event('profile.email_changed')] final class EmailChanged diff --git a/tests/Benchmark/BasicImplementation/Events/ProfileCreated.php b/tests/Benchmark/BasicImplementation/Events/ProfileCreated.php index 4b627274..bd47f28d 100644 --- a/tests/Benchmark/BasicImplementation/Events/ProfileCreated.php +++ b/tests/Benchmark/BasicImplementation/Events/ProfileCreated.php @@ -4,11 +4,11 @@ namespace Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Event; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId; +use Patchlevel\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\PersonalData; #[Event('profile.created')] final class ProfileCreated diff --git a/tests/Benchmark/PersonalDataBench.php b/tests/Benchmark/PersonalDataBench.php index 4325e8a2..940e44fe 100644 --- a/tests/Benchmark/PersonalDataBench.php +++ b/tests/Benchmark/PersonalDataBench.php @@ -5,10 +5,8 @@ namespace Patchlevel\EventSourcing\Tests\Benchmark; use Patchlevel\EventSourcing\Aggregate\AggregateRootId; -use Patchlevel\EventSourcing\Cryptography\EventPayloadCryptographer; -use Patchlevel\EventSourcing\Cryptography\Store\DoctrineCipherKeyStore; +use Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore; use Patchlevel\EventSourcing\Message\Serializer\DefaultHeadersSerializer; -use Patchlevel\EventSourcing\Metadata\Event\AttributeEventMetadataFactory; use Patchlevel\EventSourcing\Repository\DefaultRepository; use Patchlevel\EventSourcing\Repository\Repository; use Patchlevel\EventSourcing\Schema\ChainDoctrineSchemaConfigurator; @@ -19,6 +17,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 PhpBench\Attributes as Bench; #[Bench\BeforeMethods('setUp')] @@ -36,8 +35,7 @@ public function setUp(): void $cipherKeyStore = new DoctrineCipherKeyStore($connection); - $cryptographer = EventPayloadCryptographer::createWithOpenssl( - new AttributeEventMetadataFactory(), + $cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl( $cipherKeyStore, ); diff --git a/tests/Integration/PersonalData/Events/NameChanged.php b/tests/Integration/PersonalData/Events/NameChanged.php index 74151541..c758aa2d 100644 --- a/tests/Integration/PersonalData/Events/NameChanged.php +++ b/tests/Integration/PersonalData/Events/NameChanged.php @@ -4,11 +4,11 @@ namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Event; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId; +use Patchlevel\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\PersonalData; #[Event('profile.name_changed')] final class NameChanged diff --git a/tests/Integration/PersonalData/Events/ProfileCreated.php b/tests/Integration/PersonalData/Events/ProfileCreated.php index 1fe47528..a9a6c59f 100644 --- a/tests/Integration/PersonalData/Events/ProfileCreated.php +++ b/tests/Integration/PersonalData/Events/ProfileCreated.php @@ -4,11 +4,11 @@ namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Event; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\ProfileId; +use Patchlevel\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\PersonalData; #[Event('profile.created')] final class ProfileCreated diff --git a/tests/Integration/PersonalData/PersonalDataTest.php b/tests/Integration/PersonalData/PersonalDataTest.php index 5a1d7086..567c8cfd 100644 --- a/tests/Integration/PersonalData/PersonalDataTest.php +++ b/tests/Integration/PersonalData/PersonalDataTest.php @@ -5,13 +5,9 @@ namespace Patchlevel\EventSourcing\Tests\Integration\PersonalData; use Doctrine\DBAL\Connection; -use Patchlevel\EventSourcing\Cryptography\EventPayloadCryptographer; -use Patchlevel\EventSourcing\Cryptography\SnapshotPayloadCryptographer; -use Patchlevel\EventSourcing\Cryptography\Store\DoctrineCipherKeyStore; +use Patchlevel\EventSourcing\Cryptography\DoctrineCipherKeyStore; use Patchlevel\EventSourcing\Message\Serializer\DefaultHeadersSerializer; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AggregateRootRegistry; -use Patchlevel\EventSourcing\Metadata\AggregateRoot\AttributeAggregateRootMetadataFactory; -use Patchlevel\EventSourcing\Metadata\Event\AttributeEventMetadataFactory; use Patchlevel\EventSourcing\Repository\DefaultRepositoryManager; use Patchlevel\EventSourcing\Schema\ChainDoctrineSchemaConfigurator; use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector; @@ -24,6 +20,7 @@ use Patchlevel\EventSourcing\Subscription\Subscriber\MetadataSubscriberAccessorRepository; use Patchlevel\EventSourcing\Tests\DbalManager; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Processor\DeletePersonalDataProcessor; +use Patchlevel\Hydrator\Cryptography\PersonalDataPayloadCryptographer; use PHPUnit\Framework\TestCase; /** @coversNothing */ @@ -44,11 +41,7 @@ public function tearDown(): void public function testSuccessfulWithEvent(): void { $cipherKeyStore = new DoctrineCipherKeyStore($this->connection); - - $cryptographer = EventPayloadCryptographer::createWithOpenssl( - new AttributeEventMetadataFactory(), - $cipherKeyStore, - ); + $cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl($cipherKeyStore); $store = new DoctrineDbalStore( $this->connection, @@ -102,11 +95,7 @@ public function testSuccessfulWithEvent(): void public function testRemoveKeyWithEvent(): void { $cipherKeyStore = new DoctrineCipherKeyStore($this->connection); - - $cryptographer = EventPayloadCryptographer::createWithOpenssl( - new AttributeEventMetadataFactory(), - $cipherKeyStore, - ); + $cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl($cipherKeyStore); $subscriptionStore = new DoctrineSubscriptionStore( $this->connection, @@ -186,16 +175,7 @@ public function testRemoveKeyWithEvent(): void public function testRemoveKeyWithEventAndSnapshot(): void { $cipherKeyStore = new DoctrineCipherKeyStore($this->connection); - - $cryptographer = EventPayloadCryptographer::createWithOpenssl( - new AttributeEventMetadataFactory(), - $cipherKeyStore, - ); - - $snapShotCryptographer = SnapshotPayloadCryptographer::createWithOpenssl( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore, - ); + $cryptographer = PersonalDataPayloadCryptographer::createWithOpenssl($cipherKeyStore); $subscriptionStore = new DoctrineSubscriptionStore( $this->connection, @@ -219,7 +199,7 @@ public function testRemoveKeyWithEventAndSnapshot(): void null, DefaultSnapshotStore::createDefault( ['default' => $snapshotAdapter], - $snapShotCryptographer, + $cryptographer, ), ); diff --git a/tests/Integration/PersonalData/Processor/DeletePersonalDataProcessor.php b/tests/Integration/PersonalData/Processor/DeletePersonalDataProcessor.php index 6b661a5c..bdaa3467 100644 --- a/tests/Integration/PersonalData/Processor/DeletePersonalDataProcessor.php +++ b/tests/Integration/PersonalData/Processor/DeletePersonalDataProcessor.php @@ -7,9 +7,9 @@ use Patchlevel\EventSourcing\Aggregate\AggregateHeader; use Patchlevel\EventSourcing\Attribute\Processor; use Patchlevel\EventSourcing\Attribute\Subscribe; -use Patchlevel\EventSourcing\Cryptography\Store\CipherKeyStore; use Patchlevel\EventSourcing\Message\Message; use Patchlevel\EventSourcing\Tests\Integration\PersonalData\Events\PersonalDataRemoved; +use Patchlevel\Hydrator\Cryptography\Store\CipherKeyStore; #[Processor('delete_personal_data')] final class DeletePersonalDataProcessor diff --git a/tests/Integration/PersonalData/Profile.php b/tests/Integration/PersonalData/Profile.php index 84dd0fc6..a750c06f 100644 --- a/tests/Integration/PersonalData/Profile.php +++ b/tests/Integration/PersonalData/Profile.php @@ -7,14 +7,14 @@ use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot; use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Id; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Attribute\Snapshot; use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer; 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\Hydrator\Attribute\DataSubjectId; +use Patchlevel\Hydrator\Attribute\PersonalData; #[Aggregate('profile')] #[Snapshot('default', 2)] diff --git a/tests/Unit/Cryptography/Cipher/CreateCipherKeyFailedTest.php b/tests/Unit/Cryptography/Cipher/CreateCipherKeyFailedTest.php deleted file mode 100644 index 8096db34..00000000 --- a/tests/Unit/Cryptography/Cipher/CreateCipherKeyFailedTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMessage()); - } -} diff --git a/tests/Unit/Cryptography/Cipher/DecryptionFailedTest.php b/tests/Unit/Cryptography/Cipher/DecryptionFailedTest.php deleted file mode 100644 index a3af5c79..00000000 --- a/tests/Unit/Cryptography/Cipher/DecryptionFailedTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMessage()); - } -} diff --git a/tests/Unit/Cryptography/Cipher/EncryptionFailedTest.php b/tests/Unit/Cryptography/Cipher/EncryptionFailedTest.php deleted file mode 100644 index 6dbd10ab..00000000 --- a/tests/Unit/Cryptography/Cipher/EncryptionFailedTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMessage()); - } -} diff --git a/tests/Unit/Cryptography/Cipher/OpensslCipherKeyFactoryTest.php b/tests/Unit/Cryptography/Cipher/OpensslCipherKeyFactoryTest.php deleted file mode 100644 index 449c9374..00000000 --- a/tests/Unit/Cryptography/Cipher/OpensslCipherKeyFactoryTest.php +++ /dev/null @@ -1,33 +0,0 @@ -assertSame(16, strlen($cipherKey->key)); - $this->assertSame('aes128', $cipherKey->method); - $this->assertSame(16, strlen($cipherKey->iv)); - } - - public function testMethodNotSupported(): void - { - $this->expectException(MethodNotSupported::class); - - $cipherKeyFactory = new OpensslCipherKeyFactory(method: 'foo'); - $cipherKeyFactory(); - } -} diff --git a/tests/Unit/Cryptography/Cipher/OpensslCipherTest.php b/tests/Unit/Cryptography/Cipher/OpensslCipherTest.php deleted file mode 100644 index ca3346cf..00000000 --- a/tests/Unit/Cryptography/Cipher/OpensslCipherTest.php +++ /dev/null @@ -1,78 +0,0 @@ -encrypt($this->createKey(), $value); - - self::assertEquals($encryptedString, $return); - } - - public function testEncryptFailed(): void - { - $this->expectException(EncryptionFailed::class); - - $cipher = new OpensslCipher(); - $cipher->encrypt(new CipherKey( - 'key', - 'bar', - 'abcdefg123456789', - ), ''); - } - - #[DataProvider('dataProvider')] - public function testDecrypt(mixed $value, string $encryptedString): void - { - $cipher = new OpensslCipher(); - $return = $cipher->decrypt($this->createKey(), $encryptedString); - - self::assertEquals($value, $return); - } - - public function testDecryptFailed(): void - { - $this->expectException(DecryptionFailed::class); - - $cipher = new OpensslCipher(); - $cipher->decrypt($this->createKey('foo'), 'emNpWDlMWFBnRStpZk9YZktrUStRQT09'); - } - - public static function dataProvider(): Generator - { - yield 'empty' => ['', 'emNpWDlMWFBnRStpZk9YZktrUStRQT09']; - yield 'string' => ['foo bar baz', 'YUlYRnJZMEd1RkFycjNrQitETHhqQT09']; - yield 'integer' => [42, 'M1FHSnlnbWNlZFJiV2xwdzZIZUhDdz09']; - yield 'float' => [0.5, 'N2tOWGNia3lrdUJ1ancrMFA4OEY0Zz09']; - yield 'null' => [null, 'OUE1T081cXdpNmFMc1FIMGsrME5vdz09']; - yield 'true' => [true, 'NCtWMDE4WnV5NEtCamVVdkIxZjRrdz09']; - yield 'false' => [false, 'czh5NUYxWXhQOWhSbGVwWG5ETFdVQT09']; - yield 'array' => [['foo' => 'bar'], 'cHo2QlhxSnNFZG1kUEhRZ3pjcFJrUT09']; - yield 'long text' => ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', 'eDNCalYzSS9LbkZIcGdKNWVmUFQwTTI0YXhhSnNmdUxXeXhGUGFwMWZkTmx1ZnNwNzBUa29NcUFxUzRFV3V2WWNlUmt6YWhTSlRzVXpqd3RLZkpzUWFWYVRCR1pvbkt3TUE4UzZmaDVQcTYzMzJoWVBRRzllbHhhNjYrenNWbzFDZ2lnVm1PRFhvamozZEVmcXFYVTZGQ1dIWEgzcE1mU2w2SWlRQ2o2WFdNPQ==']; - } - - /** @param non-empty-string $key */ - private function createKey(string $key = 'key'): CipherKey - { - return new CipherKey( - $key, - 'aes128', - 'abcdefg123456789', - ); - } -} diff --git a/tests/Unit/Cryptography/CryptographicHydratorTest.php b/tests/Unit/Cryptography/CryptographicHydratorTest.php deleted file mode 100644 index 51ea7b96..00000000 --- a/tests/Unit/Cryptography/CryptographicHydratorTest.php +++ /dev/null @@ -1,74 +0,0 @@ - 'bar']; - $encryptedPayload = ['foo' => 'encrypted']; - - $parentHydrator = $this->prophesize(Hydrator::class); - $parentHydrator - ->hydrate(stdClass::class, $payload) - ->willReturn($object) - ->shouldBeCalledOnce(); - - $cryptographer = $this->prophesize(PayloadCryptographer::class); - $cryptographer - ->decrypt(stdClass::class, $encryptedPayload) - ->willReturn($payload) - ->shouldBeCalledOnce(); - - $hydrator = new CryptographicHydrator( - $parentHydrator->reveal(), - $cryptographer->reveal(), - ); - - $return = $hydrator->hydrate(stdClass::class, $encryptedPayload); - - self::assertSame($object, $return); - } - - public function testExtract(): void - { - $object = new stdClass(); - $payload = ['foo' => 'bar']; - $encryptedPayload = ['foo' => 'encrypted']; - - $parentHydrator = $this->prophesize(Hydrator::class); - $parentHydrator - ->extract($object) - ->willReturn($payload) - ->shouldBeCalledOnce(); - - $cryptographer = $this->prophesize(PayloadCryptographer::class); - $cryptographer - ->encrypt(stdClass::class, $payload) - ->willReturn($encryptedPayload) - ->shouldBeCalledOnce(); - - $hydrator = new CryptographicHydrator( - $parentHydrator->reveal(), - $cryptographer->reveal(), - ); - - $return = $hydrator->extract($object); - - self::assertSame($encryptedPayload, $return); - } -} diff --git a/tests/Unit/Cryptography/EventPayloadCryptographerTest.php b/tests/Unit/Cryptography/EventPayloadCryptographerTest.php deleted file mode 100644 index 6e3a947d..00000000 --- a/tests/Unit/Cryptography/EventPayloadCryptographerTest.php +++ /dev/null @@ -1,274 +0,0 @@ -prophesize(CipherKeyStore::class); - $cipherKeyStore->get(Argument::any())->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $payload = ['id' => 'foo', 'email' => 'info@patchlevel.de']; - - $result = $cryptographer->encrypt(ProfileVisited::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertSame($payload, $result); - } - - public function testEncryptWithMissingKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willThrow(new CipherKeyNotExists('foo')); - $cipherKeyStore->store('foo', $cipherKey)->shouldBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->willReturn($cipherKey)->shouldBeCalledOnce(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->encrypt($cipherKey, 'info@patchlevel.de') - ->willReturn('encrypted') - ->shouldBeCalledOnce(); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->encrypt(EmailChanged::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertEquals(['id' => 'foo', 'email' => 'encrypted'], $result); - } - - public function testEncryptWithExistingKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willReturn($cipherKey); - $cipherKeyStore->store('foo', Argument::type(CipherKey::class))->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->encrypt($cipherKey, 'info@patchlevel.de') - ->willReturn('encrypted') - ->shouldBeCalledOnce(); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->encrypt(EmailChanged::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertEquals(['id' => 'foo', 'email' => 'encrypted'], $result); - } - - public function testSkipDecrypt(): void - { - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get(Argument::any())->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $payload = ['id' => 'foo', 'email' => 'info@patchlevel.de']; - - $result = $cryptographer->decrypt(ProfileVisited::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertSame($payload, $result); - } - - public function testDecryptWithMissingKey(): void - { - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willThrow(new CipherKeyNotExists('foo')); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher->decrypt()->shouldNotBeCalled(); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->decrypt(EmailChanged::class, ['id' => 'foo', 'email' => 'encrypted']); - - self::assertEquals(['id' => 'foo', 'email' => 'fallback'], $result); - } - - public function testDecryptWithInvalidKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willReturn($cipherKey); - $cipherKeyStore->store('foo', Argument::type(CipherKey::class))->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->decrypt($cipherKey, 'encrypted') - ->willThrow(new DecryptionFailed()) - ->shouldBeCalledOnce(); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->decrypt(EmailChanged::class, ['id' => 'foo', 'email' => 'encrypted']); - - self::assertEquals(['id' => 'foo', 'email' => 'fallback'], $result); - } - - public function testDecryptWithExistingKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willReturn($cipherKey); - $cipherKeyStore->store('foo', Argument::type(CipherKey::class))->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->decrypt($cipherKey, 'encrypted') - ->willReturn('info@patchlevel.de') - ->shouldBeCalledOnce(); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->decrypt(EmailChanged::class, ['id' => 'foo', 'email' => 'encrypted']); - - self::assertEquals(['id' => 'foo', 'email' => 'info@patchlevel.de'], $result); - } - - public function testUnsupportedSubjectId(): void - { - $this->expectException(UnsupportedSubjectId::class); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $cryptographer->decrypt(EmailChanged::class, ['id' => null, 'email' => 'encrypted']); - } - - public function testMissingSubjectId(): void - { - $this->expectException(MissingSubjectId::class); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new EventPayloadCryptographer( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $cryptographer->decrypt(EmailChanged::class, ['email' => 'encrypted']); - } - - public function testCreateWithOpenssl(): void - { - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - - $cryptographer = EventPayloadCryptographer::createWithOpenssl( - new AttributeEventMetadataFactory(), - $cipherKeyStore->reveal(), - ); - - self::assertInstanceOf(EventPayloadCryptographer::class, $cryptographer); - } -} diff --git a/tests/Unit/Cryptography/MissingSubjectIdTest.php b/tests/Unit/Cryptography/MissingSubjectIdTest.php deleted file mode 100644 index 90b6c0b6..00000000 --- a/tests/Unit/Cryptography/MissingSubjectIdTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMessage()); - } -} diff --git a/tests/Unit/Cryptography/SnapshotPayloadCryptographerTest.php b/tests/Unit/Cryptography/SnapshotPayloadCryptographerTest.php deleted file mode 100644 index cef4274a..00000000 --- a/tests/Unit/Cryptography/SnapshotPayloadCryptographerTest.php +++ /dev/null @@ -1,274 +0,0 @@ -prophesize(CipherKeyStore::class); - $cipherKeyStore->get(Argument::any())->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $payload = ['id' => 'foo', 'email' => 'info@patchlevel.de']; - - $result = $cryptographer->encrypt(Profile::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertSame($payload, $result); - } - - public function testEncryptWithMissingKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willThrow(new CipherKeyNotExists('foo')); - $cipherKeyStore->store('foo', $cipherKey)->shouldBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->willReturn($cipherKey)->shouldBeCalledOnce(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->encrypt($cipherKey, 'info@patchlevel.de') - ->willReturn('encrypted') - ->shouldBeCalledOnce(); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->encrypt(ProfileWithSnapshot::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertEquals(['id' => 'foo', 'email' => 'encrypted'], $result); - } - - public function testEncryptWithExistingKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willReturn($cipherKey); - $cipherKeyStore->store('foo', Argument::type(CipherKey::class))->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->encrypt($cipherKey, 'info@patchlevel.de') - ->willReturn('encrypted') - ->shouldBeCalledOnce(); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->encrypt(ProfileWithSnapshot::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertEquals(['id' => 'foo', 'email' => 'encrypted'], $result); - } - - public function testSkipDecrypt(): void - { - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get(Argument::any())->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $payload = ['id' => 'foo', 'email' => 'info@patchlevel.de']; - - $result = $cryptographer->decrypt(Profile::class, ['id' => 'foo', 'email' => 'info@patchlevel.de']); - - self::assertSame($payload, $result); - } - - public function testDecryptWithMissingKey(): void - { - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willThrow(new CipherKeyNotExists('foo')); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher->decrypt()->shouldNotBeCalled(); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->decrypt(ProfileWithSnapshot::class, ['id' => 'foo', 'email' => 'encrypted']); - - self::assertEquals(['id' => 'foo', 'email' => 'fallback'], $result); - } - - public function testDecryptWithInvalidKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willReturn($cipherKey); - $cipherKeyStore->store('foo', Argument::type(CipherKey::class))->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->decrypt($cipherKey, 'encrypted') - ->willThrow(new DecryptionFailed()) - ->shouldBeCalledOnce(); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->decrypt(ProfileWithSnapshot::class, ['id' => 'foo', 'email' => 'encrypted']); - - self::assertEquals(['id' => 'foo', 'email' => 'fallback'], $result); - } - - public function testDecryptWithExistingKey(): void - { - $cipherKey = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyStore->get('foo')->willReturn($cipherKey); - $cipherKeyStore->store('foo', Argument::type(CipherKey::class))->shouldNotBeCalled(); - - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipherKeyFactory->__invoke()->shouldNotBeCalled(); - - $cipher = $this->prophesize(Cipher::class); - $cipher - ->decrypt($cipherKey, 'encrypted') - ->willReturn('info@patchlevel.de') - ->shouldBeCalledOnce(); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $result = $cryptographer->decrypt(ProfileWithSnapshot::class, ['id' => 'foo', 'email' => 'encrypted']); - - self::assertEquals(['id' => 'foo', 'email' => 'info@patchlevel.de'], $result); - } - - public function testUnsupportedSubjectId(): void - { - $this->expectException(UnsupportedSubjectId::class); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $cryptographer->decrypt(ProfileWithSnapshot::class, ['id' => null, 'email' => 'encrypted']); - } - - public function testMissingSubjectId(): void - { - $this->expectException(MissingSubjectId::class); - - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - $cipherKeyFactory = $this->prophesize(CipherKeyFactory::class); - $cipher = $this->prophesize(Cipher::class); - - $cryptographer = new SnapshotPayloadCryptographer( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - $cipherKeyFactory->reveal(), - $cipher->reveal(), - ); - - $cryptographer->decrypt(ProfileWithSnapshot::class, ['email' => 'encrypted']); - } - - public function testCreateWithOpenssl(): void - { - $cipherKeyStore = $this->prophesize(CipherKeyStore::class); - - $cryptographer = SnapshotPayloadCryptographer::createWithOpenssl( - new AttributeAggregateRootMetadataFactory(), - $cipherKeyStore->reveal(), - ); - - self::assertInstanceOf(SnapshotPayloadCryptographer::class, $cryptographer); - } -} diff --git a/tests/Unit/Cryptography/Store/CipherKeyNotExistsTest.php b/tests/Unit/Cryptography/Store/CipherKeyNotExistsTest.php deleted file mode 100644 index c7c1d112..00000000 --- a/tests/Unit/Cryptography/Store/CipherKeyNotExistsTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMessage()); - } -} diff --git a/tests/Unit/Cryptography/Store/InMemoryCipherKeyStoreTest.php b/tests/Unit/Cryptography/Store/InMemoryCipherKeyStoreTest.php deleted file mode 100644 index d4ee6ec2..00000000 --- a/tests/Unit/Cryptography/Store/InMemoryCipherKeyStoreTest.php +++ /dev/null @@ -1,76 +0,0 @@ -store('foo', $key); - - self::assertSame($key, $store->get('foo')); - } - - public function testLoadFailed(): void - { - $this->expectException(CipherKeyNotExists::class); - - $store = new InMemoryCipherKeyStore(); - $store->get('foo'); - } - - public function testRemove(): void - { - $key = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $store = new InMemoryCipherKeyStore(); - $store->store('foo', $key); - - self::assertSame($key, $store->get('foo')); - - $store->remove('foo'); - - $this->expectException(CipherKeyNotExists::class); - - $store->get('foo'); - } - - public function testClear(): void - { - $key = new CipherKey( - 'foo', - 'bar', - 'baz', - ); - - $store = new InMemoryCipherKeyStore(); - $store->store('foo', $key); - - self::assertSame($key, $store->get('foo')); - - $store->clear(); - - $this->expectException(CipherKeyNotExists::class); - - $store->get('foo'); - } -} diff --git a/tests/Unit/Cryptography/UnsupportedSubjectIdTest.php b/tests/Unit/Cryptography/UnsupportedSubjectIdTest.php deleted file mode 100644 index 036660ec..00000000 --- a/tests/Unit/Cryptography/UnsupportedSubjectIdTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getMessage()); - } -} diff --git a/tests/Unit/Fixture/EmailChanged.php b/tests/Unit/Fixture/EmailChanged.php index 60bcbfc4..4434eadd 100644 --- a/tests/Unit/Fixture/EmailChanged.php +++ b/tests/Unit/Fixture/EmailChanged.php @@ -4,17 +4,13 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Fixture; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Event; -use Patchlevel\EventSourcing\Attribute\PersonalData; #[Event('email_changed')] final class EmailChanged { public function __construct( - #[DataSubjectId] public string $id, - #[PersonalData('fallback')] public string $email, ) { } diff --git a/tests/Unit/Fixture/ProfileWithSnapshot.php b/tests/Unit/Fixture/ProfileWithSnapshot.php index 5a4aa52f..d215c5f6 100644 --- a/tests/Unit/Fixture/ProfileWithSnapshot.php +++ b/tests/Unit/Fixture/ProfileWithSnapshot.php @@ -7,9 +7,7 @@ use Patchlevel\EventSourcing\Aggregate\BasicAggregateRoot; use Patchlevel\EventSourcing\Attribute\Aggregate; use Patchlevel\EventSourcing\Attribute\Apply; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Id; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Attribute\Snapshot; use Patchlevel\EventSourcing\Attribute\SuppressMissingApply; use Patchlevel\EventSourcing\Serializer\Normalizer\IdNormalizer; @@ -22,10 +20,8 @@ final class ProfileWithSnapshot extends BasicAggregateRoot { #[Id] #[IdNormalizer] - #[DataSubjectId] private ProfileId $id; #[EmailNormalizer] - #[PersonalData(fallback: 'fallback')] private Email $email; /** @var array */ #[ArrayNormalizer(new MessageNormalizer())] diff --git a/tests/Unit/Metadata/Aggregate/AttributeAggregateMetadataFactoryTest.php b/tests/Unit/Metadata/Aggregate/AttributeAggregateMetadataFactoryTest.php index e4234602..7bf5f640 100644 --- a/tests/Unit/Metadata/Aggregate/AttributeAggregateMetadataFactoryTest.php +++ b/tests/Unit/Metadata/Aggregate/AttributeAggregateMetadataFactoryTest.php @@ -4,17 +4,10 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Metadata\Aggregate; -use Patchlevel\EventSourcing\Attribute\Aggregate; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; -use Patchlevel\EventSourcing\Attribute\Id; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Metadata\AggregateRoot\ArgumentTypeIsMissing; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AttributeAggregateRootMetadataFactory; use Patchlevel\EventSourcing\Metadata\AggregateRoot\DuplicateEmptyApplyAttribute; -use Patchlevel\EventSourcing\Metadata\AggregateRoot\MissingDataSubjectId; use Patchlevel\EventSourcing\Metadata\AggregateRoot\MixedApplyAttributeUsage; -use Patchlevel\EventSourcing\Metadata\AggregateRoot\MultipleDataSubjectId; -use Patchlevel\EventSourcing\Metadata\AggregateRoot\SubjectIdAndPersonalDataConflict; use Patchlevel\EventSourcing\Tests\Unit\Fixture\MessageDeleted; use Patchlevel\EventSourcing\Tests\Unit\Fixture\NameChanged; use Patchlevel\EventSourcing\Tests\Unit\Fixture\Profile; @@ -26,7 +19,6 @@ use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithBrokenApplyNoType; use Patchlevel\EventSourcing\Tests\Unit\Fixture\ProfileWithEmptyApply; use Patchlevel\EventSourcing\Tests\Unit\Fixture\SplittingEvent; -use Patchlevel\Hydrator\Attribute\NormalizedName; use PHPUnit\Framework\TestCase; /** @covers \Patchlevel\EventSourcing\Metadata\AggregateRoot\AttributeAggregateRootMetadataFactory */ @@ -101,95 +93,4 @@ public function testBrokenApplyWithBothUsages(): void $metadataFactory->metadata(ProfileWithBrokenApplyBothUsage::class); } - - public function testPersonalData(): void - { - $event = new #[Aggregate('profile')] - class ('id', 'name') { - public function __construct( - #[Id] - #[DataSubjectId] - #[NormalizedName('_id')] - public string $id, - #[PersonalData('fallback')] - #[NormalizedName('_name')] - public string $name, - ) { - } - }; - - $metadataFactory = new AttributeAggregateRootMetadataFactory(); - $metadata = $metadataFactory->metadata($event::class); - - self::assertSame('profile', $metadata->name); - self::assertSame('_id', $metadata->dataSubjectIdField); - self::assertCount(2, $metadata->propertyMetadata); - - self::assertSame('id', $metadata->propertyMetadata['id']->propertyName); - self::assertSame(false, $metadata->propertyMetadata['id']->isPersonalData); - self::assertSame('_id', $metadata->propertyMetadata['id']->fieldName); - self::assertSame(null, $metadata->propertyMetadata['id']->personalDataFallback); - - self::assertSame('name', $metadata->propertyMetadata['name']->propertyName); - self::assertSame(true, $metadata->propertyMetadata['name']->isPersonalData); - self::assertSame('_name', $metadata->propertyMetadata['name']->fieldName); - self::assertSame('fallback', $metadata->propertyMetadata['name']->personalDataFallback); - } - - public function testMissingDataSubjectId(): void - { - $event = new #[Aggregate('profile')] - class ('name') { - public function __construct( - #[Id] - #[PersonalData] - public string $name, - ) { - } - }; - - $this->expectException(MissingDataSubjectId::class); - - $metadataFactory = new AttributeAggregateRootMetadataFactory(); - $metadataFactory->metadata($event::class); - } - - public function testDataSubjectIdIsPersonalData(): void - { - $event = new #[Aggregate('profile')] - class ('name') { - public function __construct( - #[Id] - #[DataSubjectId] - #[PersonalData] - public string $name, - ) { - } - }; - - $this->expectException(SubjectIdAndPersonalDataConflict::class); - - $metadataFactory = new AttributeAggregateRootMetadataFactory(); - $metadataFactory->metadata($event::class); - } - - public function testMultipleDataSubjectId(): void - { - $aggregate = new #[Aggregate('profile')] - class ('id', 'name') { - public function __construct( - #[Id] - #[DataSubjectId] - public string $id, - #[DataSubjectId] - public string $name, - ) { - } - }; - - $this->expectException(MultipleDataSubjectId::class); - - $metadataFactory = new AttributeAggregateRootMetadataFactory(); - $metadataFactory->metadata($aggregate::class); - } } diff --git a/tests/Unit/Metadata/Event/AttributeEventMetadataFactoryTest.php b/tests/Unit/Metadata/Event/AttributeEventMetadataFactoryTest.php index 0a1949af..6087ec12 100644 --- a/tests/Unit/Metadata/Event/AttributeEventMetadataFactoryTest.php +++ b/tests/Unit/Metadata/Event/AttributeEventMetadataFactoryTest.php @@ -4,16 +4,10 @@ namespace Patchlevel\EventSourcing\Tests\Unit\Metadata\Event; -use Patchlevel\EventSourcing\Attribute\DataSubjectId; use Patchlevel\EventSourcing\Attribute\Event; -use Patchlevel\EventSourcing\Attribute\PersonalData; use Patchlevel\EventSourcing\Attribute\SplitStream; use Patchlevel\EventSourcing\Metadata\Event\AttributeEventMetadataFactory; use Patchlevel\EventSourcing\Metadata\Event\ClassIsNotAnEvent; -use Patchlevel\EventSourcing\Metadata\Event\MissingDataSubjectId; -use Patchlevel\EventSourcing\Metadata\Event\MultipleDataSubjectId; -use Patchlevel\EventSourcing\Metadata\Event\SubjectIdAndPersonalDataConflict; -use Patchlevel\Hydrator\Attribute\NormalizedName; use PHPUnit\Framework\TestCase; /** @covers \Patchlevel\EventSourcing\Metadata\Event\AttributeEventMetadataFactory */ @@ -41,8 +35,6 @@ class { self::assertSame('profile_created', $metadata->name); self::assertSame(false, $metadata->splitStream); - self::assertSame(null, $metadata->dataSubjectIdField); - self::assertEmpty($metadata->propertyMetadata); } public function testSplitStream(): void @@ -57,95 +49,5 @@ class { self::assertSame('profile_created', $metadata->name); self::assertSame(true, $metadata->splitStream); - self::assertSame(null, $metadata->dataSubjectIdField); - self::assertEmpty($metadata->propertyMetadata); - } - - public function testPersonalData(): void - { - $event = new #[Event('profile_created')] - class ('id', 'name') { - public function __construct( - #[DataSubjectId] - #[NormalizedName('_id')] - public string $id, - #[PersonalData('fallback')] - #[NormalizedName('_name')] - public string $name, - ) { - } - }; - - $metadataFactory = new AttributeEventMetadataFactory(); - $metadata = $metadataFactory->metadata($event::class); - - self::assertSame('profile_created', $metadata->name); - self::assertSame(false, $metadata->splitStream); - self::assertSame('_id', $metadata->dataSubjectIdField); - self::assertCount(2, $metadata->propertyMetadata); - - self::assertSame('id', $metadata->propertyMetadata['id']->propertyName); - self::assertSame(false, $metadata->propertyMetadata['id']->isPersonalData); - self::assertSame('_id', $metadata->propertyMetadata['id']->fieldName); - self::assertSame(null, $metadata->propertyMetadata['id']->personalDataFallback); - - self::assertSame('name', $metadata->propertyMetadata['name']->propertyName); - self::assertSame(true, $metadata->propertyMetadata['name']->isPersonalData); - self::assertSame('_name', $metadata->propertyMetadata['name']->fieldName); - self::assertSame('fallback', $metadata->propertyMetadata['name']->personalDataFallback); - } - - public function testMissingDataSubjectId(): void - { - $event = new #[Event('profile_created')] - class ('name') { - public function __construct( - #[PersonalData] - public string $name, - ) { - } - }; - - $this->expectException(MissingDataSubjectId::class); - - $metadataFactory = new AttributeEventMetadataFactory(); - $metadataFactory->metadata($event::class); - } - - public function testDataSubjectIdIsPersonalData(): void - { - $event = new #[Event('profile_created')] - class ('name') { - public function __construct( - #[DataSubjectId] - #[PersonalData] - public string $name, - ) { - } - }; - - $this->expectException(SubjectIdAndPersonalDataConflict::class); - - $metadataFactory = new AttributeEventMetadataFactory(); - $metadataFactory->metadata($event::class); - } - - public function testMultipleDataSubjectId(): void - { - $event = new #[Event('profile_created')] - class ('id', 'name') { - public function __construct( - #[DataSubjectId] - public string $id, - #[DataSubjectId] - public string $name, - ) { - } - }; - - $this->expectException(MultipleDataSubjectId::class); - - $metadataFactory = new AttributeEventMetadataFactory(); - $metadataFactory->metadata($event::class); } }