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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
## Next

* [feature] 🌟 String pattern anonymizer, build complex strings by fetching values from other anonymizers.
* [internal] introduce anonymizer context for carrying environment configuration to anonymizers (#235).
* [bc] Salt in `AbstractAnonymizer::$option->get('salt')` in now in `AbstractAnonymizer::$context->salt` (#235).
* [bc] `AbstractAnonymizer::__construct()` now expects an additional `$context` parameter (#235).
* [bc] `Anonymizator::__construct()` `$salt` parameter was removed (#235).
* [fix] Some minor PHP 8.4 deprecations.

## 2.0.3

Expand Down
22 changes: 11 additions & 11 deletions src/Anonymization/Anonymizator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\AbstractAnonymizer;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\AnonymizerRegistry;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Context;
use MakinaCorpus\DbToolsBundle\Anonymization\Config\AnonymizationConfig;
use MakinaCorpus\DbToolsBundle\Anonymization\Config\AnonymizerConfig;
use MakinaCorpus\DbToolsBundle\Helper\Format;
Expand Down Expand Up @@ -42,15 +43,17 @@ class Anonymizator implements LoggerAwareInterface
];

private OutputInterface $output;
private readonly Context $defaultContext;

public function __construct(
private DatabaseSession $databaseSession,
private AnonymizerRegistry $anonymizerRegistry,
private AnonymizationConfig $anonymizationConfig,
private ?string $salt = null,
?Context $defaultContext = null,
) {
$this->logger = new NullLogger();
$this->output = new NullOutput();
$this->defaultContext = $defaultContext ?? new Context();
}

Comment on lines -50 to 58
Copy link
Member

Choose a reason for hiding this comment

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

I in favor of keeping things the most simple as we can. Here, can we use the both options for the $defaultContext property ?

  1. If we always give a $defaultContext, I think we should not accept it to be null.
  2. If we never give a $defaultContext, then we should remove it from params and always prepopulate it in constructor.

I think we should not introduce behavior we don't need because it is more code to maintain, and it makes things more complicated to understand.

/**
Expand All @@ -71,25 +74,21 @@ public function setOutput(OutputInterface $output): self
return $this;
}

#[\Deprecated(message: "Will be removed in 3.0, use Context::generateRandomSalt() instead.", since: "2.1.0")]
public static function generateRandomSalt(): string
{
return \base64_encode(\random_bytes(12));
}

protected function getSalt(): string
{
return $this->salt ??= self::generateRandomSalt();
return Context::generateRandomSalt();
}

/**
* Create anonymizer instance.
*/
protected function createAnonymizer(AnonymizerConfig $config): AbstractAnonymizer
protected function createAnonymizer(AnonymizerConfig $config, Context $context): AbstractAnonymizer
{
return $this->anonymizerRegistry->createAnonymizer(
$config->anonymizer,
$config,
$config->options->with(['salt' => $this->getSalt()]),
$context,
$this->databaseSession
);
}
Expand Down Expand Up @@ -127,6 +126,7 @@ public function anonymize(
}

$plan = [];
$context = clone $this->defaultContext;
Copy link
Member

Choose a reason for hiding this comment

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

Given taht $context is readonly in AbstractAnonymizer, why do you need to clone it here ?


if ($onlyTargets) {
foreach ($onlyTargets as $targetString) {
Expand Down Expand Up @@ -160,7 +160,7 @@ public function anonymize(
foreach ($plan as $table => $targets) {
$anonymizers[$table] = [];
foreach ($this->anonymizationConfig->getTableConfig($table, $targets) as $target => $config) {
$anonymizers[$table][] = $this->createAnonymizer($config);
$anonymizers[$table][] = $this->createAnonymizer($config, $context);
}
}

Expand Down Expand Up @@ -910,7 +910,7 @@ public function checkAnonymizationConfig(): array
foreach ($this->anonymizationConfig->all() as $table => $tableConfig) {
foreach ($tableConfig as $config) {
try {
$this->createAnonymizer($config);
$this->createAnonymizer($config, $this->defaultContext);
} catch (\Exception $e) {
if (!\key_exists($table, $errors)) {
$errors[$table] = [];
Expand Down
7 changes: 4 additions & 3 deletions src/Anonymization/Anonymizer/AbstractAnonymizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer;

use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizator;
use MakinaCorpus\QueryBuilder\DatabaseSession;
use MakinaCorpus\QueryBuilder\Expression;
use MakinaCorpus\QueryBuilder\ExpressionFactory;
Expand All @@ -22,7 +21,8 @@ final public function __construct(
protected string $tableName,
protected string $columnName,
protected DatabaseSession $databaseSession,
protected Options $options,
protected readonly Context $context,
protected readonly Options $options,
) {
$this->validateOptions();
}
Expand Down Expand Up @@ -77,9 +77,10 @@ protected function getJoinColumn(): Expression
/**
* Get a random, global salt for anonymizing hashed values.
*/
#[\Deprecated(message: "Will be removed in 3.0, use \$this->context->salt instead.", since: "2.1.0")]
protected function getSalt(): string
{
return $this->options->get('salt') ?? Anonymizator::generateRandomSalt();
return $this->context->salt;
}
Comment on lines +80 to 84
Copy link
Member

Choose a reason for hiding this comment

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

I think we can directly get rid of this


/**
Expand Down
4 changes: 2 additions & 2 deletions src/Anonymization/Anonymizer/AnonymizerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ public function getAllAnonymizerMetadata(): array
public function createAnonymizer(
string $name,
AnonymizerConfig $config,
Options $options,
Context $context,
DatabaseSession $databaseSession,
): AbstractAnonymizer {
$className = $this->getAnonymizerClass($name);

$ret = new $className($config->table, $config->targetName, $databaseSession, $options);
$ret = new $className($config->table, $config->targetName, $databaseSession, $context, $config->options);
\assert($ret instanceof AbstractAnonymizer);

if ($ret instanceof WithAnonymizerRegistry) {
Expand Down
21 changes: 21 additions & 0 deletions src/Anonymization/Anonymizer/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer;

class Context
{
public readonly string $salt;

public function __construct(
?string $salt = null,
) {
$this->salt = $salt ?? self::generateRandomSalt();
}

public static function generateRandomSalt(): string
{
return \base64_encode(\random_bytes(12));
}
}
2 changes: 1 addition & 1 deletion src/Anonymization/Anonymizer/Core/EmailAnonymizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function createAnonymizeExpression(Update $update): Expression
$userExpr = $expr->column($this->columnName, $this->tableName);

if ($this->options->getBool('use_salt', true)) {
$userExpr = $expr->concat($userExpr, $expr->value($this->getSalt()));
$userExpr = $expr->concat($userExpr, $expr->value($this->context->salt));
}

$emailHashExpr = $expr->md5($userExpr);
Expand Down
2 changes: 1 addition & 1 deletion src/Anonymization/Anonymizer/Core/Md5Anonymizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function createAnonymizeExpression(Update $update): Expression
$columnExpr = $expr->column($this->columnName, $this->tableName);

if ($this->options->get('use_salt', true)) {
$columnExpr = $expr->concat($columnExpr, $expr->value($this->getSalt()));
$columnExpr = $expr->concat($columnExpr, $expr->value($this->context->salt));

// Work around some RDBMS not seeing the NULL value anymore
// once we added the string concat.
Expand Down
4 changes: 2 additions & 2 deletions src/Anonymization/Anonymizer/Core/StringPatternAnonymizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,14 @@ private function getAnonymizer(string $anonymizer, ?Options $options = null, int
return $ret;
}

$config = new AnonymizerConfig($this->tableName, $this->columnName, $anonymizer, new Options());
$config = new AnonymizerConfig($this->tableName, $this->columnName, $anonymizer, $options ?? new Options());

return $this->childAnonymizers[$key] = $this
->getAnonymizerRegistry()
->createAnonymizer(
$anonymizer,
$config,
$options ?? new Options(),
$this->context,
$this->databaseSession
)
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace MakinaCorpus\DbToolsBundle\Tests\Unit\Anonymization\Anonymizer;

use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\AbstractMultipleColumnAnonymizer;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Context;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Options;
use MakinaCorpus\DbToolsBundle\Attribute\AsAnonymizer;
use MakinaCorpus\DbToolsBundle\Test\UnitTestCase;
Expand All @@ -17,6 +18,7 @@ public function testValidateOptionsOkWithAllColumnOption(): void
'some_table',
'some_column',
$this->getDatabaseSession(),
new Context(),
new Options([
'column_1' => 'actual_column_1',
'column_2' => 'actual_column_2',
Expand All @@ -32,6 +34,7 @@ public function testValidateOptionsOkWithSomeColumnOption(): void
'some_table',
'some_column',
$this->getDatabaseSession(),
new Context(),
new Options([
'column_2' => 'actual_column_2',
]),
Expand All @@ -48,7 +51,8 @@ public function testValidateOptionsKoWithNoOption(): void
'some_table',
'some_column',
$this->getDatabaseSession(),
new Options([]),
new Context(),
new Options(),
);
}

Expand All @@ -60,6 +64,7 @@ public function testValidateOptionsKoWithColumnMapedTwice(): void
'some_table',
'some_column',
$this->getDatabaseSession(),
new Context(),
new Options([
'column_1' => 'actual_column_1',
'column_2' => 'actual_column_1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace MakinaCorpus\DbToolsBundle\Tests\Unit\Anonymization\Anonymizer\Core;

use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Context;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Core\ConstantAnonymizer;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Options;
use MakinaCorpus\DbToolsBundle\Test\UnitTestCase;
Expand All @@ -16,6 +17,7 @@ public function testValidateOptionsOkWithValueOption(): void
'some_table',
'some_column',
$this->getDatabaseSession(),
new Context(),
new Options([
'value' => 'test',
]),
Expand All @@ -32,6 +34,7 @@ public function testValidateOptionsKoWithValueOption(): void
'some_table',
'some_column',
$this->getDatabaseSession(),
new Context(),
new Options([]),
);
}
Expand Down
22 changes: 16 additions & 6 deletions tests/Unit/Anonymization/Anonymizer/Core/EmailAnonymizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace MakinaCorpus\DbToolsBundle\Tests\Unit\Anonymization\Anonymizer\Core;

use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Options;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Context;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Core\EmailAnonymizer;
use MakinaCorpus\DbToolsBundle\Anonymization\Anonymizer\Options;
use MakinaCorpus\DbToolsBundle\Test\UnitTestCase;

class EmailAnonymizerTest extends UnitTestCase
Expand All @@ -16,7 +17,8 @@ public function testValidateOptionsOkWithNoOption(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Options([]),
new Context(),
new Options(),
);

self::expectNotToPerformAssertions();
Expand All @@ -28,6 +30,7 @@ public function testValidateOptionsOkWithDomainOptionAsString(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Context(),
new Options([
'domain' => 'makina-corpus.com',
]),
Expand All @@ -44,6 +47,7 @@ public function testValidateOptionsKoWithDomainNotStringable(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Context(),
new Options([
'domain' => ['ttt', 'ttt'],
]),
Expand All @@ -56,6 +60,7 @@ public function testValidateOptionsOkWithUseSaltOptionAsBool(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Context(),
new Options([
'use_salt' => true,
]),
Expand All @@ -72,6 +77,7 @@ public function testValidateOptionsKoWithUseSaltOptionAsNoneBool(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Context(),
new Options([
'use_salt' => ['true'],
]),
Expand All @@ -86,9 +92,10 @@ public function testAnonymizeWithDefaultDomain(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Options([
'salt' => 'my_salt',
])
new Context(
salt: 'my_salt',
),
new Options(),
);

$instance->anonymize($update);
Expand Down Expand Up @@ -121,9 +128,11 @@ public function testAnonymize(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Context(
salt: 'my_salt',
),
new Options([
'domain' => 'makina-corpus.com',
'salt' => 'my_salt',
]),
);

Expand Down Expand Up @@ -157,6 +166,7 @@ public function testAnonymizeWithoutSalt(): void
'some_table',
'email',
$this->getDatabaseSession(),
new Context(),
new Options([
'use_salt' => false,
]),
Expand Down
Loading