-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GDPR #404
base: main
Are you sure you want to change the base?
GDPR #404
Conversation
@unixslayer It should be a part of State / ES aggregates functionality. The need to encrypt data using symmetric or asymmetric encryption is quite a popular task |
The automatic encryption/decryption process can be applied to command or event attributes using PHP attributes (more abstract than PersonalData) |
@lifinsky at first we are focusing on ES aggregates as State are persisted differently and may require different approach.
Can you elaborate more? I think I don't follow. Example may be welcome. |
I am not a big fan of "GDPR" for naming the package: I'm willing to bet that in x years a new law will be called something like PROTECT (Personal Rights Over Transparency and Ethical Consent Transmission) or something else and GDPR would not have any meaning anymore :) And I guess there is this sort of regulation in the US also. |
@jlabedo |
Encryption/decryption process can be applied to any sensitive data. Payment, card data, anything. It can also be part of the tokenization process or PCI DSS |
How about making it part of the event sourcing configuration or providing the ability to use for any DTO/VO attributes? PHP has nice feature SensitiveParameter. SensitiveData as naming idea. |
At the moment, in a similar problem, my team concentrated the process of encryption and decryption in VO from an abstract class |
/**
* @template T of Stringable
*
* @SuppressWarnings(PHPMD.LongVariable)
*/
abstract class AbstractEncrypted
{
/**
* @param T $originalObject
*/
private mixed $decryptedObject;
final private function __construct(private readonly string $encrypted) {}
public static function createFromEncrypted(string $encrypted, SensitiveDataDecryptorInterface $sensitiveDataDecryptor): static
{
$encryptedObject = new static($encrypted);
$encryptedObject->assertIsValid($sensitiveDataDecryptor);
return $encryptedObject;
}
public static function createFromEncryptedSkippingValidation(string $encrypted): static
{
return new static($encrypted);
}
public function getEncryptedString(): string
{
return $this->encrypted;
}
/**
* @param self<T> $other
*/
public function equals(self $other): bool
{
return $this->encrypted === $other->encrypted;
}
/**
* @return T
*/
public function getDecryptedObject(SensitiveDataDecryptorInterface $sensitiveDataDecryptor): mixed
{
if (!isset($this->decryptedObject)) {
$this->initializeDecryptedObjectFromEncryptedData($sensitiveDataDecryptor);
}
return $this->decryptedObject;
}
/**
* @return T
*/
abstract protected function createDecryptedObject(string $decryptedString): mixed;
/**
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
*/
private function assertIsValid(SensitiveDataDecryptorInterface $sensitiveDataDecryptor): void
{
$this->initializeDecryptedObjectFromEncryptedData($sensitiveDataDecryptor);
}
private function initializeDecryptedObjectFromEncryptedData(SensitiveDataDecryptorInterface $sensitiveDataDecryptor): void
{
$this->decryptedObject = $this->createDecryptedObject($sensitiveDataDecryptor->decrypt($this->encrypted));
}
} |
We also use Ecotone converters: class CardNumberConverter
{
#[Converter]
public static function convertToString(#[SensitiveParameter] CardNumber $cardNumber): string
{
return (string) $cardNumber;
}
#[Converter]
public static function convertFromStringSkippingValidation(#[SensitiveParameter] string $cardNumber): CardNumber
{
return CardNumber::createSkippingValidation($cardNumber);
}
public static function convertFromString(#[SensitiveParameter] string $cardNumber): CardNumber
{
return CardNumber::create($cardNumber);
}
} It adds a lot of boilerplate code, but allows validation of distributed events with sensitive data. |
The depth and complexity of the problem lies in the fact that it is sometimes necessary to decrypt the data for ES projection. Because reading data from event sourcing aggregates is slow. |
@@ -39,6 +39,7 @@ | |||
"packages/PdoEventSourcing/src", | |||
"packages/PdoEventSourcing/src" | |||
], | |||
"Ecotone\\GDPR\\": "packages/GDPR/src", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DataProtection
as package name sounds good :)
ModuleReferenceSearchService $moduleReferenceSearchService, | ||
InterfaceToCallRegistry $interfaceToCallRegistry | ||
): void { | ||
if (! ExtensionObjectResolver::contains(PersonalDataEncryptionConfiguration::class, $extensionObjects)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ModuleReferenceSearchService $moduleReferenceSearchService, | ||
InterfaceToCallRegistry $interfaceToCallRegistry | ||
): void { | ||
if (! ExtensionObjectResolver::contains(PersonalDataEncryptionConfiguration::class, $extensionObjects)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What we do with in other modules is that if you install the package, it enables given features by default.
So installing a package is agreement to enable given behaviour.
I think we could do that the same here.
Like if I install it, then it's enough to place attribute on the Event to start using it.
Auto-Configuration ftw :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgafka I thought about it and noticed it makes no sense for auto-configuration when it comes to encryption. Even if Ecotone knows what
should be encrypted, it is useless without knowing how
. Although after giving it a second thought I think that it may be good idea for Ecotone to manage keys by default.
{ | ||
} | ||
|
||
public static function createWithDefaults(): self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could state for which EventStore references it's enables
public static function createWithDefaults(): self | |
public static function createWithDefaults(array $eventStoreReferences = [EventStore::class]): self |
Then if someone want to switch, he simply create and pass different reference
I guess the encryption using Converters would make it work out of the box for all Messages types (queries/command/events). That would actually be huge advantage, because there is none such solution in PHP, that does that for everything. @unixslayer any thoughts on this? |
@dgafka This draft did what I planned - it gave me some additional context to think about ;)
I'm already working on this. Further implementation should be ready very soon ;) |
@unixslayer cool :) So @unixslayer can we say, that on run-time we will be dealing with decrypted messages (because deserialized), yet after they will be stored in event-store, queue, dlq they will be encrypted (because serialized)? |
@dgafka that is correct. |
No description provided.