Skip to content
Merged
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
62 changes: 39 additions & 23 deletions docs/encryption.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,37 @@ Example of configuration for AWS
Encrypted Fields Map
--------------------

You can configure which fields are encrypted in each collection by specifying the
The encrypted fields are set to the collection when you create it, and the MongoDB
client will query the server for the collection schema before performing any
operations. For additional security, you can also specify the encrypted fields
in the connection configuration, which allows the client to use local rules
instead of downloading the remote schema from the server, that could potentially
be tampered with if an attacker compromises the server.

The Encrypted Fields Maps is a list of all encrypted fields associated with all
the collection namespaces that has encryption enabled. To configure it, you
can run a command that extract the encrypted fields from the server and generate
the ``encryptedFieldsMap`` configuration.

.. code-block:: console

php bin/console doctrine:mongodb:dump-encrypted-fields-map --format yaml

The output of the command will be a YAML configuration for the
``autoEncryption.encryptedFieldsMap`` option in the connection configuration.
This setting is **recommended** for improved security and performance.

- If the connection ``encryptedFieldsMap`` object contains a key for the specified
collection, the client uses that object to perform automatic Queryable Encryption,
rather than using the remote schema. At minimum, the local rules must encrypt
all fields that the remote schema does.
collection namespace, the client uses that object to perform automatic
Queryable Encryption, rather than using the remote schema. At minimum, the
local rules must encrypt all fields that the remote schema does.

- If the connection ``encryptedFieldsMap`` object doesn't contain a key for the
specified collection, the client downloads the server-side remote schema for
the collection and uses it instead.
specified collection namespace, the client downloads the server-side remote
schema for the collection and uses it instead.

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/>`_.

Note that there is no ``fields`` key in the configuration of each collection
for the bundle configuration. Instead, you directly specify the list of
encrypted fields as an array under the collection namespace.

.. tabs::

.. group-tab:: YAML
Expand All @@ -111,9 +122,10 @@ encrypted fields as an array under the collection namespace.
autoEncryption:
encryptedFieldsMap:
"mydatabase.mycollection":
- keyId: { $binary: { base64: 2CSosXLSTEKaYphcSnUuCw==, subType: '04' } }
path: "sensitive_field"
bsonType: "string"
fields:
- keyId: { $binary: { base64: 2CSosXLSTEKaYphcSnUuCw==, subType: '04' } }
path: "sensitive_field"
bsonType: "string"

.. group-tab:: XML

Expand All @@ -124,11 +136,13 @@ encrypted fields as an array under the collection namespace.
<doctrine:encryptedFieldsMap>
<![CDATA[
{
"mydatabase.mycollection": [
"keyId": { "$binary": { "base64": "2CSosXLSTEKaYphcSnUuCw==", "subType": "04" } },
"path": "sensitive_field",
"bsonType": "string"
]
"mydatabase.mycollection": {
fields: [
"keyId": { "$binary": { "base64": "2CSosXLSTEKaYphcSnUuCw==", "subType": "04" } },
"path": "sensitive_field",
"bsonType": "string"
]
}
}
]]>
</doctrine:encryptedFieldsMap>
Expand All @@ -146,10 +160,12 @@ encrypted fields as an array under the collection namespace.
->autoEncryption([
'encryptedFieldsMap' => [
'mydatabase.mycollection' => [
[
'path' => 'sensitive_field',
'keyId' => ['$binary' => ['base64' => '2CSosXLSTEKaYphcSnUuCw==', 'subType' => '04' ] ],
'bsonType' => 'string',
'fields' => [
[
'path' => 'sensitive_field',
'keyId' => ['$binary' => ['base64' => '2CSosXLSTEKaYphcSnUuCw==', 'subType' => '04' ] ],
'bsonType' => 'string',
],
],
],
],
Expand Down
7 changes: 3 additions & 4 deletions src/Command/DumpEncryptedFieldsMapCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

foreach ($encryptedFieldsMap as $ns => $encryptedFields) {
$encryptedFieldsMap[$ns] = json_decode(PackedArray::fromPHP($encryptedFields['fields'])->toRelaxedExtendedJSON(), true);
// Keep only the "fields" key and ignore "escCollection" and "ecocCollection"
$encryptedFieldsMap[$ns] = ['fields' => json_decode(PackedArray::fromPHP($encryptedFields['fields'])->toRelaxedExtendedJSON(), true)];
}

$io->section(sprintf('Dumping encrypted fields map for document manager "%s"', $name));
Expand Down Expand Up @@ -105,9 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

private function getDocumentNamespace(ClassMetadata $metadata, string $defaultDb): string
{
$db = $metadata->getDatabase();
$db = $db ?: $defaultDb;
$db = $db ?: 'doctrine';
$db = $metadata->getDatabase() ?: $defaultDb ?: 'doctrine';

return $db . '.' . $metadata->getCollection();
}
Expand Down
30 changes: 17 additions & 13 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -407,20 +407,24 @@ private function addConnectionsSection(ArrayNodeDefinition $rootNode): void
return $v;
})->end()
->prototype('array')
->prototype('array')
->children()
->scalarNode('path')->isRequired()->cannotBeEmpty()->end()
->scalarNode('bsonType')->isRequired()->cannotBeEmpty()->end()
->variableNode('keyId')->isRequired()->cannotBeEmpty()->end()
->arrayNode('queries')
->children()
->arrayNode('fields')
->prototype('array')
->children()
->scalarNode('queryType')->isRequired()->cannotBeEmpty()->end()
->variableNode('min')->end()
->variableNode('max')->end()
->integerNode('sparsity')->end()
->integerNode('precision')->end()
->integerNode('trimFactor')->end()
->integerNode('contention')->end()
->scalarNode('path')->isRequired()->cannotBeEmpty()->end()
->scalarNode('bsonType')->isRequired()->cannotBeEmpty()->end()
->variableNode('keyId')->isRequired()->cannotBeEmpty()->end()
->arrayNode('queries')
->children()
->scalarNode('queryType')->isRequired()->cannotBeEmpty()->end()
->variableNode('min')->end()
->variableNode('max')->end()
->integerNode('sparsity')->end()
->integerNode('precision')->end()
->integerNode('trimFactor')->end()
->integerNode('contention')->end()
->end()
->end()
->end()
->end()
->end()
Expand Down
2 changes: 1 addition & 1 deletion src/DependencyInjection/DoctrineMongoDBExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ private function normalizeAutoEncryption(array $autoEncryption, string $defaultD
foreach ($autoEncryption['encryptedFieldsMap'] as &$value) {
// Wrap the encrypted fields in a 'fields' key as required the encryptedFieldsMap structure.
// Some values can be BSON binary, date or numbers, the extended JSON format is used to convert them BSON document.
$value = (new Definition(BsonDocument::class))->setFactory([BsonDocument::class, 'fromJSON'])->setArguments([json_encode(['fields' => $value])]);
$value = (new Definition(BsonDocument::class))->setFactory([BsonDocument::class, 'fromJSON'])->setArguments([json_encode($value)]);
}
}

Expand Down
Loading