03.Modules - matiux/broadway-sensitive-serializer Wiki

Library modules

The library consists of 2 Modules, DataManager and Serializer.

Data Manager

DataManager module deals with data encryption and decryption, the creation of the AGGREGATE_KEY and the orchestration of the logics related to the sensitization and desensitization of events.

SensitiveDataManager

It is the interface for string encryption and decryption services. It asks for the implementation of the encrypt and decrypt methods, in which to implement your own concrete logic. The interface also provides the public constant SensitiveDataManager::IS_SENSITIZED_INDICATOR which is used as a prefix in encrypted strings in order to understand if a string is in the clear or not. This check can be done with the SensitiveTool::isSensitized(string $data): bool tool. Very convenient when it is necessary to carry out validations in the hydration phase, for example of a Value Object or an Aggregate.

The library provides an implementation of this interface that uses the AES256 algorithm: AES256SensitiveDataManager

KeyGenerator

It is the interface for the AGGREGATE_KEY creation services. Asks for the implementation of the generate method.

The library provides an implementation of this interface based on openssl: OpenSSLKeyGenerator

AggregateKeys

It is the interface to the repository that takes care of the persistence of the AGGREGATE_KEY through Model AggregateKey. It asks for the implementation of the add, withAggregateId and update methods.

A DBAL-based implementation is available by installing the Broadway Sensitive Serializer DBAL library.

Serializer

BroadwaySerializerDecorator

It is the abstract class that represents the original Broadway serializer decorator. It implements the Broadway\Serializer\Serializer interface and depends on a Broadway\Serializer\Serializer object.

SensitiveSerializer

It is the concrete serializer implemented by the library. Extends Broadway\Serializer\Serializer and depends on a Broadway\Serializer\Serializer object (you can pass the standard Broadway serializer, SimpleInterfaceSerializer) and a SensitizerStrategy object.

Sensitization strategies

The library provides three different types of sensitization for the events payload, Whole, Partial and Custom.

Whole Strategy

The Whole strategy aims to encrypt all the keys of the event payload with the exception of the aggregate id and the date of issue of the event.

{
    "class": "SensitiveUser\\User\\Domain\\Event\\UserRegistered",
    "payload": {
        "email": "#-#OFLfN9XDKtWrmCmUb6mhY0Iz2V6wtam0pcqs6vDJFRU=:bxQo+zXfjUgrD0jHuht0mQ==",
        "id": "b0fce205-d816-46ac-886f-06de19236750",
        "name": "#-#EXWLg\/JANMK\/M+DmlpnOyQ==:bxQo+zXfjUgrD0jHuht0mQ==",
        "occurred_at": "2022-01-08T14:25:13.483+00:00",
        "surname": "#-#2Iuofg4NKKPLAG2kdJrbmQ==:bxQo+zXfjUgrD0jHuht0mQ=="
    }
}

The reference class for this strategy is WholePayloadSensitizer. While the client class of the strategy is WholeStrategy. This class depends on the WholePayloadSensitizer and the WholePayloadSensitizerRegistry registry which must be initialized with a class-string[] containing the list of FQCN (Full Qualified Class Name) of the events that you want to make subject to encryption. This therefore implies that not all events will be encrypted, but it can be selected selectively by populating the register.

Keys exclusion

The id key of the Aggregate can be configured during strategy creation via the WholePayloadSensitizer::$excludedIdKey attribute. In the same way it is possible to indicate a list of keys to be excluded from encryption using the WholePayloadSensitizer::$excludedKeys attribute.

Run Whole Strategy example example/WholeStrategy

./dc up -d
./dc enter
composer install
php example/WholeStrategy/example.php

Partial Strategy

The partial strategy, probably the most convenient, involves the selective and parameterized encryption of a payload. It will be sufficient to pass to the PartialPayloadSensitizerRegistry register an array with the events to be encrypted and for each event, indicating the keys:

The client class of the strategy is PartialStrategy which is dependent on the PartialPayloadSensitizerRegistry and PartialPayloadSensitizer.

$events = [
   MyEvent::class => ['email', 'surname'],
   MySecondEvent::class => ['address'],
];

new PartialPayloadSensitizerRegistry($events);

Run Partial Strategy example example/PartialStrategy

./dc up -d
./dc enter
composer install
php example/PartialStrategy/example.php

Custom Strategy

The Custom strategy involves the creation of specific Sensitizers in order to sensitize only a part of the payload according to the needs. These Sensitizers extend the abstract class PayloadSensitizer which involves the implementation of the PayloadSensitizer::generateSensitizedPayload(string $decryptedAggregateKey): array and PayloadSensitizer::generateDesensitizedPayload(string $decryptedAggregateKey): array methods.

Once defined, the Sensitizers must be used to initialize the specific CustomPayloadSensitizerRegistry registry of this strategy.

The client class of the strategy is CustomStrategy which is solely dependent on the CustomPayloadSensitizerRegistry. An example of implementation is present in the test.

Run Custom Strategy example example/CustomStrategy

./dc up -d
./dc enter
composer install
php example/CustomStrategy/example.php

Strategy summary

Automatic creation of the AggregateKey model

The PayloadSensitizer::$automaticAggregateKeyCreation parameter determines if the AggregateKey model should be created automatically at serialization, or if you want to create it manually. The existence check of the model is not carried out in the PayloadSensitizer::desensitize(array $serializedObject): array method as it would be a contradiction; the process of saving events starts with the saving and relative serialization of a first event, so when calling the desensitize method it is assumed that the AggregateKey has already been created. Otherwise an exception will be throw.

Automatic creation

In this mode the AggregateKey model, if it does not exist, is created when calling method PayloadSensitizer::sensitize(array $serializedObject): array. The key is created if it does not exist, otherwise it uses the existing one:

$decryptedAggregateKey = $this->automaticAggregateKeyCreation ?
   $this->createAggregateKeyIfDoesNotExist($aggregateId) :
   $this->obtainDecryptedAggregateKeyOrError($aggregateId);

Manual creation

In this mode the AggregateKey model must exist, if it doesn't, an exception will be raised. This mode involves creating the model in advance. The most convenient time may be during the creation of the Aggregate.

$aggregateKeyManager->createAggregateKey($userId);

$user = User::create($userId, $name, $surname, $email, $registrationDate);

$users->add($user);

Generally speaking, the correct way to handle this in both ways would be to run the domain service atomically, within a transaction. The ddd-starter-pack library provide some convenient abstractions to handle this: TransactionalApplicationServiceTest

Architectural diagram

Architecture diagram