Skip to content

Conversation

aclark4life
Copy link
Collaborator

@aclark4life aclark4life commented Jun 27, 2025

Previous attempts and additional context here:

@aclark4life

This comment was marked as resolved.

@timgraham

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@timgraham

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@timgraham

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@timgraham

This comment was marked as resolved.

@aclark4life

This comment was marked as resolved.

@timgraham

This comment was marked as resolved.

Copilot

This comment was marked as outdated.

@aclark4life aclark4life requested a review from Copilot August 19, 2025 23:18
Copilot

This comment was marked as resolved.

@aclark4life aclark4life force-pushed the INTPYTHON-527 branch 5 times, most recently from 26874aa to f32d62b Compare August 25, 2025 17:55
field_list = []
for field in fields:
if getattr(field, "encrypted", False):
key_alt_name = f"{db_table}_{field.column}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sufficient to avoid collisions? Unlikely as it may be:

db_table = "foo"
column = "bar_id"

db_table = "foo_bar"
column = "id"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well now that you mention it, how about a double separator ? {db_table}__{field.column} would pass that test with these key alt names:

  • foo__bar_id
  • foo_bar__id

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A period may be a more typical separator, including to indicate nested fields.

@aclark4life aclark4life force-pushed the INTPYTHON-527 branch 3 times, most recently from 1169c04 to d04bdbb Compare September 10, 2025 11:55
@aclark4life aclark4life force-pushed the INTPYTHON-527 branch 2 times, most recently from ccd2f36 to 94c1dc2 Compare September 17, 2025 21:25
@aclark4life aclark4life requested review from timgraham, a team and addaleax and removed request for addaleax September 19, 2025 19:58
The spec says: "encryptedFieldsMap maps a collection namespace to an
encryptedFields."

In this commit, we clarify the distinction between encrypted fields map
and encrypted fields.
- Test Python tutorial models (still unencrypted in this commit)
- Test all supported encrypted fields.
  - Test equality and range query type queries
- Test management command
- Test schema
- Removed test router
- Removed test_base
- EncryptedEmbeddedModel for encrypted objects
- EmbeddedModel for models with encrypted fields
db_table = model._meta.db_table
field_list = []
for field in fields:
if isinstance(field, EmbeddedModelField):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about PolymorphicEmbeddedFields or EmbeddedModelArrayFIeld? The design doc still needs a list of all encrypted fields that will be implemented, I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those could be added because I think the existing embedded models are doing all the work.

Comment on lines 19 to 24
class EncryptedEmbeddedModel(EmbeddedModel):
encrypted = True

class Meta:
abstract = True
required_db_features = {"supports_queryable_encryption"}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EncryptedEmbeddedModel isn't present in the design doc. Did you thinking change? I don't see what purpose it serves.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without it, I get this:

django.core.exceptions.ImproperlyConfigured: No kms_provider found in database router.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EncryptedEmbeddedModel isn't present in the design doc. Did you thinking change? I don't see what purpose it serves.

And yes the design doc needs to be updated.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without it, I get this:

django.core.exceptions.ImproperlyConfigured: No kms_provider found in database router.

The encryption_ tests pass for me without it. We shouldn't be trying to resolve a kms_provider for an embedded model anyway.

patient_name = models.CharField(max_length=255)
patient_id = models.BigIntegerField()
patient_record = EmbeddedModelField(PatientRecord)
patient_record = EncryptedEmbeddedModelField(PatientRecord)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per design doc, this is supposed to be EmbeddedModelField.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Comment on lines 19 to 24
class EncryptedEmbeddedModel(EmbeddedModel):
encrypted = True

class Meta:
abstract = True
required_db_features = {"supports_queryable_encryption"}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embedded models aren't created anyway, so required_db_features = {"supports_queryable_encryption"} would serve no purpose.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to facilitate use of the skip decorator.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of required_db_features is to prevent the model from being created if the database doesn't support it. Since SchemaEditor already ignores embedded models, required_db_features serves no purpose here.

if getattr(field, "encrypted", False):
return True

return bool(issubclass(model, EncryptedEmbeddedModel))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the model has encrypted fields, the loop above would already have returned True. This would only be executed if the model doesn't have any encrypted fields. :-D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for the case when an EncryptedEmbeddedModel has no encrypted fields but itself is encrypted e.g. Billing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code that calls model_has_encrypted_fields() shouldn't receive embedded models. Embedded models don't have their own collection, so they aren't going to be passed to SchemaEditor._create_collection() and neither are they returned by router.get_migratable_models() in showencryptedfieldsmap.py.

I don't see EncryptedEmbeddedModel serving any purpose. If I missed something, revert my commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants