-
Notifications
You must be signed in to change notification settings - Fork 1.1k
PYTHON-5046 Support $lookup in CSFLE and QE #2210
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
Changes from 14 commits
9306a9e
c69c3bd
1fb56b0
ff300a9
ea56a2e
3ea9a25
d967ffa
c9a4bd0
590e455
6e33d29
74fac79
daa420d
2c46edb
8770445
8c45daa
53da94d
c85a856
09a82da
74b338b
7dcf8bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,7 +73,7 @@ | |
is_greenthread_patched, | ||
) | ||
|
||
from bson import DatetimeMS, Decimal128, encode, json_util | ||
from bson import BSON, DatetimeMS, Decimal128, encode, json_util | ||
from bson.binary import UUID_SUBTYPE, Binary, UuidRepresentation | ||
from bson.codec_options import CodecOptions | ||
from bson.errors import BSONError | ||
|
@@ -94,6 +94,7 @@ | |
EncryptionError, | ||
InvalidOperation, | ||
OperationFailure, | ||
PyMongoError, | ||
ServerSelectionTimeoutError, | ||
WriteError, | ||
) | ||
|
@@ -2419,6 +2420,318 @@ async def test_05_roundtrip_encrypted_unindexed(self): | |
self.assertEqual(decrypted, val) | ||
|
||
|
||
# https://github.com/mongodb/specifications/blob/527e22d5090ec48bf1e144c45fc831de0f1935f6/source/client-side-encryption/tests/README.md#25-test-lookup | ||
class TestLookupProse(AsyncEncryptionIntegrationTest): | ||
@async_client_context.require_no_standalone | ||
@async_client_context.require_version_min(7, 0, -1) | ||
async def asyncSetUp(self): | ||
await super().asyncSetUp() | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
await self.encrypted_client.db.drop_collection("keyvault") | ||
ShaneHarvey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
key_doc = json_data("etc", "data", "lookup", "key-doc.json") | ||
key_vault = await create_key_vault(self.encrypted_client.db.keyvault, key_doc) | ||
self.addCleanup(key_vault.drop) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addCleanup -> addAsyncCleanup Also can we use the client_context client here to drop the entire database instead of just the keyvault? This will fix the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively we can just leave the data there and skip the cleanup altogether. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah good catch, and yes! done! |
||
|
||
await self.encrypted_client.db.drop_collection("csfle") | ||
await self.encrypted_client.db.create_collection( | ||
"csfle", | ||
validator={"$jsonSchema": json_data("etc", "data", "lookup", "schema-csfle.json")}, | ||
) | ||
|
||
await self.encrypted_client.db.drop_collection("csfle2") | ||
await self.encrypted_client.db.create_collection( | ||
"csfle2", | ||
validator={"$jsonSchema": json_data("etc", "data", "lookup", "schema-csfle2.json")}, | ||
) | ||
|
||
await self.encrypted_client.db.drop_collection("qe") | ||
await self.encrypted_client.db.create_collection( | ||
"qe", encryptedFields=json_data("etc", "data", "lookup", "schema-qe.json") | ||
) | ||
|
||
await self.encrypted_client.db.drop_collection("qe2") | ||
await self.encrypted_client.db.create_collection( | ||
"qe2", encryptedFields=json_data("etc", "data", "lookup", "schema-qe2.json") | ||
) | ||
|
||
await self.encrypted_client.db.drop_collection("no_schema") | ||
await self.encrypted_client.db.create_collection("no_schema") | ||
|
||
await self.encrypted_client.db.drop_collection("no_schema2") | ||
await self.encrypted_client.db.create_collection("no_schema2") | ||
|
||
self.unencrypted_client = await self.async_rs_or_single_client() | ||
|
||
await self.encrypted_client.db.csfle.insert_one({"csfle": "csfle"}) | ||
doc = await self.unencrypted_client.db.csfle.find_one() | ||
self.assertTrue(isinstance(doc["csfle"], Binary)) | ||
await self.encrypted_client.db.csfle2.insert_one({"csfle2": "csfle2"}) | ||
doc = await self.unencrypted_client.db.csfle2.find_one() | ||
self.assertTrue(isinstance(doc["csfle2"], Binary)) | ||
await self.encrypted_client.db.qe.insert_one({"qe": "qe"}) | ||
doc = await self.unencrypted_client.db.qe.find_one() | ||
self.assertTrue(isinstance(doc["qe"], Binary)) | ||
await self.encrypted_client.db.qe2.insert_one({"qe2": "qe2"}) | ||
doc = await self.unencrypted_client.db.qe2.find_one() | ||
self.assertTrue(isinstance(doc["qe2"], Binary)) | ||
await self.encrypted_client.db.no_schema.insert_one({"no_schema": "no_schema"}) | ||
await self.encrypted_client.db.no_schema2.insert_one({"no_schema2": "no_schema2"}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_1(self): | ||
ShaneHarvey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.encrypted_client = await self.async_rs_or_single_client( | ||
ShaneHarvey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.csfle.aggregate( | ||
[ | ||
{"$match": {"csfle": "csfle"}}, | ||
{ | ||
"$lookup": { | ||
"from": "no_schema", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"no_schema": "no_schema"}}, | ||
{"$project": {"_id": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"csfle": "csfle", "matched": [{"no_schema": "no_schema"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_2(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.qe.aggregate( | ||
[ | ||
{"$match": {"qe": "qe"}}, | ||
{ | ||
"$lookup": { | ||
"from": "no_schema", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"no_schema": "no_schema"}}, | ||
{"$project": {"_id": 0, "__safeContent__": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0, "__safeContent__": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"qe": "qe", "matched": [{"no_schema": "no_schema"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_3(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.no_schema.aggregate( | ||
[ | ||
{"$match": {"no_schema": "no_schema"}}, | ||
{ | ||
"$lookup": { | ||
"from": "csfle", | ||
"as": "matched", | ||
"pipeline": [{"$match": {"csfle": "csfle"}}, {"$project": {"_id": 0}}], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"no_schema": "no_schema", "matched": [{"csfle": "csfle"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_4(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.no_schema.aggregate( | ||
[ | ||
{"$match": {"no_schema": "no_schema"}}, | ||
{ | ||
"$lookup": { | ||
"from": "qe", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"qe": "qe"}}, | ||
{"$project": {"_id": 0, "__safeContent__": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"no_schema": "no_schema", "matched": [{"qe": "qe"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_5(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.csfle.aggregate( | ||
[ | ||
{"$match": {"csfle": "csfle"}}, | ||
{ | ||
"$lookup": { | ||
"from": "csfle2", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"csfle2": "csfle2"}}, | ||
{"$project": {"_id": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"csfle": "csfle", "matched": [{"csfle2": "csfle2"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_6(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.qe.aggregate( | ||
[ | ||
{"$match": {"qe": "qe"}}, | ||
{ | ||
"$lookup": { | ||
"from": "qe2", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"qe2": "qe2"}}, | ||
{"$project": {"_id": 0, "__safeContent__": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0, "__safeContent__": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"qe": "qe", "matched": [{"qe2": "qe2"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_7(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
doc = await anext( | ||
await self.encrypted_client.db.no_schema.aggregate( | ||
[ | ||
{"$match": {"no_schema": "no_schema"}}, | ||
{ | ||
"$lookup": { | ||
"from": "no_schema2", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"no_schema2": "no_schema2"}}, | ||
{"$project": {"_id": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertEqual(doc, {"no_schema": "no_schema", "matched": [{"no_schema2": "no_schema2"}]}) | ||
|
||
@async_client_context.require_version_min(8, 1, -1) | ||
async def test_8(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
with self.assertRaises(PyMongoError) as exc: | ||
_ = await anext( | ||
await self.encrypted_client.db.csfle.aggregate( | ||
[ | ||
{"$match": {"csfle": "qe"}}, | ||
{ | ||
"$lookup": { | ||
"from": "qe", | ||
"as": "matched", | ||
"pipeline": [{"$match": {"qe": "qe"}}, {"$project": {"_id": 0}}], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertTrue("not supported" in str(exc)) | ||
|
||
@async_client_context.require_version_max(8, 1, -1) | ||
async def test_9(self): | ||
self.encrypted_client = await self.async_rs_or_single_client( | ||
auto_encryption_opts=AutoEncryptionOpts( | ||
key_vault_namespace="db.keyvault", | ||
kms_providers={"local": {"key": LOCAL_MASTER_KEY}}, | ||
) | ||
) | ||
with self.assertRaises(PyMongoError) as exc: | ||
_ = await anext( | ||
await self.encrypted_client.db.csfle.aggregate( | ||
[ | ||
{"$match": {"csfle": "csfle"}}, | ||
{ | ||
"$lookup": { | ||
"from": "no_schema", | ||
"as": "matched", | ||
"pipeline": [ | ||
{"$match": {"no_schema": "no_schema"}}, | ||
{"$project": {"_id": 0}}, | ||
], | ||
} | ||
}, | ||
{"$project": {"_id": 0}}, | ||
] | ||
) | ||
) | ||
self.assertTrue("Upgrade" in str(exc)) | ||
|
||
|
||
# https://github.com/mongodb/specifications/blob/072601/source/client-side-encryption/tests/README.md#rewrap | ||
class TestRewrapWithSeparateClientEncryption(AsyncEncryptionIntegrationTest): | ||
MASTER_KEYS: Mapping[str, Mapping[str, Any]] = { | ||
|
Uh oh!
There was an error while loading. Please reload this page.