Skip to content

Commit dc21a08

Browse files
authored
PYTHON-3300 Add Explicit Queryable Encryption Example to Docs (#973)
1 parent 43c2062 commit dc21a08

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

doc/examples/encryption.rst

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,142 @@ Automatic encryption in Queryable Encryption is configured with an ``encrypted_f
409409
In the above example, the ``firstName`` and ``lastName`` fields are
410410
automatically encrypted and decrypted.
411411

412+
Explicit Queryable Encryption (Beta)
413+
````````````````````````````````````
414+
415+
PyMongo 4.2 brings beta support for Queryable Encryption with MongoDB 6.0.
416+
417+
Queryable Encryption is the second version of Client-Side Field Level Encryption.
418+
Data is encrypted client-side. Queryable Encryption supports indexed encrypted fields,
419+
which are further processed server-side.
420+
421+
You must have MongoDB 6.0rc8+ to preview the capability.
422+
423+
Until PyMongo 4.2 release is finalized, it can be installed using::
424+
425+
pip install "pymongo@git+ssh://[email protected]/mongodb/[email protected]#egg=pymongo[encryption]"
426+
427+
Additionally, ``libmongocrypt`` must be installed from `source <https://github.com/mongodb/libmongocrypt/blob/master/bindings/python/README.rst#installing-from-source>`_.
428+
429+
Explicit encryption in Queryable Encryption is performed using the ``encrypt`` and ``decrypt``
430+
methods. Automatic encryption (to allow the ``find_one`` to automatically decrypt) is configured
431+
using an ``encrypted_fields`` mapping, as demonstrated by the following example::
432+
433+
import os
434+
435+
from pymongo import MongoClient
436+
from pymongo.encryption import (Algorithm, AutoEncryptionOpts,
437+
ClientEncryption, QueryType)
438+
439+
440+
def main():
441+
# This must be the same master key that was used to create
442+
# the encryption key.
443+
local_master_key = os.urandom(96)
444+
kms_providers = {"local": {"key": local_master_key}}
445+
446+
# The MongoDB namespace (db.collection) used to store
447+
# the encryption data keys.
448+
key_vault_namespace = "encryption.__pymongoTestKeyVault"
449+
key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)
450+
451+
# Set up the key vault (key_vault_namespace) for this example.
452+
client = MongoClient()
453+
key_vault = client[key_vault_db_name][key_vault_coll_name]
454+
455+
# Ensure that two data keys cannot share the same keyAltName.
456+
key_vault.drop()
457+
key_vault.create_index(
458+
"keyAltNames",
459+
unique=True,
460+
partialFilterExpression={"keyAltNames": {"$exists": True}})
461+
462+
client_encryption = ClientEncryption(
463+
kms_providers,
464+
key_vault_namespace,
465+
# The MongoClient to use for reading/writing to the key vault.
466+
# This can be the same MongoClient used by the main application.
467+
client,
468+
# The CodecOptions class used for encrypting and decrypting.
469+
# This should be the same CodecOptions instance you have configured
470+
# on MongoClient, Database, or Collection.
471+
client.codec_options)
472+
473+
# Create a new data key for the encryptedField.
474+
indexed_key_id = client_encryption.create_data_key(
475+
'local')
476+
unindexed_key_id = client_encryption.create_data_key(
477+
'local')
478+
479+
encrypted_fields = {
480+
"escCollection": "enxcol_.default.esc",
481+
"eccCollection": "enxcol_.default.ecc",
482+
"ecocCollection": "enxcol_.default.ecoc",
483+
"fields": [
484+
{
485+
"keyId": indexed_key_id,
486+
"path": "encryptedIndexed",
487+
"bsonType": "string",
488+
"queries": {
489+
"queryType": "equality"
490+
}
491+
},
492+
{
493+
"keyId": unindexed_key_id,
494+
"path": "encryptedUnindexed",
495+
"bsonType": "string",
496+
}
497+
]
498+
}
499+
500+
opts = AutoEncryptionOpts(
501+
{"local": {"key": local_master_key}},
502+
key_vault.full_name,
503+
bypass_query_analysis=True,
504+
key_vault_client=client,
505+
)
506+
507+
# The MongoClient used to read/write application data.
508+
encrypted_client = MongoClient(auto_encryption_opts=opts)
509+
encrypted_client.drop_database("test")
510+
db = encrypted_client.test
511+
512+
# Create the collection with encrypted fields.
513+
coll = db.create_collection("coll", encrypted_fields=encrypted_fields)
514+
515+
# Create and encrypt an indexed and unindexed value.
516+
val = "encrypted indexed value"
517+
unindexed_val = "encrypted unindexed value"
518+
insert_payload_indexed = client_encryption.encrypt(val, Algorithm.INDEXED, indexed_key_id)
519+
insert_payload_unindexed = client_encryption.encrypt(unindexed_val, Algorithm.UNINDEXED,
520+
unindexed_key_id)
521+
522+
# Insert the payloads.
523+
coll.insert_one({
524+
"encryptedIndexed": insert_payload_indexed,
525+
"encryptedUnindexed": insert_payload_unindexed
526+
})
527+
528+
# Encrypt our find payload using QueryType.EQUALITY.
529+
# The value of "data_key_id" must be the same as used to encrypt the values
530+
# above.
531+
find_payload = client_encryption.encrypt(
532+
val, Algorithm.INDEXED, indexed_key_id, query_type=QueryType.EQUALITY
533+
)
534+
535+
# Find the document we inserted using the encrypted payload.
536+
# The returned document is automatically decrypted.
537+
doc = coll.find_one({"encryptedIndexed": find_payload})
538+
print('Returned document: %s' % (doc,))
539+
540+
# Cleanup resources.
541+
client_encryption.close()
542+
encrypted_client.close()
543+
544+
545+
if __name__ == "__main__":
546+
main()
547+
412548
.. _explicit-client-side-encryption:
413549

414550
Explicit Encryption

0 commit comments

Comments
 (0)