Skip to content

Commit 492c723

Browse files
committed
Add fields property to encryptedFieldsMap
1 parent c183333 commit 492c723

File tree

6 files changed

+81
-45
lines changed

6 files changed

+81
-45
lines changed

lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -314,14 +314,4 @@ public static function rootDocumentCannotBeEncrypted(string $className): self
314314
$className,
315315
));
316316
}
317-
318-
public static function invalidEncryptedQueryRangeType(string $className, string $fieldName, string $type): self
319-
{
320-
return new self(sprintf(
321-
'The field type "%s" for field "%s::%s" is not supported for "range" query on encrypted field.',
322-
$type,
323-
$className,
324-
$fieldName,
325-
));
326-
}
327317
}

lib/Doctrine/ODM/MongoDB/MongoDBException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,9 @@ public static function transactionalSessionMismatch(): self
160160
{
161161
return new self('The transactional operation cannot be executed because it was started in a different session.');
162162
}
163+
164+
public static function notADocumentClass(string $className): self
165+
{
166+
return new self(sprintf('The class "%s" is not a document class.', $className));
167+
}
163168
}

lib/Doctrine/ODM/MongoDB/SchemaManager.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ public function createDocumentCollection(string $documentName, ?int $maxTimeMs =
650650
$encryptedFields = (new EncryptedFieldsMapGenerator($this->dm->getMetadataFactory()))->getEncryptedFieldsMapForClass($class->name);
651651

652652
if ($encryptedFields) {
653-
$options['encryptedFields'] = ['fields' => $encryptedFields];
653+
$options['encryptedFields'] = $encryptedFields;
654654
}
655655
}
656656

lib/Doctrine/ODM/MongoDB/Utility/EncryptedFieldsMapGenerator.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
99
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface;
1010
use Doctrine\ODM\MongoDB\Mapping\MappingException;
11+
use Doctrine\ODM\MongoDB\MongoDBException;
1112
use Doctrine\ODM\MongoDB\Types\Type;
1213
use Generator;
1314
use LogicException;
@@ -26,7 +27,7 @@ public function __construct(private ClassMetadataFactoryInterface $classMetadata
2627
/**
2728
* Returns the full encryption fields map for a document manager
2829
*
29-
* @return array<class-string, array<int, array{path: string, bsonType: string, keyId: ?string}>>
30+
* @return array<class-string, array{fields: array<int, array{path: string, bsonType: string, keyId: null}>}>
3031
*/
3132
public function getEncryptedFieldsMap(): array
3233
{
@@ -43,7 +44,7 @@ public function getEncryptedFieldsMap(): array
4344
continue;
4445
}
4546

46-
$encryptedFieldsMap[$classMetadata->getName()] = $classMap;
47+
$encryptedFieldsMap[$classMetadata->getName()] = ['fields' => $classMap];
4748
}
4849

4950
return $encryptedFieldsMap;
@@ -54,20 +55,30 @@ public function getEncryptedFieldsMap(): array
5455
*
5556
* @param class-string $className
5657
*
57-
* @return array<int, array{path: string, bsonType: string, keyId: ?string}>
58+
* @return array{fields: array<int, array{path: string, bsonType: string, keyId: null}>}|null
5859
*/
59-
public function getEncryptedFieldsMapForClass(string $className): array
60+
public function getEncryptedFieldsMapForClass(string $className): ?array
6061
{
6162
$classMetadata = $this->classMetadataFactory->getMetadataFor($className);
6263

63-
return iterator_to_array($this->createEncryptedFieldsMapForClass($classMetadata));
64+
if (! $classMetadata->isDocument()) {
65+
throw MongoDBException::notADocumentClass($className);
66+
}
67+
68+
$fields = iterator_to_array($this->createEncryptedFieldsMapForClass($classMetadata));
69+
70+
if ($fields === []) {
71+
return null;
72+
}
73+
74+
return ['fields' => $fields];
6475
}
6576

