Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
/**
* licence Enterprise
*/
class ChannelProtectionConfiguration
readonly class ChannelProtectionConfiguration
{
private function __construct(
private string $channelName,
private ?string $encryptionKey,
private bool $isPayloadSensitive,
private array $sensitiveHeaders,
public string $channelName,
public ?string $encryptionKey,
public bool $isPayloadSensitive,
public array $sensitiveHeaders,
) {
}

Expand All @@ -20,29 +20,18 @@ public static function create(string $channelName, ?string $encryptionKey = null
return new self($channelName, $encryptionKey, $isPayloadSensitive, $sensitiveHeaders);
}

public function channelName(): string
public function withEncryptionKey(string $encryptionKey): self
{
return $this->channelName;
}

public function messageEncryptionConfig(): MessageEncryptionConfig
{
return new MessageEncryptionConfig($this->encryptionKey, $this->isPayloadSensitive, $this->sensitiveHeaders);
return self::create($this->channelName, $encryptionKey, $this->isPayloadSensitive, $this->sensitiveHeaders);
}

public function withSensitivePayload(bool $isPayloadSensitive): self
{
$config = clone $this;
$config->isPayloadSensitive = $isPayloadSensitive;

return $config;
return self::create($this->channelName, $this->encryptionKey, $isPayloadSensitive, $this->sensitiveHeaders);
}

public function withSensitiveHeader(string $sensitiveHeader): self
{
$config = clone $this;
$config->sensitiveHeaders[] = $sensitiveHeader;

return $config;
return self::create($this->channelName, $this->encryptionKey, $this->isPayloadSensitive, array_merge($this->sensitiveHeaders, [$sensitiveHeader]));
}
}
103 changes: 57 additions & 46 deletions packages/DataProtection/src/Configuration/DataProtectionModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
use Ecotone\AnnotationFinder\AnnotationFinder;
use Ecotone\DataProtection\Attribute\Sensitive;
use Ecotone\DataProtection\Attribute\WithEncryptionKey;
use Ecotone\DataProtection\Attribute\WithSensitiveHeader;
use Ecotone\DataProtection\Encryption\Key;
use Ecotone\DataProtection\MessageEncryption\MessageEncryptor;
use Ecotone\DataProtection\OutboundDecryptionChannelBuilder;
use Ecotone\DataProtection\OutboundEncryptionChannelBuilder;
use Ecotone\DataProtection\Protector\ChannelProtector;
use Ecotone\DataProtection\Protector\DataDecryptor;
use Ecotone\DataProtection\Protector\DataEncryptor;
use Ecotone\DataProtection\Protector\DataProtectorConfig;
use Ecotone\JMSConverter\JMSConverterConfiguration;
use Ecotone\Messaging\Attribute\ModuleAnnotation;
use Ecotone\Messaging\Attribute\Parameter\Header;
Expand All @@ -28,6 +30,7 @@
use Ecotone\Messaging\Config\Container\Reference;
use Ecotone\Messaging\Config\ModulePackageList;
use Ecotone\Messaging\Config\ModuleReferenceSearchService;
use Ecotone\Messaging\Handler\ClassPropertyDefinition;
use Ecotone\Messaging\Handler\InterfaceToCallRegistry;
use Ecotone\Messaging\Handler\Type;
use Ecotone\Messaging\Support\Assert;
Expand All @@ -43,19 +46,19 @@ final class DataProtectionModule extends NoExternalConfigurationModule
final public const KEY_SERVICE_ID_FORMAT = 'ecotone.encryption.key.%s';

/**
* @param array<MessageEncryptionConfig> $encryptionConfigs
* @param array<DataProtectorConfig> $dataProtectorConfigs
*/
public function __construct(private array $encryptionConfigs)
public function __construct(private array $dataProtectorConfigs)
{
}

