Skip to content

Commit

Permalink
lint
Browse files Browse the repository at this point in the history
  • Loading branch information
felipao-mx committed Jun 6, 2024
1 parent 8c88f16 commit 774b182
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 40 deletions.
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# mongoengine-plus
[![codecov](https://codecov.io/gh/cuenca-mx/mongoengine-plus/graph/badge.svg?token=CwoY4toTQU)](https://codecov.io/gh/cuenca-mx/mongoengine-plus)
[![test](https://github.com/cuenca-mx/cuenca-python/workflows/test/badge.svg)](https://github.com/cuenca-mx/mongoengine-plus/actions?query=workflow%3Atest)

Extra field types, function helpers and asyncio support for [mongoengine](https://github.com/MongoEngine/mongoengine)

## Installation
Expand Down Expand Up @@ -148,22 +151,23 @@ We recommend using `async_to_list()` for small result sets.

## Client-side Field Level Encryption

Mongoengine-plus introduces a new field type called `EncryptedString` that implements
Mongoengine-plus introduces a new field type called `EncryptedStringField` that implements
Client-side Field Level Encryption ([CSFLE](https://www.mongodb.com/docs/manual/core/csfle/))
using [pymongo](https://pymongo.readthedocs.io/en/stable/examples/encryption.html) encryption classes.
This feature allows explicit data encryption before sending it over the network to MongoDB,
and automatic data decryption after reading from MongoDB. It supports both synchronous
and asynchronous operations. Currently, the `EncryptedString` implementation supports
and asynchronous operations. Currently, the `EncryptedStringField` implementation supports
the AWS KMS service as the Key Management Service (KMS) provider.

```python
from mongoengine import Document, StringField
from mongoengine_plus.types import EncryptedString
from mongoengine_plus.types import EncryptedStringField
from pymongo.encryption import Algorithm


class User(Document):
id = StringField(primary_key=True)
ssn = EncryptedString(
ssn = EncryptedStringField(
algorithm=Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic
)

Expand All @@ -177,11 +181,11 @@ print(user_.ssn) # Output: '12345'

```

There are a few steps before you can start using `EncryptedString`.
There are a few steps before you can start using `EncryptedStringField`.

### 1. Create a Data Encryption Key (DEK)

Before using `EncryptedString`, you'll need to create a Data Encryption Key (DEK)
Before using `EncryptedStringField`, you'll need to create a Data Encryption Key (DEK)
for encrypting and decrypting your data. The DEK should follow the recommended
requirements described in the official MongoDB documentation on [Keys and Key Vaults](https://www.mongodb.com/docs/manual/core/csfle/fundamentals/keys-key-vaults/#std-label-csfle-reference-keys-key-vaults).
We've provided a helper method to create your DEK easily.
Expand Down Expand Up @@ -211,19 +215,18 @@ You'll need to execute this step only once during the project setup. Ensure that
MongoDB user has the necessary permissions for collection and index creation, and
access to the AWS KMS key.

### 2. Configure `EncryptedString`
### 2. Configure `EncryptedStringField`

Since `EncryptedString` needs to read the DEK from your MongoDB instance and access the
Since `EncryptedStringField` needs to read the DEK from your MongoDB instance and access the
KMS key for encryption/decryption, you'll need to configure it as follows. This
configuration might be in your `__init__.py` file and should be executed once.

```python
from mongoengine import Document, StringField
from mongoengine_plus.types import EncryptedString
from mongoengine_plus.types import EncryptedStringField
from pymongo.encryption import Algorithm


EncryptedString.configure_aws_kms(
EncryptedStringField.configure_aws_kms(
'encryption.__keyVault',
'my_key_name',
'your-aws-key-id',
Expand All @@ -234,7 +237,7 @@ EncryptedString.configure_aws_kms(

class User(Document):
id = StringField(primary_key=True)
ssn = EncryptedString(
ssn = EncryptedStringField(
algorithm=Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic
)
```
Expand All @@ -243,7 +246,7 @@ Now you are ready to go!

### 3. Optimize KMS requests (optional)

There's a caveat in the `EncryptedString` implementation. Every time `EncryptedString` needs
There's a caveat in the `EncryptedStringField` implementation. Every time `EncryptedStringField` needs
to encrypt or decrypt data, it uses the `pymongo.encryption.ClientEncryption`,
which makes a request to the AWS KMS service endpoint. This can potentially slow down
the performance of reading and writing encrypted data to MongoDB. As a workaround,
Expand Down
4 changes: 2 additions & 2 deletions mongoengine_plus/types/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__all__ = ['EnumField', 'EncryptedString']
__all__ = ['EnumField', 'EncryptedStringField']

from .encrypted_string.fields import EncryptedString
from .encrypted_string.fields import EncryptedStringField
from .enum_field import EnumField
8 changes: 4 additions & 4 deletions mongoengine_plus/types/encrypted_string/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
__all__ = ['EncryptedString', 'cache_kms_data_key']
__all__ = ['EncryptedStringField', 'cache_kms_data_key']

import codecs

import boto3
from pymongo.encryption import _EncryptionIO

from .fields import EncryptedString
from .fields import EncryptedStringField


def cache_kms_data_key(
Expand All @@ -18,8 +18,8 @@ def cache_kms_data_key(
) -> None:
"""
Retrieve the KMS Key used to encrypt and decrypt data and creates a cache
to optimize the usage of `EncryptedString`. You should execute this function once
before making any database write or read operations
to optimize the usage of `EncryptedString`. You should execute this
function once before making any database write or read operations
"""
from .base import get_data_key

Expand Down
2 changes: 1 addition & 1 deletion mongoengine_plus/types/encrypted_string/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
CODEC_OPTION = CodecOptions(uuid_representation=STANDARD)


class EncryptedString(BaseField):
class EncryptedStringField(BaseField):
"""
Represents an encrypted string. Supports two types of
encryption algorithms: Deterministic and Random.
Expand Down
10 changes: 5 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pytest
from pymongo import MongoClient

from mongoengine_plus.types import EncryptedString
from mongoengine_plus.types import EncryptedStringField
from mongoengine_plus.types.encrypted_string.base import create_data_key


Expand Down Expand Up @@ -69,21 +69,21 @@ def setup_encrypted_string_data_key(
Creates data keys for testing purpose. It is required in order to use
Explicit Client-Side Field Level Encryption (CSFLE)
"""
EncryptedString.configure_aws_kms(
EncryptedStringField.configure_aws_kms(
'encryption.__keyVault',
'thekey',
'test',
'test',
'us-east-1',
)

db_name, key_coll = EncryptedString.key_namespace.split(".", 1)
db_name, key_coll = EncryptedStringField.key_namespace.split(".", 1)

key_vault = db_connection[db_name][key_coll]
key_vault.drop()
create_data_key(
EncryptedString.kms_provider,
EncryptedString.key_namespace,
EncryptedStringField.kms_provider,
EncryptedStringField.key_namespace,
kms_key_arn,
'thekey',
kms_connection_url,
Expand Down
30 changes: 15 additions & 15 deletions tests/types/test_encrypted_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pymongo.encryption import Algorithm, ClientEncryption, _EncryptionIO

from mongoengine_plus.models import uuid_field
from mongoengine_plus.types import EncryptedString
from mongoengine_plus.types import EncryptedStringField
from mongoengine_plus.types.encrypted_string import cache_kms_data_key
from mongoengine_plus.types.encrypted_string.base import (
create_data_key,
Expand All @@ -22,7 +22,7 @@
class User(Document):
id = StringField(primary_key=True, default=uuid_field('US'))
name = StringField()
nss = EncryptedString(
nss = EncryptedStringField(
algorithm=Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic,
required=True,
unique=True,
Expand All @@ -38,17 +38,17 @@ def user() -> Generator[User, None, None]:


def test_configure_encrypted_string():
EncryptedString.configure_aws_kms(
EncryptedStringField.configure_aws_kms(
'foo.bar',
'keyname',
'test',
'test',
'us-east-1',
)
assert EncryptedString.key_namespace == 'foo.bar'
assert EncryptedString.key_name == 'keyname'
assert EncryptedString.aws_region_name == 'us-east-1'
assert EncryptedString.kms_provider == dict(
assert EncryptedStringField.key_namespace == 'foo.bar'
assert EncryptedStringField.key_name == 'keyname'
assert EncryptedStringField.aws_region_name == 'us-east-1'
assert EncryptedStringField.kms_provider == dict(
aws=dict(accessKeyId='test', secretAccessKey='test')
)

Expand All @@ -66,16 +66,16 @@ def test_create_data_key(
key_name = 'thekey'
kms_region_name = 'us-east-1'

EncryptedString.configure_aws_kms(
EncryptedStringField.configure_aws_kms(
f'{db_name}.{collection_name}',
key_name,
'test',
'test',
kms_region_name,
)
create_data_key(
EncryptedString.kms_provider,
EncryptedString.key_namespace,
EncryptedStringField.kms_provider,
EncryptedStringField.key_namespace,
kms_key_arn,
key_name,
kms_connection_url,
Expand All @@ -89,7 +89,7 @@ def test_create_data_key(
assert type(data_key['keyMaterial']) is bytes
assert data_key['masterKey'] == dict(
provider='aws',
region=EncryptedString.aws_region_name,
region=EncryptedStringField.aws_region_name,
key=kms_key_arn,
endpoint=kms_connection_url.replace('https://', ''),
)
Expand All @@ -114,8 +114,8 @@ def test_encrypted_string_on_saving_and_reading(
assert type(user_dict['nss']) == Binary

with ClientEncryption(
EncryptedString.kms_provider,
EncryptedString.key_namespace,
EncryptedStringField.kms_provider,
EncryptedStringField.key_namespace,
client,
CODEC_OPTION,
) as client_encryption:
Expand All @@ -141,8 +141,8 @@ def test_cache_kms_request(kms_connection_url: str) -> None:
# in production environments.
with patch('boto3.client', partial(boto3.client, verify=False)):
cache_kms_data_key(
EncryptedString.key_namespace,
EncryptedString.key_name,
EncryptedStringField.key_namespace,
EncryptedStringField.key_name,
'test',
'test',
'us-east-1',
Expand Down

0 comments on commit 774b182

Please sign in to comment.