async def main(): # This must be the same master key that was used to create # the encryption key. local_master_key = os.urandom(96) kms_providers = {"local": {"key": local_master_key}} # The MongoDB namespace (db.collection) used to store # the encryption data keys. key_vault_namespace = "encryption.__pymongoTestKeyVault" key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1) # bypass_auto_encryption=True disable automatic encryption but keeps # the automatic _decryption_ behavior. bypass_auto_encryption will # also disable spawning mongocryptd. auto_encryption_opts = AutoEncryptionOpts( kms_providers, key_vault_namespace, bypass_auto_encryption=True) client = AsyncIOMotorClient(auto_encryption_opts=auto_encryption_opts) coll = client.test.coll # Clear old data await coll.drop() # Set up the key vault (key_vault_namespace) for this example. key_vault = client[key_vault_db_name][key_vault_coll_name] # Ensure that two data keys cannot share the same keyAltName. await key_vault.drop() await key_vault.create_index( "keyAltNames", unique=True, partialFilterExpression={"keyAltNames": {"$exists": True}}) client_encryption = AsyncIOMotorClientEncryption( kms_providers, key_vault_namespace, # The MotorClient to use for reading/writing to the key vault. # This can be the same MotorClient used by the main application. client, # The CodecOptions class used for encrypting and decrypting. # This should be the same CodecOptions instance you have configured # on MotorClient, Database, or Collection. coll.codec_options) # Create a new data key for the encryptedField. data_key_id = await client_encryption.create_data_key( 'local', key_alt_names=['pymongo_encryption_example_4']) # Explicitly encrypt a field: encrypted_field = await client_encryption.encrypt( "123456789", Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_alt_name='pymongo_encryption_example_4') await coll.insert_one({"encryptedField": encrypted_field}) # Automatically decrypts any encrypted fields. doc = await coll.find_one() print('Decrypted document: %s' % (doc,)) unencrypted_coll = AsyncIOMotorClient().test.coll print('Encrypted document: %s' % (await unencrypted_coll.find_one(),)) # Cleanup resources. await client_encryption.close()
def setUpClass(cls): super(TestBsonSizeBatches, cls).setUpClass() db = client_context.client.db cls.coll = db.coll cls.coll.drop() # Configure the encrypted 'db.coll' collection via jsonSchema. json_schema = json_data('limits', 'limits-schema.json') db.create_collection('coll', validator={'$jsonSchema': json_schema}, codec_options=OPTS, write_concern=WriteConcern(w='majority')) # Create the key vault. coll = client_context.client.get_database( 'keyvault', write_concern=WriteConcern(w='majority'), codec_options=OPTS)['datakeys'] coll.drop() coll.insert_one(json_data('limits', 'limits-key.json')) opts = AutoEncryptionOpts({'local': { 'key': LOCAL_MASTER_KEY }}, 'keyvault.datakeys') cls.listener = OvertCommandListener() cls.client_encrypted = rs_or_single_client( auto_encryption_opts=opts, event_listeners=[cls.listener]) cls.coll_encrypted = cls.client_encrypted.db.coll
def test_auto_encrypt_local_schema_map(self): # Configure the encrypted field via the local schema_map option. schemas = {'pymongo_test.test': json_data('custom', 'schema.json')} opts = AutoEncryptionOpts( KMS_PROVIDERS, 'admin.datakeys', schema_map=schemas) self._test_auto_encrypt(opts)
def test_corpus_local_schema(self): # Configure the encrypted field via the local schema_map option. schemas = {'db.coll': self.fix_up_schema( json_data('corpus', 'corpus-schema.json'))} opts = AutoEncryptionOpts( self.kms_providers(), 'admin.datakeys', schema_map=schemas) self._test_corpus(opts)
def test_auto_encrypt(self): # Configure the encrypted field via jsonSchema. json_schema = json_data('custom', 'schema.json') create_with_schema(self.db.test, json_schema) self.addCleanup(self.db.test.drop) opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys') self._test_auto_encrypt(opts)
def get_csfle_enabled_client(self, schema): return MongoClient( self.connection_string, auto_encryption_opts=AutoEncryptionOpts( self.kms_provider, self.key_vault_namespace, mongocryptd_bypass_spawn=self.mongocryptd_bypass_spawn, mongocryptd_spawn_path=self.mongocryptd_spawn_path, schema_map=schema))
def test_auto_encrypt(self): # Configure the encrypted field via jsonSchema. json_schema = json_data('custom', 'schema.json') coll = self.db.create_collection( 'test', validator={'$jsonSchema': json_schema}, codec_options=OPTS) self.addCleanup(coll.drop) opts = AutoEncryptionOpts(KMS_PROVIDERS, 'admin.datakeys') self._test_auto_encrypt(opts)
def __init__(self, kms, schema): key_vault_namespace = "encryption.__keyVault" fle_opts = AutoEncryptionOpts(kms, key_vault_namespace, schema_map=schema, **{}) self.client = MongoClient(mongo_url, auto_encryption_opts=fle_opts) self.collection = self.client[db][collection] print(f'encrypted connection initiated. \n')
def test_use_after_close(self): opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys') client = rs_or_single_client(auto_encryption_opts=opts) self.addCleanup(client.close) client.admin.command('isMaster') client.close() with self.assertRaisesRegex(InvalidOperation, 'Cannot use MongoClient after close'): client.admin.command('isMaster')
def _test_external_key_vault(self, with_external_key_vault): self.client.db.coll.drop() vault = create_key_vault(self.client.keyvault.datakeys, json_data('corpus', 'corpus-key-local.json'), json_data('corpus', 'corpus-key-aws.json')) self.addCleanup(vault.drop) # Configure the encrypted field via the local schema_map option. schemas = {'db.coll': json_data('external', 'external-schema.json')} if with_external_key_vault: key_vault_client = rs_or_single_client(username='******', password='******') self.addCleanup(key_vault_client.close) else: key_vault_client = client_context.client opts = AutoEncryptionOpts(self.kms_providers(), 'keyvault.datakeys', schema_map=schemas, key_vault_client=key_vault_client) client_encrypted = rs_or_single_client(auto_encryption_opts=opts, uuidRepresentation='standard') self.addCleanup(client_encrypted.close) client_encryption = ClientEncryption(self.kms_providers(), 'keyvault.datakeys', key_vault_client, OPTS) self.addCleanup(client_encryption.close) if with_external_key_vault: # Authentication error. with self.assertRaises(EncryptionError) as ctx: client_encrypted.db.coll.insert_one({"encrypted": "test"}) # AuthenticationFailed error. self.assertIsInstance(ctx.exception.cause, OperationFailure) self.assertEqual(ctx.exception.cause.code, 18) else: client_encrypted.db.coll.insert_one({"encrypted": "test"}) if with_external_key_vault: # Authentication error. with self.assertRaises(EncryptionError) as ctx: client_encryption.encrypt( "test", Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=LOCAL_KEY_ID) # AuthenticationFailed error. self.assertIsInstance(ctx.exception.cause, OperationFailure) self.assertEqual(ctx.exception.cause.code, 18) else: client_encryption.encrypt( "test", Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=LOCAL_KEY_ID)
def parse_auto_encrypt_opts(self, opts): """Parse clientOptions.autoEncryptOpts.""" opts = camel_to_snake_args(opts) kms_providers = opts['kms_providers'] if 'aws' in kms_providers: kms_providers['aws'] = AWS_CREDS if not any(AWS_CREDS.values()): self.skipTest('AWS environment credentials are not set') if 'key_vault_namespace' not in opts: opts['key_vault_namespace'] = 'keyvault.datakeys' opts = dict(opts) return AutoEncryptionOpts(**opts)
def test_init_spawn_args(self): # User can override idleShutdownTimeoutSecs opts = AutoEncryptionOpts( {}, 'admin.datakeys', mongocryptd_spawn_args=['--idleShutdownTimeoutSecs=88']) self.assertEqual( opts._mongocryptd_spawn_args, ['--idleShutdownTimeoutSecs=88']) # idleShutdownTimeoutSecs is added by default opts = AutoEncryptionOpts( {}, 'admin.datakeys', mongocryptd_spawn_args=[]) self.assertEqual( opts._mongocryptd_spawn_args, ['--idleShutdownTimeoutSecs=60']) # Also added when other options are given opts = AutoEncryptionOpts( {}, 'admin.datakeys', mongocryptd_spawn_args=['--quiet', '--port=27020']) self.assertEqual( opts._mongocryptd_spawn_args, ['--quiet', '--port=27020', '--idleShutdownTimeoutSecs=60'])
def test_init(self): opts = AutoEncryptionOpts({}, 'keyvault.datakeys') self.assertEqual(opts._kms_providers, {}) self.assertEqual(opts._key_vault_namespace, 'keyvault.datakeys') self.assertEqual(opts._key_vault_client, None) self.assertEqual(opts._schema_map, None) self.assertEqual(opts._bypass_auto_encryption, False) self.assertEqual(opts._mongocryptd_uri, 'mongodb://localhost:27020') self.assertEqual(opts._mongocryptd_bypass_spawn, False) self.assertEqual(opts._mongocryptd_spawn_path, 'mongocryptd') self.assertEqual(opts._mongocryptd_spawn_args, ['--idleShutdownTimeoutSecs=60'])
async def main(): # The MongoDB namespace (db.collection) used to store the # encrypted documents in this example. encrypted_namespace = "test.coll" # This must be the same master key that was used to create # the encryption key. local_master_key = os.urandom(96) kms_providers = {"local": {"key": local_master_key}} # The MongoDB namespace (db.collection) used to store # the encryption data keys. key_vault_namespace = "encryption.__pymongoTestKeyVault" key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1) # The MotorClient used to access the key vault (key_vault_namespace). key_vault_client = AsyncIOMotorClient() key_vault = key_vault_client[key_vault_db_name][key_vault_coll_name] # Ensure that two data keys cannot share the same keyAltName. await key_vault.drop() await key_vault.create_index( "keyAltNames", unique=True, partialFilterExpression={"keyAltNames": { "$exists": True }}) await create_json_schema_file(kms_providers, key_vault_namespace, key_vault_client) # Load the JSON Schema and construct the local schema_map option. with open('jsonSchema.json', 'r') as file: json_schema_string = file.read() json_schema = json_util.loads(json_schema_string) schema_map = {encrypted_namespace: json_schema} auto_encryption_opts = AutoEncryptionOpts(kms_providers, key_vault_namespace, schema_map=schema_map) client = AsyncIOMotorClient(auto_encryption_opts=auto_encryption_opts) db_name, coll_name = encrypted_namespace.split(".", 1) coll = client[db_name][coll_name] # Clear old data await coll.drop() await coll.insert_one({"encryptedField": "123456789"}) decrypted_doc = await coll.find_one() print('Decrypted document: %s' % (decrypted_doc, )) unencrypted_coll = AsyncIOMotorClient()[db_name][coll_name] encrypted_doc = await unencrypted_coll.find_one() print('Encrypted document: %s' % (encrypted_doc, ))
def test_raise_max_wire_version_error(self): opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys') client = rs_or_single_client(auto_encryption_opts=opts) self.addCleanup(client.close) msg = 'Auto-encryption requires a minimum MongoDB version of 4.2' with self.assertRaisesRegex(ConfigurationError, msg): client.test.test.insert_one({}) with self.assertRaisesRegex(ConfigurationError, msg): client.admin.command('isMaster') with self.assertRaisesRegex(ConfigurationError, msg): client.test.test.find_one({}) with self.assertRaisesRegex(ConfigurationError, msg): client.test.test.bulk_write([InsertOne({})])
def test_views_are_prohibited(self): self.client.db.view.drop() self.client.db.create_collection('view', viewOn='coll') self.addCleanup(self.client.db.view.drop) opts = AutoEncryptionOpts(self.kms_providers(), 'keyvault.datakeys') client_encrypted = rs_or_single_client(auto_encryption_opts=opts, uuidRepresentation='standard') self.addCleanup(client_encrypted.close) with self.assertRaisesRegex(EncryptionError, 'cannot auto encrypt a view'): client_encrypted.db.view.insert_one({})
def test_raise_unsupported_error(self): opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys') client = rs_or_single_client(auto_encryption_opts=opts) self.addCleanup(client.close) msg = 'find_raw_batches does not support auto encryption' with self.assertRaisesRegex(InvalidOperation, msg): client.test.test.find_raw_batches({}) msg = 'aggregate_raw_batches does not support auto encryption' with self.assertRaisesRegex(InvalidOperation, msg): client.test.test.aggregate_raw_batches([]) if client_context.is_mongos: msg = 'Exhaust cursors are not supported by mongos' else: msg = 'exhaust cursors do not support auto encryption' with self.assertRaisesRegex(InvalidOperation, msg): next(client.test.test.find(cursor_type=CursorType.EXHAUST))
def test_init(self): opts = AutoEncryptionOpts({}, 'admin.datakeys') self.assertEqual(opts._kms_providers, {}) self.assertEqual(opts._key_vault_namespace, 'admin.datakeys') self.assertEqual(opts._key_vault_client, None) self.assertEqual(opts._schema_map, None) self.assertEqual(opts._bypass_auto_encryption, False) if hasattr(socket, 'AF_UNIX'): self.assertEqual(opts._mongocryptd_uri, 'mongodb://%2Ftmp%2Fmongocryptd.sock') else: self.assertEqual(opts._mongocryptd_uri, 'mongodb://localhost:27020') self.assertEqual(opts._mongocryptd_bypass_spawn, False) self.assertEqual(opts._mongocryptd_spawn_path, 'mongocryptd') self.assertEqual(opts._mongocryptd_spawn_args, ['--idleShutdownTimeoutSecs=60'])
"lab7.data": { "bsonType": "object", "properties": { "ssn": { "encrypt": { "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", "keyId": [key_uuid] } }, } } } encryption_opts = AutoEncryptionOpts(kms_providers, "lab7.key_vault", schema_map=schema_map, mongocryptd_bypass_spawn=True) encrypted_data = MongoClient(auto_encryption_opts=encryption_opts).lab7.data unencrypted_data = MongoClient().lab7.data encrypted_data.insert_one({ "name": "Doris", "surname": "Jones", "ssn": "457-55-5462" }) encrypted_data.insert_one({ "name": "Ellis", "surname": "Smith", "ssn": "234-45-1111" })
def __init__(self, kms_providers, key_vault_namespace, key_vault_client, codec_options, kms_tls_options=None): """Explicit client-side field level encryption. The ClientEncryption class encapsulates explicit operations on a key vault collection that cannot be done directly on a MongoClient. Similar to configuring auto encryption on a MongoClient, it is constructed with a MongoClient (to a MongoDB cluster containing the key vault collection), KMS provider configuration, and keyVaultNamespace. It provides an API for explicitly encrypting and decrypting values, and creating data keys. It does not provide an API to query keys from the key vault collection, as this can be done directly on the MongoClient. See :ref:`explicit-client-side-encryption` for an example. :Parameters: - `kms_providers`: Map of KMS provider options. The `kms_providers` map values differ by provider: - `aws`: Map with "accessKeyId" and "secretAccessKey" as strings. These are the AWS access key ID and AWS secret access key used to generate KMS messages. An optional "sessionToken" may be included to support temporary AWS credentials. - `azure`: Map with "tenantId", "clientId", and "clientSecret" as strings. Additionally, "identityPlatformEndpoint" may also be specified as a string (defaults to 'login.microsoftonline.com'). These are the Azure Active Directory credentials used to generate Azure Key Vault messages. - `gcp`: Map with "email" as a string and "privateKey" as `bytes` or a base64 encoded string. Additionally, "endpoint" may also be specified as a string (defaults to 'oauth2.googleapis.com'). These are the credentials used to generate Google Cloud KMS messages. - `kmip`: Map with "endpoint" as a host with required port. For example: ``{"endpoint": "example.com:443"}``. - `local`: Map with "key" as `bytes` (96 bytes in length) or a base64 encoded string which decodes to 96 bytes. "key" is the master key used to encrypt/decrypt data keys. This key should be generated and stored as securely as possible. - `key_vault_namespace`: The namespace for the key vault collection. The key vault collection contains all data keys used for encryption and decryption. Data keys are stored as documents in this MongoDB collection. Data keys are protected with encryption by a KMS provider. - `key_vault_client`: A MongoClient connected to a MongoDB cluster containing the `key_vault_namespace` collection. - `codec_options`: An instance of :class:`~bson.codec_options.CodecOptions` to use when encoding a value for encryption and decoding the decrypted BSON value. This should be the same CodecOptions instance configured on the MongoClient, Database, or Collection used to access application data. - `kms_tls_options` (optional): A map of KMS provider names to TLS options to use when creating secure connections to KMS providers. Accepts the same TLS options as :class:`pymongo.mongo_client.MongoClient`. For example, to override the system default CA file:: kms_tls_options={'kmip': {'tlsCAFile': certifi.where()}} Or to supply a client certificate:: kms_tls_options={'kmip': {'tlsCertificateKeyFile': 'client.pem'}} .. versionchanged:: 4.0 Added the `kms_tls_options` parameter and the "kmip" KMS provider. .. versionadded:: 3.9 """ if not _HAVE_PYMONGOCRYPT: raise ConfigurationError( "client-side field level encryption requires the pymongocrypt " "library: install a compatible version with: " "python -m pip install 'pymongo[encryption]'") if not isinstance(codec_options, CodecOptions): raise TypeError("codec_options must be an instance of " "bson.codec_options.CodecOptions") self._kms_providers = kms_providers self._key_vault_namespace = key_vault_namespace self._key_vault_client = key_vault_client self._codec_options = codec_options db, coll = key_vault_namespace.split('.', 1) key_vault_coll = key_vault_client[db][coll] opts = AutoEncryptionOpts(kms_providers, key_vault_namespace, kms_tls_options=kms_tls_options) self._io_callbacks = _EncryptionIO(None, key_vault_coll, None, opts) self._encryption = ExplicitEncrypter( self._io_callbacks, MongoCryptOptions(kms_providers, None))
async def main(): # The MongoDB namespace (db.collection) used to store the # encrypted documents in this example. encrypted_namespace = "test.coll" # This must be the same master key that was used to create # the encryption key. local_master_key = os.urandom(96) kms_providers = {"local": {"key": local_master_key}} # The MongoDB namespace (db.collection) used to store # the encryption data keys. key_vault_namespace = "encryption.__pymongoTestKeyVault" key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1) # The MotorClient used to access the key vault (key_vault_namespace). key_vault_client = AsyncIOMotorClient() key_vault = key_vault_client[key_vault_db_name][key_vault_coll_name] # Ensure that two data keys cannot share the same keyAltName. await key_vault.drop() await key_vault.create_index( "keyAltNames", unique=True, partialFilterExpression={"keyAltNames": {"$exists": True}}) client_encryption = AsyncIOMotorClientEncryption( kms_providers, key_vault_namespace, key_vault_client, # The CodecOptions class used for encrypting and decrypting. # This should be the same CodecOptions instance you have configured # on MotorClient, Database, or Collection. We will not be calling # encrypt() or decrypt() in this example so we can use any # CodecOptions. CodecOptions()) # Create a new data key and json schema for the encryptedField. data_key_id = await client_encryption.create_data_key( 'local', key_alt_names=['pymongo_encryption_example_2']) json_schema = { "properties": { "encryptedField": { "encrypt": { "keyId": [data_key_id], "bsonType": "string", "algorithm": Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic } } }, "bsonType": "object" } auto_encryption_opts = AutoEncryptionOpts( kms_providers, key_vault_namespace) client = AsyncIOMotorClient(auto_encryption_opts=auto_encryption_opts) db_name, coll_name = encrypted_namespace.split(".", 1) db = client[db_name] # Clear old data await db.drop_collection(coll_name) # Create the collection with the encryption JSON Schema. await db.create_collection( coll_name, # uuid_representation=STANDARD is required to ensure that any # UUIDs in the $jsonSchema document are encoded to BSON Binary # with the standard UUID subtype 4. This is only needed when # running the "create" collection command with an encryption # JSON Schema. codec_options=CodecOptions(uuid_representation=STANDARD), write_concern=WriteConcern(w="majority"), validator={"$jsonSchema": json_schema}) coll = client[db_name][coll_name] await coll.insert_one({"encryptedField": "123456789"}) decrypted_doc = await coll.find_one() print('Decrypted document: %s' % (decrypted_doc,)) unencrypted_coll = AsyncIOMotorClient()[db_name][coll_name] encrypted_doc = await unencrypted_coll.find_one() print('Encrypted document: %s' % (encrypted_doc,)) try: await unencrypted_coll.insert_one({"encryptedField": "123456789"}) except OperationFailure as exc: print('Unencrypted insert failed: %s' % (exc.details,))
def test_data_key(self): listener = OvertCommandListener() client = rs_or_single_client(event_listeners=[listener]) self.addCleanup(client.close) client.db.coll.drop() vault = create_key_vault(client.keyvault.datakeys) self.addCleanup(vault.drop) # Configure the encrypted field via the local schema_map option. schemas = { "db.coll": { "bsonType": "object", "properties": { "encrypted_placeholder": { "encrypt": { "keyId": "/placeholder", "bsonType": "string", "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" } } } } } opts = AutoEncryptionOpts(self.kms_providers(), 'keyvault.datakeys', schema_map=schemas) client_encrypted = rs_or_single_client(auto_encryption_opts=opts, uuidRepresentation='standard') self.addCleanup(client_encrypted.close) client_encryption = ClientEncryption(self.kms_providers(), 'keyvault.datakeys', client, OPTS) self.addCleanup(client_encryption.close) # Local create data key. listener.reset() local_datakey_id = client_encryption.create_data_key( 'local', key_alt_names=['local_altname']) self.assertBinaryUUID(local_datakey_id) cmd = listener.results['started'][-1] self.assertEqual('insert', cmd.command_name) self.assertEqual({'w': 'majority'}, cmd.command.get('writeConcern')) docs = list(vault.find({'_id': local_datakey_id})) self.assertEqual(len(docs), 1) self.assertEqual(docs[0]['masterKey']['provider'], 'local') # Local encrypt by key_id. local_encrypted = client_encryption.encrypt( 'hello local', Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=local_datakey_id) self.assertEncrypted(local_encrypted) client_encrypted.db.coll.insert_one({ '_id': 'local', 'value': local_encrypted }) doc_decrypted = client_encrypted.db.coll.find_one({'_id': 'local'}) self.assertEqual(doc_decrypted['value'], 'hello local') # Local encrypt by key_alt_name. local_encrypted_altname = client_encryption.encrypt( 'hello local', Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_alt_name='local_altname') self.assertEqual(local_encrypted_altname, local_encrypted) # AWS create data key. listener.reset() master_key = { 'region': 'us-east-1', 'key': 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-' '9f25-e30687b580d0' } aws_datakey_id = client_encryption.create_data_key( 'aws', master_key=master_key, key_alt_names=['aws_altname']) self.assertBinaryUUID(aws_datakey_id) cmd = listener.results['started'][-1] self.assertEqual('insert', cmd.command_name) self.assertEqual({'w': 'majority'}, cmd.command.get('writeConcern')) docs = list(vault.find({'_id': aws_datakey_id})) self.assertEqual(len(docs), 1) self.assertEqual(docs[0]['masterKey']['provider'], 'aws') # AWS encrypt by key_id. aws_encrypted = client_encryption.encrypt( 'hello aws', Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=aws_datakey_id) self.assertEncrypted(aws_encrypted) client_encrypted.db.coll.insert_one({ '_id': 'aws', 'value': aws_encrypted }) doc_decrypted = client_encrypted.db.coll.find_one({'_id': 'aws'}) self.assertEqual(doc_decrypted['value'], 'hello aws') # AWS encrypt by key_alt_name. aws_encrypted_altname = client_encryption.encrypt( 'hello aws', Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_alt_name='aws_altname') self.assertEqual(aws_encrypted_altname, aws_encrypted) # Explicitly encrypting an auto encrypted field. msg = (r'Cannot encrypt element of type binData because schema ' r'requires that type is one of: \[ string \]') with self.assertRaisesRegex(EncryptionError, msg): client_encrypted.db.coll.insert_one( {'encrypted_placeholder': local_encrypted})
def test_init_requires_pymongocrypt(self): with self.assertRaises(ConfigurationError): AutoEncryptionOpts({}, 'keyvault.datakeys')
def get_connection(with_enc=False): """ Returns instance of global pooled connection. The connection instance has automatic decryption enabled. -> MongoClient If with_enc=True this returns a ClientEncryption used for encryption fields along the connection -> MonogClient, ClientEncryption """ global CONNECTION global CLIENT_ENC if CONNECTION is not None and CLIENT_ENC is not None: if with_enc: return CONNECTION, CLIENT_ENC else: return CONNECTION else: # Key must be 96 bytes local_master_key_raw = os.environ["SOFI_BIFROST_ENCRYPTION_KEY"] local_master_key = binascii.a2b_base64(local_master_key_raw.encode()) kms_providers = {"local": {"key": local_master_key}} # The MongoDB namespace (db.collection) used to store # the encryption data keys. key_vault_namespace = SOFI_BIFROST_ENCRYPTION_NAMESPACE key_vault_db_name, key_name = ( ENCRYPTION_DB, ENCRYPTION_KEY_NAME, ) # bypass_auto_encryption=True disable automatic encryption but keeps # the automatic _decryption_ behavior. bypass_auto_encryption will # also disable spawning mongocryptd. auto_encryption_opts = AutoEncryptionOpts(kms_providers, key_vault_namespace, bypass_auto_encryption=True) client = (MongoClient( auto_encryption_opts=auto_encryption_opts) if DEBUG else MongoClient(BIFROST_MONGO_CONN, auto_encryption_opts=auto_encryption_opts)) if not client.is_primary: logging.debug( "MongoDB client is not primary - getting the primary client") client = client.primary coll = client.test.coll # First time key setup. Index creation in mongo is idempotent key_vault = client[key_vault_db_name][key_name] key_vault.create_index( "keyAltNames", unique=True, partialFilterExpression={"keyAltNames": { "$exists": True }}, ) client_encryption = ClientEncryption( kms_providers, key_vault_namespace, # The MongoClient to use for reading/writing to the key vault. # This can be the same MongoClient used by the main application. client, # The CodecOptions class used for encrypting and decrypting. # This should be the same CodecOptions instance you have configured # on MongoClient, Database, or Collection. coll.codec_options, ) existing = client[key_vault_db_name][key_name].find_one() if existing is None: client_encryption.create_data_key("local", key_alt_names=[key_name]) CONNECTION = client CLIENT_ENC = client_encryption if with_enc: return CONNECTION, CLIENT_ENC else: return CONNECTION
def test_corpus(self): opts = AutoEncryptionOpts(self.kms_providers(), 'keyvault.datakeys') self._test_corpus(opts)
def test_kwargs(self): opts = AutoEncryptionOpts(KMS_PROVIDERS, 'keyvault.datakeys') client = MongoClient(auto_encryption_opts=opts, connect=False) self.addCleanup(client.close) self.assertEqual(get_client_opts(client).auto_encryption_opts, opts)
client = MongoClient("localhost", 27017) # ** A LOCAL TEST KEY SHOULD NEVER BE USED IN PRODUCTION, ONLY FOR DEVELOPMENT ** # Test key material generated on Mac & Linux with: echo $(head -c 96 /dev/urandom | base64 | tr -d '\n') local_key = 'CgOcoan3c/wm2c+WsOO6fXOUlJgd7SLQ1vl///aEFX6vXN9+7VOAP+iHKheZiYlB09ZS7CDcAQhlPeTeQNz03xiGbiCJJvl3uj4lnG+5i/udSLJAcwgtgtaedkFD0ROq' key_bin = binary.Binary(base64.b64decode(local_key)) kms_providers = {'local': {'key': key_bin}} print( "Please ensure mongocryptd is running (included in the standard enterprise download package)." ) fle_opts = AutoEncryptionOpts(kms_providers, "demoFLE.__keystore", mongocryptd_bypass_spawn=True) client = MongoClient("mongodb://localhost:27017/demoFLE", auto_encryption_opts=fle_opts) db = client.demoFLE client_encryption = pymongo.encryption.ClientEncryption( kms_providers, "demoFLE.__keystore", client, OPTS) client_encryption.create_data_key('local', key_alt_names=['pykey1']) key1 = db.get_collection('__keystore').find_one({"keyAltNames": "pykey1"})['_id'] client.close()
def test_kwargs(self): opts = AutoEncryptionOpts({}, 'admin.datakeys') client = MongoClient(auto_encryption_opts=opts, connect=False) self.assertEqual(get_client_opts(client).auto_encryption_opts, opts)