This release adds first-class support for MongoDB Queryable Encryption. Encrypted fields are declared once in the connection configuration, and the MongoDB driver encrypts, decrypts and answers queries on them transparently. Eloquent models keep their plain shape: no attribute, property or cast is needed.
'driver_options' => [
'autoEncryption' => [
'keyVaultNamespace' => 'encryption.__keyVault',
'kmsProviders' => [
'local' => ['key' => base64_encode(file_get_contents(storage_path('encryption/master-key.bin')))],
],
'encryptedFieldsMap' => [
'patients' => [
'fields' => [
// `queryType` and the range options are flat, and a bare
// string declares a randomized, non-queryable field.
'ssn' => ['bsonType' => 'string', 'queryType' => 'equality'],
'billing_amount' => ['bsonType' => 'int', 'queryType' => 'range', 'min' => 0, 'max' => 5000, 'sparsity' => 1],
'billing' => 'object',
],
],
],
],
],The encrypted collection, its metadata collections and indexes, and the missing data keys are created by one command. The same creation is available with Schema::connection('mongodb')->createEncrypted('patients').
php artisan mongodb:encryption:create-collection
php artisan mongodb:encryption:statusQueryable Encryption requires MongoDB 8.0 or later for range queries (equality queries work from 7.0), on a replica set or a sharded cluster, with the Automatic Encryption Shared Library. Queries on encrypted fields cannot be combined with Atlas Search.
Queryable Encryption
- Declare encrypted fields in the connection and create the collection with the Schema DSL or Artisan: by @GromNaN in #3577
- Create every mapped encrypted collection at once, with interactive selection and a
--dry-runpreview: by @GromNaN in #3607 - Coerce the value written to an encrypted field to the declared BSON type, and derive the matching Eloquent cast: by @GromNaN in #3645
- Hide the reserved
__safeContent__field from hydrated models and reject writes to it: by @GromNaN in #3652 - Report a mapped collection that exists without the declared encrypted fields, and a collection whose fields differ from the configuration: by @GromNaN in #3653
- Key the
encryptedFieldsMapby namespace before handing it to the driver. The map was keyed by collection name, so automatic encryption ignored it and a write to a mapped collection that the server did not know as encrypted stored the fields in plaintext: by @GromNaN in #3657 - Rotate the master key of the data keys with
mongodb:encryption:rewrap-data-keys: by @paulinevos in #3602 - Forward
tlsOptionstoClientEncryption, for a KMS reachable only through a private CA: by @GromNaN in #3606 - Report an unreachable server from
mongodb:encryption:status: by @zigzagdev in #3624 - Keep the
ConnectionAPI small. OnlyisAutoEncryptionEnabled()andgetClientEncryption()are public, for diagnostics and forencryptExpression(). The rest of the encryption logic stays internal: by @GromNaN in #3660
Breaking changes
- Apply the connection prefix in the schema methods inherited from Laravel.
hasTable(),dropIfExists(),getColumns()andgetIndexes()now apply the prefix, as Laravel does. The migration repository relies onhasTable(), so with a prefix every migration ran again. An application that calls these methods with the real collection name must pass the unprefixed name.hasCollection()andgetCollection()are not Laravel methods and keep taking the real name: by @GromNaN in #3655
Fixed
- Reject the operators the date-based
wheremethods cannot compile: by @zigzagdev in #3612 - Preserve the position of renamed keys in query results: by @paulinevos in #3641
- Keep the exact value of a double used as a relation key: by @GromNaN in #3658
- Read an attribute masked by a set-only
Attributemutator: by @GromNaN in #3557 - Replay query macros, constraints and eager loads when eager loading
MorphTorelations, somorphWith(),morphWithCount()andconstrain()are no longer no-ops: by @GromNaN in #3559 - Handle a null value in
count(): by @MaddipatlaChetan24 in #3586 - Report MongoDB query events to listeners such as Telescope and Debugbar without enabling the query log: by @GromNaN in #3646
Security hardening
- Deny the server-side JavaScript operators
$where,$functionand$accumulatorinDatabaseQuery, including nested in filters, projections,letand aggregation stages: by @paulinevos in #3651 - Verify that the class resolved from a
morphTotype is an Eloquent model before instantiating it, on both the lazy and the eager loading paths: by @paulinevos in #3589
Internal and CI
- Replace
Database::selectCollection()withgetCollection(): by @zigzagdev in #3630 - Fix the Eloquent builder inheritance, so PHPStan and Larastan resolve the MongoDB builder macros: by @Wirone in #3632
- Scope the static analysis caches per PHP version and driver: by @paulinevos in #3575
- Fix the
MongoBatchRepositoryTestmock for Laravel 13.32 named arguments: by @GromNaN in #3583 - Use static methods in
AtlasSearchTest: by @MaddipatlaChetan24 in #3582 - Verify that
transactionLevel()reflects the active transaction: by @zigzagdev in #3572 - Fix user creation in
TransactionTest: by @MaddipatlaChetan24 in #3600 - Add a label reminder to the pull request template: by @paulinevos in #3599
- Test
loadSum,loadAvg,loadMinandloadMaxon relations: by @GromNaN in #3566 - Fix the flaky Atlas CI startup and the flaky
DatabaseFailedJobProviderTest: by @GromNaN in #3609
New Contributors
- @MaddipatlaChetan24 made their first contribution in #3582
- @Wirone made their first contribution in #3632
- @kevinAlbs made their first contribution in #3661
Full Changelog: 5.11.0...5.12.0