github mongodb/laravel-mongodb 5.12.0

4 hours ago

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:status

Queryable 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-run preview: 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 encryptedFieldsMap by 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 tlsOptions to ClientEncryption, 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 Connection API small. Only isAutoEncryptionEnabled() and getClientEncryption() are public, for diagnostics and for encryptExpression(). 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() and getIndexes() now apply the prefix, as Laravel does. The migration repository relies on hasTable(), 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() and getCollection() are not Laravel methods and keep taking the real name: by @GromNaN in #3655

Fixed

  • Reject the operators the date-based where methods 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 Attribute mutator: by @GromNaN in #3557
  • Replay query macros, constraints and eager loads when eager loading MorphTo relations, so morphWith(), morphWithCount() and constrain() 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, $function and $accumulator in DatabaseQuery, including nested in filters, projections, let and aggregation stages: by @paulinevos in #3651
  • Verify that the class resolved from a morphTo type 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() with getCollection(): 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 MongoBatchRepositoryTest mock 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, loadMin and loadMax on relations: by @GromNaN in #3566
  • Fix the flaky Atlas CI startup and the flaky DatabaseFailedJobProviderTest: by @GromNaN in #3609

New Contributors

Full Changelog: 5.11.0...5.12.0

Don't miss a new laravel-mongodb release

NewReleases is sending notifications on new releases.