6677
/**
6778
* @param array<class-string, true> $visitedClasses
6879
* @phpstan-param ClassMetadata<T> $classMetadata
6980
*
70-
* @return Generator<int, array{path: string, bsonType: string, keyId: ?string}>
81+
* @return Generator<int, array{path: string, bsonType: string, keyId: null}>
7182
*
7283
* @template T of object
7384
*/
@@ -86,15 +97,14 @@ private function createEncryptedFieldsMapForClass(
8697
}
8798

8899
foreach ($classMetadata->fieldMappings as $mapping) {
89-
// @todo support polymorphic types and inheritance?
90100
// Add fields recursively
91101
if ($mapping['embedded'] ?? false) {
92102
$embedMetadata = $this->classMetadataFactory->getMetadataFor($mapping['targetDocument']);
93103

94104
// When the embedded document class is encrypted, the field is encrypted,
95105
// but none of the embedded fields are encrypted separately.
96106
if ($embedMetadata->isEncrypted) {
97-
$mapping['encrypt'] ??= []; // @todo get the keyId
107+
$mapping['encrypt'] ??= [];
98108
} elseif (! isset($mapping['encrypt'])) {
99109
yield from $this->createEncryptedFieldsMapForClass(
100110
$embedMetadata,

phpstan-baseline.neon

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,43 +1999,43 @@ parameters:
19991999
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20002000

20012001
-
2002-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:__construct\(\) has parameter \$classNames with no value type specified in iterable type array\.$#'
2002+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:__construct\(\) has parameter \$classNames with no value type specified in iterable type array\.$#'
20032003
identifier: missingType.iterableValue
20042004
count: 1
20052005
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20062006

20072007
-
2008-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:doLoadMetadata\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
2008+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:doLoadMetadata\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
20092009
identifier: missingType.generics
20102010
count: 1
20112011
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20122012

20132013
-
2014-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:doLoadMetadata\(\) has parameter \$parent with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
2014+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:doLoadMetadata\(\) has parameter \$parent with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
20152015
identifier: missingType.generics
20162016
count: 1
20172017
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20182018

20192019
-
2020-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:getAllMetadata\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#'
2020+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:getAllMetadata\(\) return type with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata does not specify its types\: T$#'
20212021
identifier: missingType.generics
20222022
count: 1
20232023
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20242024

20252025
-
2026-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:initializeReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
2026+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:initializeReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
20272027
identifier: missingType.generics
20282028
count: 1
20292029
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20302030

20312031
-
2032-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:isEntity\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
2032+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:isEntity\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
20332033
identifier: missingType.generics
20342034
count: 1
20352035
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php
20362036

20372037
-
2038-
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:168\:\:wakeupReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
2038+
message: '#^Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory@anonymous/tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest\.php\:197\:\:wakeupReflection\(\) has parameter \$class with generic interface Doctrine\\Persistence\\Mapping\\ClassMetadata but does not specify its types\: T$#'
20392039
identifier: missingType.generics
20402040
count: 1
20412041
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php

tests/Doctrine/ODM/MongoDB/Tests/Tools/EncryptedFieldsMapGeneratorTest.php

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
1111
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface;
1212
use Doctrine\ODM\MongoDB\Mapping\MappingException;
13+
use Doctrine\ODM\MongoDB\MongoDBException;
1314
use Doctrine\ODM\MongoDB\Tests\BaseTestCase;
1415
use Doctrine\ODM\MongoDB\Utility\EncryptedFieldsMapGenerator;
1516
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
1617
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
1718
use Doctrine\Persistence\Mapping\ReflectionService;
19+
use Documents\Bars\Bar;
1820
use Documents\Encryption\Client;
1921
use Documents\Encryption\InvalidRootEncrypt;
2022
use Documents\Encryption\Patient;
@@ -52,7 +54,7 @@ public function testGetEncryptionFieldsMapForClass(): void
5254
],
5355
];
5456

55-
self::assertEquals($expected, $encryptedFieldsMap);
57+
self::assertEquals(['fields' => $expected], $encryptedFieldsMap);
5658
}
5759

5860
public function testGetEncryptionFieldsMapForClassForEmbeddedDocument(): void
@@ -73,7 +75,7 @@ public function testGetEncryptionFieldsMapForClassForEmbeddedDocument(): void
7375
],
7476
];
7577

