Skip to content

[Encryption] Add support for Field-Level Automatic and Queryable Encryption#2759

Merged
GromNaN merged 29 commits intodoctrine:feature/queryable-encryptionfrom
GromNaN:fle
Jun 12, 2025
Merged

[Encryption] Add support for Field-Level Automatic and Queryable Encryption#2759
GromNaN merged 29 commits intodoctrine:feature/queryable-encryptionfrom
GromNaN:fle

Conversation

@GromNaN
Copy link
Member

@GromNaN GromNaN commented Apr 25, 2025

Q A
Type feature
BC Break no
Fixed issues PHPORM-317

Summary

⚠️ This feature requires a MongoDB Atlas or Enterprise license.

Client-Side Field Level Encryption (CSFLE) is a feature that enables you to encrypt data in your application before you send it over the network to MongoDB. With CSFLE enabled, no MongoDB product has access to your data in an unencrypted form.

Queryable Encryption gives you the ability to perform the following tasks:

  • Encrypt sensitive data fields from the client-side.
  • Store sensitive data fields as fully randomized encrypted data on the database server-side.
  • Run expressive queries on the encrypted data.

Implementation:

The encrypted collection must be created using the schema manager (odm:schema:create command)

@GromNaN GromNaN changed the base branch from 2.11.x to 2.12.x April 28, 2025 20:08
@GromNaN GromNaN force-pushed the fle branch 2 times, most recently from a6ec9ed to 81f3e90 Compare April 30, 2025 14:46
@GromNaN GromNaN changed the base branch from 2.12.x to feature/queryable-encryption May 5, 2025 13:48
Copy link
Member

@alcaeus alcaeus left a comment

Choose a reason for hiding this comment

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

Left some questions, and phpstan seems to be unhappy about some of the mapping types.

The logic and tests for it look solid.

<xs:element name="field" type="odm:field" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="embed-one" type="odm:embed-one" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="embed-many" type="odm:embed-many" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="encrypt" type="odm:encrypt-field" minOccurs="0" maxOccurs="1" />
Copy link
Member

Choose a reason for hiding this comment

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

Why is this an element instead of an attribute? Is it to future-proof this in case there will be options for encrypting entire documents in future?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I plan to add a "key" attribute to the element.

Copy link
Member Author

Choose a reason for hiding this comment

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

As discussed, the "key" don't need to be configured with queryable encryption. I keep the distinct XML attribute for extensibility.

$this->dm->getClientEncryption(),
$this->dm->getConfiguration()->getKmsProvider(),
null, // @todo when is it necessary to set the master key?
$options,
Copy link
Member

Choose a reason for hiding this comment

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

Should you be using $this->getWriteOptions here like below and add the encrypted field config?

@GromNaN GromNaN force-pushed the fle branch 5 times, most recently from f67e094 to 18d7c18 Compare May 22, 2025 13:56
Comment on lines +322 to +328
'min', 'max' => match ($mapping['type']) {
Type::INT => (int) $encryptValue,
Type::FLOAT => (float) $encryptValue,
Type::DECIMAL128 => new Decimal128((string) $encryptValue),
Type::DATE, Type::DATE_IMMUTABLE => new UTCDateTime(new DateTimeImmutable((string) $encryptValue)),
default => null, // Invalid
},
Copy link
Member Author

Choose a reason for hiding this comment

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

I've added the transformation of values from string to native BSON types here; should use the Type::convertToDatabaseValue() inside EncryptionFieldMap.

GromNaN added 12 commits May 26, 2025 14:18
…octrine#2773)

CommandSucceededEvent|CommandFailedEvent::getServer() has been deprecated in ext-mongodb 1.20 and removed in 2.0.0.
For backward compatibility: we keep this method in MongoDB ODM, but throw an exception the extension is not compatible. I'm not adding a trigger_deprecation as this is already triggered by the extension method call.

The new methods getPort() and getHost() are added as proxy to extension methods that are always available in the required ext-mongodb 1.21+.

This is not covered by tests, I can add tests on CommandLogger before merging.
* CursorInterface always extends Traversable, no need to assert on SPL Iterator
* Remove duplicate assert
@GromNaN GromNaN marked this pull request as ready for review June 12, 2025 12:27
@GromNaN GromNaN requested a review from alcaeus June 12, 2025 12:40
$class->getCollection(),
$this->dm->getClientEncryption(),
$this->dm->getConfiguration()->getKmsProvider(),
null, // @todo when is it necessary to set the master key?
Copy link
Member

Choose a reason for hiding this comment

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

To leave a quick answer, some KMS providers require a master key (e.g. AWS)

new Command(['buildInfo' => 1]),
)->toArray()[0];

if (! in_array('enterprise', $buildInfo->modules ?? [])) {
Copy link
Member

Choose a reason for hiding this comment

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

I noticed that atlas-local does not expose an enterprise module, so we may want to remove this check before wrapping up the feature.

@GromNaN GromNaN merged commit 72ee408 into doctrine:feature/queryable-encryption Jun 12, 2025
20 checks passed
@GromNaN GromNaN changed the title Add support for Field-Level Automatic and Queryable Encryption [Encryption] Add support for Field-Level Automatic and Queryable Encryption Jul 29, 2025
@GromNaN GromNaN deleted the fle branch July 30, 2025 16:12
GromNaN added a commit that referenced this pull request Aug 11, 2025
* Add test document classes from tutorial

https://github.com/mongodb/docs/blob/master/source/includes/qe-tutorials/node/queryable-encryption-tutorial.js

* Create encryption field map

* Create encrypted collection

* Use promoted properties for encrypted document tests

* Revert changes in BaseTestCase

* Rename EncryptionFieldMapTest

* Create Configuration::getDriverOptions() to create the client

* Support class-level #[Encrypt] attribute

* Skip QE tests on non-supported server configuration

* Add documentation on #[Encrypt] attribute

* Add XML mapping for "encrypt" tag

* Create specific xsd type for embedded documents to enable <encrypt> tag

* Fix import class Throwable

* Fix access to $version property before initialization

* Use an enum for query type

* Make getClientEncryption internal

* Improve type of min/max bounds for range queries

* Use getWriteOptions with createEncryptedCollection

* Use random local master key

* Add assertion on non-decrypted data

* Ignore all DOCTRINE_MONGODB_DATABASE phpstan errors

* Baseline phpstan

* Fix CS and skip phpstan issues
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants