Skip to content
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

RUST-1384 Implement auto encryption #717

Merged
merged 35 commits into from
Aug 17, 2022

Conversation

abr-egn
Copy link
Contributor

@abr-egn abr-egn commented Aug 4, 2022

RUST-1384

This implements support for auto encryption and decryption; the large majority of the complexity is in state_machine.rs; the changes needed to the main executor were pretty minor.

This is the companion PR to mongodb/libmongocrypt-rust#11 (and its ill-fated predecessor mongodb/libmongocrypt-rust#9).

// can return a helpful error if things get into a broken state rather than panicing.
let mut ctx = Ok(ctx);
loop {
let state = result_ref(&ctx)?.state()?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

result_ref and result_mut are needed because as_ref/as_mut on Results also applies to the error type, which causes an error when using ? because the types don't match the return type.

while let Some(kms_ctx) = scope.next_kms_ctx() {
kms_ctxen.push(Ok(kms_ctx));
}
stream::iter(kms_ctxen)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just using spawn for concurrency here wouldn't work because that requires a 'static bound; it's a little odd that the non-static equivalent is only found when operating on streams, but 🤷

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the stream versoin works basically the same as FuturesUnordered: the current thread polls all the futures, rather than having potentially many threads doing it in parallel.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, there it is. Yup, it's polling rather than parallel, which seems fine for this because these futures are going to be IO-bound anyway.

I'm going to stick with using the stream method rather than FuturesUnordered directly because the semantics of try_for_each_concurrent are very handy here.

csfle: &'a super::csfle::ClientState,
command: &'a RawDocument,
target_db: &'a str,
) -> BoxFuture<'a, Result<RawDocumentBuf>> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be boxed because it's technically recursive (run_mongocrypt_ctx can execute operations).

pub(crate) use run_command::RunCommand;
pub(crate) use update::Update;

const SERVER_4_2_0_WIRE_VERSION: i32 = 8;

/// A trait modeling the behavior of a server side operation.
///
/// No methods in this trait should have default behaviors to ensure that wrapper operations
/// replicate all behavior. Default behavior is provided by the `OperationDefault` trait.
pub(crate) trait Operation {
Copy link
Contributor Author

@abr-egn abr-egn Aug 4, 2022

Choose a reason for hiding this comment

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

The Trait / TraitWithDefaults pattern introduced here solves a gripe I've had for a while, which is that when implementing something that delegates most trait logic (e.g. raw_output::RawOutput) you won't get any help from the compiler if new trait methods are added that have defaults, which in turn could cause nasty logic bugs downstream. It's definitely a little awkward and not worth using for most traits, but Operation is critical enough that it seems worth using here.

@abr-egn abr-egn marked this pull request as ready for review August 4, 2022 16:05
Copy link
Contributor

@kmahar kmahar left a comment

Choose a reason for hiding this comment

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

looks good overall! just a couple comments re: spec details

@@ -49,7 +54,7 @@ impl<'a, T> Insert<'a, T> {
}
}

impl<'a, T: Serialize> Operation for Insert<'a, T> {
impl<'a, T: Serialize> OperationWithDefaults for Insert<'a, T> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed the spec talks about some differences in batch splitting behavior when automatic encryption is being used - it sounds like we might need to use 2MiB rather than max_bson_obj_size as the split boundary below?

cc @isabelatkinson - I think we'll need to keep the differing size limit in mind with the new bulk spec and API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, you're right - I'd initially read that as applying only to the bulk write API, but it also applies here. Updated.

Copy link
Contributor

Choose a reason for hiding this comment

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

I filed RUST-1438 so we remember to do the same for bulk writes when the time comes

let db = self.database(db.as_ref().ok_or_else(|| {
Error::internal("db required for NeedMongoCollinfo state")
})?);
let mut cursor = db.list_collections(filter, None).await?;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this operation is actually supposed to use the metadata client to avoid potential deadlocks, as described in this section of the spec. I see the integration guide mentions using the encrypted MongoClient, but I think we maybe forgot to update that back when SPEC-1768 was done (if so, it would be good to make a PR to update it for future readers).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, fixed. I'll send a PR for the integration guide.

@abr-egn abr-egn force-pushed the RUST-1384/auto-encryption branch from 115e6fd to 6bbd8c1 Compare August 11, 2022 17:27
Copy link
Contributor

@patrickfreed patrickfreed left a comment

Choose a reason for hiding this comment

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

Looks good! I just have some clarifying questions

while let Some(kms_ctx) = scope.next_kms_ctx() {
kms_ctxen.push(Ok(kms_ctx));
}
stream::iter(kms_ctxen)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the stream versoin works basically the same as FuturesUnordered: the current thread polls all the futures, rather than having potentially many threads doing it in parallel.


use super::Operation;

pub(crate) struct RawOutput<Op>(pub(crate) Op);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add a docstring indicating this type's purpose? If I'm understanding it right, it forwards all implementation to the underlying op except that it performs no validation or parsing of the response, is that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, that's correct - added a comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the commit for this didn't actually get pushed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoops, fixed.

Copy link
Contributor

@patrickfreed patrickfreed left a comment

Choose a reason for hiding this comment

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

LGTM! (bar a commit from a previous comment, no need for me to re-review)


use super::Operation;

pub(crate) struct RawOutput<Op>(pub(crate) Op);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the commit for this didn't actually get pushed.

@patrickfreed
Copy link
Contributor

Oh and it looks like the clippy task failed.

@abr-egn
Copy link
Contributor Author

abr-egn commented Aug 16, 2022

Oh and it looks like the clippy task failed.

Looks like those failures are in src/gridfs.rs, unrelated to this PR (@sanav33).

@@ -398,3 +391,140 @@ macro_rules! remove_empty_write_concern {
}

pub(crate) use remove_empty_write_concern;

// A mirror of the `Operation` trait, with default behavior where appropriate. Should only be
// implemented by leaf operation types.
Copy link
Contributor

Choose a reason for hiding this comment

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

what do you mean by leaf operation types here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Derp, my inner terminology leaking out. Updated the comment to clarify.

@@ -20,7 +22,8 @@ fn build() {
command
.body
.get("hello")
.and_then(crate::bson_util::get_int),
.unwrap()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should just remove this rather than updating

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, done. Sorry that I keep forgetting that!

Copy link
Contributor

@isabelatkinson isabelatkinson left a comment

Choose a reason for hiding this comment

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

looks like the format task failed but lgtm otherwise!

@abr-egn abr-egn merged commit 38ba60b into mongodb:main Aug 17, 2022
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.

4 participants