-
Notifications
You must be signed in to change notification settings - Fork 26
INTPYTHON-527 Add Queryable Encryption support #329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
django_mongodb_backend/management/commands/get_encrypted_fields_map.py
Outdated
Show resolved
Hide resolved
django_mongodb_backend/management/commands/get_encrypted_fields_map.py
Outdated
Show resolved
Hide resolved
django_mongodb_backend/management/commands/get_encrypted_fields_map.py
Outdated
Show resolved
Hide resolved
django_mongodb_backend/management/commands/get_encrypted_fields_map.py
Outdated
Show resolved
Hide resolved
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
That is correct. We can remove
In other words, do we support the chicken and the egg or just the egg 😂 |
Any progress on this? Do we need to ask @blink1073 for help? |
@aclark4life Soooo ... I don't know what you've done around that area so far, but it's great that you're bringing it up, I didn't realize we'd have to still worry about this part because I don't think the PR currently concerns itself at all with how we integrate with crypt_shared/mongocryptd? So I'll explain the whole story here, knowing that I'm risking telling you things you're alread well aware of 🙂
Operationally, So we generally recommend using the crypt_shared library, which can be achieved by passing Given that this is a new feature here, I'd strongly consider setting As far as the tests here are concerned themselves, I imagine they're passing locally for you because you do have |
Can we document our way around this by recommending Also what about bundling that library in the |
Maybe, but I think we'd want to have a conversation around what the typical expectations here are. You'll generally want to have development and production environments behave similarly, and you'd still need to have a plan for what to do when mongocryptd does get deprecated eventually (long-term, I think it's fair to expect this to happen). You'll also still be in a position where you need to download and install mongocryptd, the only case in which this requirement goes away is the one where you happen to have the enterprise MongoDB server binaries already ready in your
This question comes up on a regular basis 🙂 Here's a Slack thread from April, which was one of the last times we spoke about this. tl;dr: Yes, the setup process for CSFLE/QE is involved, and we'd like to make it easier. Currently, there is a requirement for the user to explicitly acknowledge that they have read and accepted the enterprise license agreement and that they are an Atlas or EA customer. Bundling this library with regular packages that can be installed via a regular package manager command like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good! I've provided comments and feedback around some documentation and clarifications, but will approve once those are addressed.
ae765eb
to
886b1e0
Compare
886b1e0
to
4496054
Compare
- Fix rebase merge conflict edits - Remove integer field FIXME comments - Remove pos_int
5b2bc07
to
bddae41
Compare
docs/source/topics/index.rst
Outdated
@@ -11,3 +11,4 @@ know: | |||
embedded-models | |||
transactions | |||
known-issues | |||
queryable-encryption |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
order above "transactions"
docs/source/ref/django-admin.rst
Outdated
.. django-admin-option:: --create-new-keys | ||
|
||
If specified, creates the data keys instead of getting them from the | ||
database. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This option need some explanation in the howto. (use case)
Also, there is no explanation here that the command "gets the keys from the database"... need more explanation.
.. admonition:: List of encrypted fields | ||
|
||
See the full list of :ref:`encrypted fields <encrypted-fields>` in the | ||
:doc:`Model field reference </ref/models/fields>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't need to repeat the name of the linked doc "Model field reference"
tests/encryption_/test_base.py
Outdated
|
||
def setUp(self): | ||
self.appointment = Appointment.objects.create(time="8:00") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no blank line needed after every object
tests/encryption_/test_management.py
Outdated
self.assertEqual(json1, json2) | ||
|
||
def test_show_encrypted_fields_map(self): | ||
self.maxDiff = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can set maxDiff = None
at the class-level.
.. admonition:: List of encrypted fields | ||
|
||
See the full list of :ref:`encrypted fields <encrypted-fields>` in the | ||
:doc:`Model field reference </ref/models/fields>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's worth include this reference, but not in the middle of the example.
{ | ||
_id: ObjectId('68825b066fac55353a8b2b41'), | ||
ssn: '123-45-6789', | ||
__safeContent__: [b'\xe0)NOFB\x9a,\x08\xd7\xdd\xb8\xa6\xba$…'] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what this is telling me. So encrypted fields appear in plain text, but what the heck is __safeContent__
? As I developer, do I care about it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a link to explanation.
You can query encrypted fields using a | ||
:ref:`manual:qe-supported-query-operators` which must be specified in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is the best page to link to (btw, the text renders as "using a Supported Query Operators". It doesn't show any of the options https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/encrypt-and-query/#configure-encrypted-fields-for-optimal-search-and-storage
tests/encryption_/models.py
Outdated
|
||
EQUALITY_QUERY = {"queryType": "equality"} | ||
RANGE_QUERY = {"queryType": "range"} | ||
RANGE_QUERY_MIN_MAX = {"queryType": "range", "min": 0, "max": 100} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least for the values that aren't reused, it doesn't make sense to use a module level variable.
``AutoEncryptionOpts``. | ||
|
||
Fortunately, this is easy to do with Django MongoDB Backend. You can use | ||
the ``showencryptedfieldsmap`` management command to generate the schema map |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:djadmin:`showencryptedfieldsmap`
.. note:: The ``showencryptedfieldsmap`` command is only available if you | ||
have the ``django_mongodb_backend`` app included in the | ||
:setting:`INSTALLED_APPS` setting. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From caching docs:
.. admonition:: Didn't work?
If you get the error ``Unknown command: 'createcachecollection'``, ensure
``"django_mongodb_backend"`` is in your :setting:`INSTALLED_APPS` setting.
To generate the encrypted fields map, run the following command in your Django | ||
project:: | ||
|
||
python manage.py showencryptedfieldsmap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If following the above steps, --database=encrypted
is also needed.
tests/encryption_/test_base.py
Outdated
] | ||
} | ||
connection = connections["encrypted"] | ||
auto_encryption_opts = getattr(connection.connection._options, "auto_encryption_opts", None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getattr
should be unnecessary. This test shouldn't run if auto_encryption_opt
isn't set.
|
||
In addition to :doc:`installing </intro/install>` and | ||
:doc:`configuring </intro/configure>` Django MongoDB Backend, | ||
you will need to install PyMongo with Queryable Encryption support:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think saying "you will need to install PyMongo with Queryable Encryption support" and then giving a command that doesn't mention pymongo could be confusing.
Probably "you need to install some additional dependencies::" could be enough.
django_mongodb_backend/schema.py
Outdated
else: | ||
db.create_collection(db_table) | ||
|
||
def _get_encrypted_fields_map(self, model, client, auto_encryption_opts, from_db=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from_db
is not very descriptive. How about create_data_keys
?
django_mongodb_backend/base.py
Outdated
# Avoid using PyMongo to check the database version or require | ||
# pymongocrypt>=1.14.2 which will contain a fix for the `buildInfo` | ||
# command. https://jira.mongodb.org/browse/PYTHON-5429 | ||
return tuple(self.connection.admin.command("buildInfo")["versionArray"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still uses PyMongo. I guess you meant the server_info()
method. Is there a big benefit to switching back to in the the future? If so, you can write something like "This can use tuple(self.connection.server_info()["versionArray"]) on pymongocrypt 1.14.2+" (heck, if there's a benefit, you can add some conditional code based on the pymongocrypt version)... pymongocrypt 1.15.0 was released Aug. 15 (1.14.1 is the latest 1.14.x release so unclear if there will be a 1.14.2). Anyway, some comment update is needed.
|
||
def handle(self, *args, **options): | ||
db = options["database"] | ||
create = options.get("create", False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create = options.get("create", False) | |
create = options.get("`create_new_keys`", False) |
for model in router.get_migratable_models(app_config, db): | ||
if model_has_encrypted_fields(model): | ||
from_db = not create | ||
fields = connection.schema_editor()._get_encrypted_fields_map( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use with connection.schema_editor() as editor:
at the top-level outside of the loops so you don't have to initialize a new one for every model.
django_mongodb_backend/schema.py
Outdated
else: | ||
db.create_collection(db_table) | ||
|
||
def _get_encrypted_fields_map(self, model, client, auto_encryption_opts, from_db=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto_encryption_opts
needn't be an argument. It's an attribute of client
.
raise ImproperlyConfigured( | ||
"Encrypted fields found but " | ||
"DATABASES[[self.connection.alias}]['OPTIONS'] is missing " | ||
"auto_encryption_opts. Please set `auto_encryption_opts` " | ||
"in the connection settings." | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try adding a test for this exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tested it manually but haven't written a test for it yet.
django_mongodb_backend/schema.py
Outdated
else: | ||
# If the encrypted fields map is provided, get the map for the | ||
# specific collection. | ||
encrypted_fields_map = encrypted_fields_map.get(db_table, None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does it mean to fallback to None
?
"KMS_PROVIDERS": {}, | ||
"KMS_CREDENTIALS": {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For KMS_CREDENTIALS, I believe the options are documented here: https://pymongo.readthedocs.io/en/stable/api/pymongo/encryption.html#pymongo.encryption.ClientEncryption.create_data_key (but pymongo docs are deprecated?)
So a minimal example for AWS: "KMS_CREDENTIALS": {"aws": {"region": "...", "key": "..."}
Don't break the build!
Don't break the build Part II
Previous attempts and additional context here: