Skip to content

Commit af57fa0

Browse files
committed
[Encryption] Fix format of encryptedFieldsMaps in the autoEncryption configuration (#905)
* Fix format of encryptedFieldsMaps in the configuration * EncryptedFieldsMaps loaded from a JSON string from XML configuration * Enable client configuration for tests - partial
1 parent 76e9684 commit af57fa0

File tree

10 files changed

+347
-139
lines changed

10 files changed

+347
-139
lines changed

config/schema/mongodb-1.0.xsd

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -179,26 +179,9 @@
179179
<xsd:attribute name="tlsDisableOCSPEndpointCheck" type="xsd:boolean" use="optional" />
180180
</xsd:complexType>
181181

182-
<xsd:complexType name="encrypted-fields-map">
183-
<xsd:sequence>
184-
<xsd:element name="encryptedFields" type="encrypted-fields" minOccurs="0" maxOccurs="unbounded" />
185-
</xsd:sequence>
186-
</xsd:complexType>
187-
188-
<xsd:complexType name="encrypted-fields">
189-
<xsd:sequence>
190-
<xsd:element name="field" type="encrypted-field" minOccurs="1" maxOccurs="unbounded" />
191-
</xsd:sequence>
192-
<xsd:attribute name="name" type="xsd:string" use="required" />
193-
</xsd:complexType>
194-
195-
<xsd:complexType name="encrypted-field">
196-
<xsd:sequence>
197-
<xsd:element name="queries" type="encrypted-queries" minOccurs="0" maxOccurs="1" />
198-
</xsd:sequence>
199-
<xsd:attribute name="path" type="xsd:string" use="required" />
200-
<xsd:attribute name="bsonType" type="xsd:string" use="required" />
201-
</xsd:complexType>
182+
<xsd:simpleType name="encrypted-fields-map">
183+
<xsd:restriction base="xsd:string"/>
184+
</xsd:simpleType>
202185

203186
<xsd:complexType name="encrypted-queries">
204187
<xsd:attribute name="queryType" type="xsd:string" use="required" />

docs/encryption.rst

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ This setting is **recommended** for improved security and performance.
9292
specified collection, the client downloads the server-side remote schema for
9393
the collection and uses it instead.
9494

95-
For more details, see the official MongoDB documentation: `Encrypted Fields and Enabled Queries <https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/encrypt-and-query/>`_.
95+
For more details, see the official MongoDB documentation:
96+
`Encrypted Fields and Enabled Queries <https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/encrypt-and-query/>`_.
97+
98+
Note that there is no ``fields`` key in the configuration of each collection
99+
for the bundle configuration. Instead, you directly specify the list of
100+
encrypted fields as an array under the collection namespace.
96101

97102
.. tabs::
98103

@@ -106,9 +111,9 @@ For more details, see the official MongoDB documentation: `Encrypted Fields and
106111
autoEncryption:
107112
encryptedFieldsMap:
108113
"mydatabase.mycollection":
109-
fields:
110-
- path: "sensitive_field"
111-
bsonType: "string"
114+
- keyId: { $binary: { base64: 2CSosXLSTEKaYphcSnUuCw==, subType: '04' } }
115+
path: "sensitive_field"
116+
bsonType: "string"
112117
113118
.. group-tab:: XML
114119

@@ -117,9 +122,15 @@ For more details, see the official MongoDB documentation: `Encrypted Fields and
117122
<doctrine:connection>
118123
<doctrine:autoEncryption>
119124
<doctrine:encryptedFieldsMap>
120-
<doctrine:encryptedFields name="mydatabase.mycollection">
121-
<doctrine:field path="sensitive_field" bsonType="string" />
122-
</doctrine:encryptedFields>
125+
<![CDATA[
126+
{
127+
"mydatabase.mycollection": [
128+
"keyId": { "$binary": { "base64": "2CSosXLSTEKaYphcSnUuCw==", "subType": "04" } },
129+
"path": "sensitive_field",
130+
"bsonType": "string"
131+
]
132+
}
133+
]]>
123134
</doctrine:encryptedFieldsMap>
124135
</doctrine:autoEncryption>
125136
</doctrine:connection>
@@ -135,11 +146,10 @@ For more details, see the official MongoDB documentation: `Encrypted Fields and
135146
->autoEncryption([
136147
'encryptedFieldsMap' => [
137148
'mydatabase.mycollection' => [
138-
'fields' => [
139-
[
140-
'path' => 'sensitive_field',
141-
'bsonType' => 'string',
142-
],
149+
[
150+
'path' => 'sensitive_field',
151+
'keyId' => ['$binary' => ['base64' => '2CSosXLSTEKaYphcSnUuCw==', 'subType' => '04' ] ],
152+
'bsonType' => 'string',
143153
],
144154
],
145155
],

phpunit.xml.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<!-- <server name="DOCTRINE_MONGODB_ODM" value="/path/to/doctrine-mongodb-odm/lib" /> -->
2121
<!-- <server name="DOCTRINE_MONGODB" value="/path/to/doctrine-mongodb/lib" /> -->
2222
<!-- <server name="DOCTRINE_COMMON" value="/path/to/doctrine-common/lib" /> -->
23+
<env name="DOCTRINE_MONGODB_SERVER" value="mongodb://localhost:27017"/>
2324
<!-- Allow 1 direct deprecation until https://github.com/doctrine/DoctrineMongoDBBundle/pull/675 is merged -->
2425
<env name="SYMFONY_DEPRECATIONS_HELPER" value="max[direct]=1"/>
2526
</php>

src/Command/DumpEncryptedFieldsMapCommand.php

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Doctrine\ODM\MongoDB\DocumentManager;
88
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
9-
use Doctrine\ODM\MongoDB\Utility\EncryptedFieldsMapGenerator;
9+
use MongoDB\BSON\PackedArray;
1010
use Symfony\Component\Console\Attribute\AsCommand;
1111
use Symfony\Component\Console\Command\Command;
1212
use Symfony\Component\Console\Input\InputInterface;
@@ -16,10 +16,7 @@
1616
use Symfony\Component\Yaml\Dumper;
1717
use Symfony\Contracts\Service\ServiceCollectionInterface;
1818

19-
use function array_combine;
20-
use function array_keys;
21-
use function array_map;
22-
use function array_values;
19+
use function json_decode;
2320
use function json_encode;
2421
use function sprintf;
2522
use function var_export;
@@ -63,24 +60,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6360
$dumper = new Dumper();
6461

6562
foreach ($this->documentManagers as $name => $documentManager) {
66-
$generator = new EncryptedFieldsMapGenerator($documentManager->getMetadataFactory());
67-
$encryptedFieldsMap = $generator->getEncryptedFieldsMap();
63+
$encryptedFieldsMap = [];
64+
foreach ($documentManager->getMetadataFactory()->getAllMetadata() as $metadata) {
65+
$database = $documentManager->getDocumentDatabase($metadata->getName());
66+
$collectionInfoIterator = $database->listCollections(['filter' => ['name' => $metadata->getCollection()]]);
67+
68+
foreach ($collectionInfoIterator as $collectionInfo) {
69+
if ($collectionInfo['options']['encryptedFields'] ?? null) {
70+
$encryptedFieldsMap[$this->getDocumentNamespace($metadata, $database->getDatabaseName())] = $collectionInfo['options']['encryptedFields'];
71+
}
72+
}
73+
}
6874

6975
if (empty($encryptedFieldsMap)) {
7076
continue;
7177
}
7278

73-
$encryptedFieldsMap = array_combine(
74-
// Convert class names in keys to their full namespaces
75-
array_map(
76-
fn (string $fqcn): string => $this->getDocumentNamespace(
77-
$documentManager->getClassMetadata($fqcn),
78-
$documentManager->getConfiguration()->getDefaultDB(),
79-
),
80-
array_keys($encryptedFieldsMap),
81-
),
82-
array_values($encryptedFieldsMap),
83-
);
79+
foreach ($encryptedFieldsMap as $ns => $encryptedFields) {
80+
$encryptedFieldsMap[$ns] = json_decode(PackedArray::fromPHP($encryptedFields['fields'])->toRelaxedExtendedJSON(), true);
81+
}
8482

8583
$io->section(sprintf('Dumping encrypted fields map for document manager "%s"', $name));
8684
switch ($format) {

src/DependencyInjection/Configuration.php

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1212
use Symfony\Component\Config\Definition\ConfigurationInterface;
1313

14-
use function array_is_list;
1514
use function count;
1615
use function in_array;
1716
use function is_array;
@@ -20,6 +19,8 @@
2019
use function method_exists;
2120
use function preg_match;
2221

22+
use const JSON_THROW_ON_ERROR;
23+
2324
/**
2425
* FrameworkExtension configuration structure.
2526
*/
@@ -401,20 +402,8 @@ private function addConnectionsSection(ArrayNodeDefinition $rootNode): void
401402
->useAttributeAsKey('name', false)
402403
->beforeNormalization()
403404
->always(static function ($v) {
404-
if (isset($v['encryptedFields']) && is_array($v['encryptedFields'])) {
405-
$encryptedFields = $v['encryptedFields'];
406-
if (! array_is_list($encryptedFields)) {
407-
$encryptedFields = [$encryptedFields];
408-
}
409-
410-
$v = [];
411-
foreach ($encryptedFields as $field) {
412-
if (is_array($field['field'] ?? null) && ! array_is_list($field['field'])) {
413-
$field['field'] = [$field['field']];
414-
}
415-
416-
$v[$field['name'] ?? ''] = $field['field'] ?? [];
417-
}
405+
if (is_string($v)) {
406+
return json_decode($v, true, 512, JSON_THROW_ON_ERROR);
418407
}
419408

420409
return $v;
@@ -424,13 +413,16 @@ private function addConnectionsSection(ArrayNodeDefinition $rootNode): void
424413
->children()
425414
->scalarNode('path')->isRequired()->cannotBeEmpty()->end()
426415
->scalarNode('bsonType')->isRequired()->cannotBeEmpty()->end()
416+
->variableNode('keyId')->isRequired()->cannotBeEmpty()->end()
427417
->arrayNode('queries')
428418
->children()
429419
->scalarNode('queryType')->isRequired()->cannotBeEmpty()->end()
430-
->integerNode('min')->end()
431-
->integerNode('max')->end()
420+
->variableNode('min')->end()
421+
->variableNode('max')->end()
432422
->integerNode('sparsity')->end()
423+
->integerNode('precision')->end()
433424
->integerNode('trimFactor')->end()
425+
->integerNode('contention')->end()
434426
->end()
435427
->end()
436428
->end()

src/DependencyInjection/DoctrineMongoDBExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
2828
use Doctrine\Persistence\Proxy;
2929
use InvalidArgumentException;
30+
use MongoDB\BSON\Document as BsonDocument;
3031
use MongoDB\Client;
3132
use ProxyManager\Proxy\LazyLoadingInterface;
3233
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
@@ -55,6 +56,7 @@
5556
use function in_array;
5657
use function interface_exists;
5758
use function is_dir;
59+
use function json_encode;
5860
use function method_exists;
5961
use function sprintf;
6062

@@ -538,6 +540,12 @@ private function normalizeAutoEncryption(array $autoEncryption, string $defaultD
538540

539541
$autoEncryption['keyVaultNamespace'] ??= $defaultDB . '.datakeys';
540542

543+
if (isset($autoEncryption['encryptedFieldsMap'])) {
544+
foreach ($autoEncryption['encryptedFieldsMap'] as &$value) {
545+
$value = (new Definition(BsonDocument::class))->setFactory([BsonDocument::class, 'fromJSON'])->setArguments([json_encode(['fields' => $value])]);
546+
}
547+
}
548+
541549
return $autoEncryption;
542550
}
543551

0 commit comments

Comments
 (0)