public static function create(AnnotationFinder $annotationRegistrationService, InterfaceToCallRegistry $interfaceToCallRegistry): static
{
$encryptionConfigs = self::resolveEncryptionConfigsFromAnnotatedClasses($annotationRegistrationService->findAnnotatedClasses(Sensitive::class), $interfaceToCallRegistry);
$encryptionConfigs = self::resolveEncryptionConfigsFromAnnotatedMethods($annotationRegistrationService->findAnnotatedMethods(CommandHandler::class), $encryptionConfigs, $interfaceToCallRegistry);
$encryptionConfigs = self::resolveEncryptionConfigsFromAnnotatedMethods($annotationRegistrationService->findAnnotatedMethods(EventHandler::class), $encryptionConfigs, $interfaceToCallRegistry);
$dataProtectorConfigs = self::resolveProtectorConfigsFromAnnotatedClasses($annotationRegistrationService->findAnnotatedClasses(Sensitive::class), $interfaceToCallRegistry);
$dataProtectorConfigs = self::resolveProtectorConfigsFromAnnotatedMethods($annotationRegistrationService->findAnnotatedMethods(CommandHandler::class), $dataProtectorConfigs, $interfaceToCallRegistry);
$dataProtectorConfigs = self::resolveProtectorConfigsFromAnnotatedMethods($annotationRegistrationService->findAnnotatedMethods(EventHandler::class), $dataProtectorConfigs, $interfaceToCallRegistry);

return new self($encryptionConfigs);
return new self($dataProtectorConfigs);
}

public function prepare(Configuration $messagingConfiguration, array $extensionObjects, ModuleReferenceSearchService $moduleReferenceSearchService, InterfaceToCallRegistry $interfaceToCallRegistry): void
Expand All @@ -82,39 +85,48 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO
);
}

$channelEncryptorReferences = $messageEncryptorReferences = [];
$channelProtectorReferences = $messageEncryptorReferences = [];
foreach ($channelProtectionConfigurations as $channelProtectionConfiguration) {
Assert::isTrue($messagingConfiguration->isPollableChannel($channelProtectionConfiguration->channelName()), sprintf('`%s` channel must be pollable channel to use Data Protection.', $channelProtectionConfiguration->channelName()));

$encryptionConfig = $channelProtectionConfiguration->messageEncryptionConfig();
$messagingConfiguration->registerServiceDefinition(
id: $id = sprintf(self::ENCRYPTOR_SERVICE_ID_FORMAT, $channelProtectionConfiguration->channelName()),
id: $id = sprintf(self::ENCRYPTOR_SERVICE_ID_FORMAT, $channelProtectionConfiguration->channelName),
definition: new Definition(
MessageEncryptor::class,
ChannelProtector::class,
[
Reference::to(sprintf(self::KEY_SERVICE_ID_FORMAT, $encryptionConfig->encryptionKeyName($dataProtectionConfiguration))),
$encryptionConfig->isPayloadSensitive,
$encryptionConfig->sensitiveHeaders,
Reference::to(sprintf(self::KEY_SERVICE_ID_FORMAT, $dataProtectionConfiguration->keyName($channelProtectionConfiguration->encryptionKey))),
$channelProtectionConfiguration->isPayloadSensitive,
$channelProtectionConfiguration->sensitiveHeaders,
],
)
);

$channelEncryptorReferences[$channelProtectionConfiguration->channelName()] = Reference::to($id);
$channelProtectorReferences[$channelProtectionConfiguration->channelName()] = Reference::to($id);
}