76-
self::assertSame($expected, $encryptedFieldsMap);
78+
self::assertEquals(['fields' => $expected], $encryptedFieldsMap);
7779
}
7880

7981
public function testVariousRangeTypes(): void
@@ -115,7 +117,7 @@ public function testVariousRangeTypes(): void
115117
],
116118
];
117119

118-
self::assertEquals($expected, $encryptedFieldsMap);
120+
self::assertEquals(['fields' => $expected], $encryptedFieldsMap);
119121
}
120122

121123
public function testRootDocumentsCannotBeEncrypted(): void
@@ -140,29 +142,58 @@ public function testGetEncryptionFieldsMap(): void
140142

141143
$expectedEncryptedFieldsMap = [
142144
Patient::class => [
143-
[
144-
'path' => 'patientRecord.ssn',
145-
'bsonType' => 'string',
146-
'keyId' => null,
147-
'queries' => ['queryType' => 'equality'],
148-
],
149-
[
150-
'path' => 'patientRecord.billing',
151-
'bsonType' => 'object',
152-
'keyId' => null,
153-
],
154-
[
155-
'path' => 'patientRecord.billingAmount',
156-
'bsonType' => 'int',
157-
'keyId' => null,
158-
'queries' => ['queryType' => 'range', 'min' => 100, 'max' => 2000, 'sparsity' => 1, 'trimFactor' => 4],
145+
'fields' => [
146+
[
147+
'path' => 'patientRecord.ssn',
148+
'bsonType' => 'string',
149+
'keyId' => null,
150+
'queries' => ['queryType' => 'equality'],
151+
],
152+
[
153+
'path' => 'patientRecord.billing',
154+
'bsonType' => 'object',
155+
'keyId' => null,
156+
],
157+
[
158+
'path' => 'patientRecord.billingAmount',
159+
'bsonType' => 'int',
160+
'keyId' => null,
161+
'queries' => ['queryType' => 'range', 'min' => 100, 'max' => 2000, 'sparsity' => 1, 'trimFactor' => 4],
162+
],
159163
],
160164
],
161165
];
162166

163167
$this->assertEquals($expectedEncryptedFieldsMap, $encryptedFieldsMap);
164168
}
165169

170+
public function testNoEncryptedFields(): void
171+
{
172+
$classMetadataFactory = $this->createMetadataFactory(
173+
$this->dm->getMetadataFactory(),
174+
Bar::class,
175+
);
176+
177+
$factory = new EncryptedFieldsMapGenerator($classMetadataFactory);
178+
179+
self::assertSame([], $factory->getEncryptedFieldsMap());
180+
self::assertNull($factory->getEncryptedFieldsMapForClass(Bar::class));
181+
}
182+
183+
public function testNotADocumentClass(): void
184+
{
185+
$classMetadataFactory = $this->createMetadataFactory(
186+
$this->dm->getMetadataFactory(),
187+
PatientRecord::class,
188+
);
189+
190+
$this->expectException(MongoDBException::class);
191+
$this->expectExceptionMessage('The class "Documents\Encryption\PatientRecord" is not a document class.');
192+
193+
$factory = new EncryptedFieldsMapGenerator($classMetadataFactory);
194+
$factory->getEncryptedFieldsMapForClass(PatientRecord::class);
195+
}
196+
166197
private function createMetadataFactory(ClassMetadataFactoryInterface $classMetadataFactory, string ...$className): ClassMetadataFactoryInterface
167198
{
168199
return new class ($classMetadataFactory, $className) extends AbstractClassMetadataFactory implements ClassMetadataFactoryInterface

0 commit comments

Comments
 (0)