-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathExtensionDoctrineCipherKeyStore.php
More file actions
127 lines (110 loc) · 4.06 KB
/
Copy pathExtensionDoctrineCipherKeyStore.php
File metadata and controls
127 lines (110 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php
declare(strict_types=1);
namespace Patchlevel\EventSourcing\Cryptography;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Patchlevel\EventSourcing\Schema\DoctrineHelper;
use Patchlevel\EventSourcing\Schema\DoctrineSchemaConfigurator;
use Patchlevel\Hydrator\Extension\Cryptography\Cipher\CipherKey;
use Patchlevel\Hydrator\Extension\Cryptography\Store\CipherKeyNotExists;
use Patchlevel\Hydrator\Extension\Cryptography\Store\CipherKeyStore;
use function base64_decode;
use function base64_encode;
/**
* @phpstan-type Row = array{
* id: non-empty-string,
* subject_id: non-empty-string,
* crypto_key: non-empty-string,
* crypto_method: non-empty-string,
* created_at: non-empty-string
* }
*/
final class ExtensionDoctrineCipherKeyStore implements CipherKeyStore, DoctrineSchemaConfigurator
{
private Type $dateTimeType;
public function __construct(
private readonly Connection $connection,
private readonly string $tableName = 'cryptography_keys',
) {
$this->dateTimeType = Type::getType(Types::DATETIMETZ_IMMUTABLE);
}
public function get(string $id): CipherKey
{
/** @var Row|false $result */
$result = $this->connection->fetchAssociative(
"SELECT * FROM {$this->tableName} WHERE id = :id",
['id' => $id],
);
if ($result === false) {
throw CipherKeyNotExists::forKeyId($id);
}
return new CipherKey(
$result['id'],
$result['subject_id'],
base64_decode($result['crypto_key']),
$result['crypto_method'],
$this->dateTimeType->convertToPHPValue($result['created_at'], $this->connection->getDatabasePlatform()),
);
}
public function currentKeyFor(string $subjectId): CipherKey
{
/** @var Row|false $result */
$result = $this->connection->fetchAssociative(
"SELECT * FROM {$this->tableName} WHERE subject_id = :subject_id",
['subject_id' => $subjectId],
);
if ($result === false) {
throw CipherKeyNotExists::forSubjectId($subjectId);
}
return new CipherKey(
$result['id'],
$result['subject_id'],
base64_decode($result['crypto_key']),
$result['crypto_method'],
$this->dateTimeType->convertToPHPValue($result['created_at'], $this->connection->getDatabasePlatform()),
);
}
public function store(CipherKey $key): void
{
$this->connection->insert($this->tableName, [
'id' => $key->id,
'subject_id' => $key->subjectId,
'crypto_key' => base64_encode($key->key),
'crypto_method' => $key->method,
'created_at' => $this->dateTimeType->convertToDatabaseValue($key->createdAt, $this->connection->getDatabasePlatform()),
]);
}
public function remove(string $id): void
{
$this->connection->delete($this->tableName, ['id' => $id]);
}
public function removeWithSubjectId(string $subjectId): void
{
$this->connection->delete($this->tableName, ['subject_id' => $subjectId]);
}
public function configureSchema(Schema $schema, Connection $connection): void
{
if (!DoctrineHelper::sameDatabase($this->connection, $connection)) {
return;
}
$table = $schema->createTable($this->tableName);
$table->addColumn('id', 'string')
->setNotnull(true)
->setLength(255);
$table->addColumn('subject_id', 'string')
->setNotnull(true)
->setLength(255);
$table->addColumn('crypto_key', 'string')
->setNotnull(true)
->setLength(255);
$table->addColumn('crypto_method', 'string')
->setNotnull(true)
->setLength(255);
$table->addColumn('created_at', 'datetimetz_immutable')
->setNotnull(true);
$table->setPrimaryKey(['id']);
$table->addIndex(['subject_id']);
}
}