foreach ($this->encryptionConfigs as $messageClass => $encryptionConfig) {
$messagingConfiguration->registerServiceDefinition(
id: $id = sprintf(self::ENCRYPTOR_SERVICE_ID_FORMAT, $messageClass),
definition: new Definition(
MessageEncryptor::class,
foreach ($this->dataProtectorConfigs as $protectorConfig) {
$messagingConfiguration->registerDataProtector(
new Definition(
DataEncryptor::class,
[
Reference::to(sprintf(self::KEY_SERVICE_ID_FORMAT, $encryptionConfig->encryptionKeyName($dataProtectionConfiguration))),
$encryptionConfig->isPayloadSensitive,
$encryptionConfig->sensitiveHeaders,
$protectorConfig->supportedType,
Reference::to(sprintf(self::KEY_SERVICE_ID_FORMAT, $protectorConfig->encryptionKeyName($dataProtectionConfiguration))),
$protectorConfig->sensitiveProperties,
$protectorConfig->scalarProperties,
],
)
);
$messagingConfiguration->registerDataProtector(
new Definition(
DataDecryptor::class,
[
$protectorConfig->supportedType,
Reference::to(sprintf(self::KEY_SERVICE_ID_FORMAT, $protectorConfig->encryptionKeyName($dataProtectionConfiguration))),
$protectorConfig->sensitiveProperties,
$protectorConfig->scalarProperties,
],
)
);
$messageEncryptorReferences[$messageClass] = Reference::to($id);
}

foreach (ExtensionObjectResolver::resolve(MessageChannelWithSerializationBuilder::class, $extensionObjects) as $pollableMessageChannel) {
Expand All @@ -125,14 +137,14 @@ public function prepare(Configuration $messagingConfiguration, array $extensionO
$messagingConfiguration->registerChannelInterceptor(
new OutboundEncryptionChannelBuilder(
relatedChannel: $pollableMessageChannel->getMessageChannelName(),
channelEncryptorReference: $channelEncryptorReferences[$pollableMessageChannel->getMessageChannelName()] ?? null,
channelEncryptorReference: $channelProtectorReferences[$pollableMessageChannel->getMessageChannelName()] ?? null,
messageEncryptorReferences: $messageEncryptorReferences,
)
);
$messagingConfiguration->registerChannelInterceptor(
new OutboundDecryptionChannelBuilder(
relatedChannel: $pollableMessageChannel->getMessageChannelName(),
channelEncryptionReference: $channelEncryptorReferences[$pollableMessageChannel->getMessageChannelName()] ?? null,
channelEncryptionReference: $channelProtectorReferences[$pollableMessageChannel->getMessageChannelName()] ?? null,
messageEncryptionReferences: $messageEncryptorReferences,
)
);
Expand All @@ -154,21 +166,27 @@ public function getModulePackageName(): string
return ModulePackageList::DATA_PROTECTION_PACKAGE;
}

private static function resolveEncryptionConfigsFromAnnotatedClasses(array $sensitiveMessages, InterfaceToCallRegistry $interfaceToCallRegistry): array
private static function resolveProtectorConfigsFromAnnotatedClasses(array $sensitiveMessages, InterfaceToCallRegistry $interfaceToCallRegistry): array
{
$encryptionConfigs = [];
$dataEncryptorConfigs = [];
foreach ($sensitiveMessages as $message) {
$classDefinition = $interfaceToCallRegistry->getClassDefinitionFor(Type::create($message));
$classDefinition = $interfaceToCallRegistry->getClassDefinitionFor($messageType = Type::create($message));
$encryptionKey = $classDefinition->findSingleClassAnnotation(Type::create(WithEncryptionKey::class))?->encryptionKey();
$sensitiveHeaders = array_map(static fn (WithSensitiveHeader $annotation) => $annotation->header, $classDefinition->getClassAnnotations(Type::create(WithSensitiveHeader::class)) ?? []);

$encryptionConfigs[$message] = new MessageEncryptionConfig(encryptionKey: $encryptionKey, isPayloadSensitive: true, sensitiveHeaders: $sensitiveHeaders);
$sensitiveProperties = $classDefinition->getPropertiesWithAnnotation(Type::create(Sensitive::class));
if ($sensitiveProperties === []) {
$sensitiveProperties = array_map(static fn (ClassPropertyDefinition $property): string => $property->getName(), $classDefinition->getProperties());
}

$scalarProperties = array_values(array_filter($sensitiveProperties, static fn (string $propertyName): bool => $classDefinition->getProperty($propertyName)->getType()->isScalar()));

$dataEncryptorConfigs[$message] = new DataProtectorConfig(supportedType: $messageType, encryptionKey: $encryptionKey, sensitiveProperties: $sensitiveProperties, scalarProperties: $scalarProperties);
}

return $encryptionConfigs;
return $dataEncryptorConfigs;
}

private static function resolveEncryptionConfigsFromAnnotatedMethods(array $annotatedMethods, array $encryptionConfigs, InterfaceToCallRegistry $interfaceToCallRegistry): array
private static function resolveProtectorConfigsFromAnnotatedMethods(array $annotatedMethods, array $encryptionConfigs, InterfaceToCallRegistry $interfaceToCallRegistry): array
{
/** @var AnnotatedMethod $method */
foreach ($annotatedMethods as $method) {
Expand All @@ -180,24 +198,17 @@ private static function resolveEncryptionConfigsFromAnnotatedMethods(array $anno
|| $payload->hasAnnotation(Headers::class)
|| $payload->hasAnnotation(Reference::class)
|| array_key_exists($payload->getTypeHint(), $encryptionConfigs)
|| ! $payload->hasAnnotation(Sensitive::class)
) {
continue;
}

$isPayloadSensitive = $payload->hasAnnotation(Sensitive::class);
if (! $isPayloadSensitive) {
continue;
}

$classDefinition = $interfaceToCallRegistry->getClassDefinitionFor($payload->getTypeDescriptor());
$encryptionKey = $payload->findSingleAnnotation(Type::create(WithEncryptionKey::class))?->encryptionKey();
$sensitiveHeaders = array_map(static fn (WithSensitiveHeader $annotation) => $annotation->header, $methodDefinition->getMethodAnnotationsOf(Type::create(WithSensitiveHeader::class)) ?? []);
foreach ($methodDefinition->getInterfaceParameters() as $parameter) {
if ($parameter->hasAnnotation(Header::class) && $parameter->hasAnnotation(Sensitive::class)) {
$sensitiveHeaders[] = $parameter->getName();
}
}
$sensitiveProperties = array_map(static fn (ClassPropertyDefinition $property): string => $property->getName(), $classDefinition->getProperties());
$scalarProperties = array_values(array_filter($sensitiveProperties, static fn (string $propertyName): bool => $classDefinition->getProperty($propertyName)->getType()->isScalar()));

$encryptionConfigs[$payload->getTypeHint()] = new MessageEncryptionConfig(encryptionKey: $encryptionKey, isPayloadSensitive: true, sensitiveHeaders: $sensitiveHeaders);
$encryptionConfigs[$payload->getTypeHint()] = new DataProtectorConfig(supportedType: $payload->getTypeDescriptor(), encryptionKey: $encryptionKey, sensitiveProperties: $sensitiveProperties, scalarProperties: $scalarProperties);
}

return $encryptionConfigs;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Ecotone\DataProtection;

use Ecotone\DataProtection\MessageEncryption\MessageEncryptor;
use Ecotone\DataProtection\MessageEncryption\ChannelEncryptor;
use Ecotone\Messaging\Channel\AbstractChannelInterceptor;
use Ecotone\Messaging\Conversion\MediaType;
use Ecotone\Messaging\Message;
Expand All @@ -17,13 +17,13 @@
class OutboundDecryptionChannelInterceptor extends AbstractChannelInterceptor
{
/**
* @param array<MessageEncryptor> $messageEncryptors
* @param array<ChannelEncryptor> $messageEncryptors
*/
public function __construct(
private readonly ?MessageEncryptor $channelEncryptor,
private readonly ?ChannelEncryptor $channelEncryptor,
private readonly array $messageEncryptors,
) {
Assert::allInstanceOfType($this->messageEncryptors, MessageEncryptor::class);
Assert::allInstanceOfType($this->messageEncryptors, ChannelEncryptor::class);
}

public function postReceive(Message $message, MessageChannel $messageChannel): ?Message
Expand All @@ -43,7 +43,7 @@ public function postReceive(Message $message, MessageChannel $messageChannel): ?
return $message;
}

private function findMessageEncryptor(Message $message): ?MessageEncryptor
private function findMessageEncryptor(Message $message): ?ChannelEncryptor
{
if (! $message->getHeaders()->containsKey(MessageHeaders::TYPE_ID)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Ecotone\DataProtection;

use Ecotone\DataProtection\MessageEncryption\MessageEncryptor;
use Ecotone\DataProtection\MessageEncryption\ChannelEncryptor;
use Ecotone\Messaging\Channel\AbstractChannelInterceptor;
use Ecotone\Messaging\Conversion\MediaType;
use Ecotone\Messaging\Message;
Expand All @@ -17,13 +17,13 @@
class OutboundEncryptionChannelInterceptor extends AbstractChannelInterceptor
{
/**
* @param array<MessageEncryptor> $messageEncryptors
* @param array<ChannelEncryptor> $messageEncryptors
*/
public function __construct(
private readonly ?MessageEncryptor $channelEncryptor,
private readonly ?ChannelEncryptor $channelEncryptor,
private readonly array $messageEncryptors,
) {
Assert::allInstanceOfType($this->messageEncryptors, MessageEncryptor::class);
Assert::allInstanceOfType($this->messageEncryptors, ChannelEncryptor::class);
}

public function preSend(Message $message, MessageChannel $messageChannel): ?Message
Expand All @@ -43,7 +43,7 @@ public function preSend(Message $message, MessageChannel $messageChannel): ?Mess
return $message;
}

private function findMessageEncryptor(Message $message): ?MessageEncryptor
private function findMessageEncryptor(Message $message): ?ChannelEncryptor
{
if (! $message->getHeaders()->containsKey(MessageHeaders::TYPE_ID)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
* licence Enterprise
*/

namespace Ecotone\DataProtection\MessageEncryption;
namespace Ecotone\DataProtection\Protector;

use Ecotone\DataProtection\Encryption\Crypto;
use Ecotone\DataProtection\Encryption\Key;
use Ecotone\Messaging\Message;
use Ecotone\Messaging\Support\Assert;
use Ecotone\Messaging\Support\MessageBuilder;

readonly class MessageEncryptor
readonly class ChannelProtector
{
public function __construct(
private Key $encryptionKey,
Expand Down
Loading
Loading