Skip to content

Disallow setting query_type for encrypt_expression #1275

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

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions src/action/csfle/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ impl ClientEncryption {
/// Encrypts a Match Expression or Aggregate Expression to query a range index.
/// `expression` is expected to be a BSON document of one of the following forms:
/// 1. A Match Expression of this form:
/// {$and: [{<field>: {$gt: <value1>}}, {<field>: {$lt: <value2> }}]}
/// `{$and: [{<field>: {$gt: <value1>}}, {<field>: {$lt: <value2> }}]}`
/// 2. An Aggregate Expression of this form:
/// {$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}]
/// $gt may also be $gte. $lt may also be $lte.
/// `{$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}]`
///
/// For either expression, `$gt` may also be `$gte`, and `$lt` may also be `$lte`.
///
/// The expression will be encrypted using the [`Algorithm::Range`] algorithm and the
/// "range" query type.
/// "range" query type. It is not valid to set a query type in [`EncryptOptions`] when calling
/// this method.
///
/// `await` will return a d[`Result<Document>`] containing the encrypted expression.
#[deeplink]
Expand All @@ -61,10 +63,7 @@ impl ClientEncryption {
mode: Expression { value: expression },
key: key.into(),
algorithm: Algorithm::Range,
options: Some(EncryptOptions {
query_type: Some("range".into()),
..Default::default()
}),
options: None,
}
}
}
Expand Down Expand Up @@ -110,14 +109,17 @@ pub struct Expression {
}

/// Options for encrypting a value.
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
#[non_exhaustive]
#[export_tokens]
pub struct EncryptOptions {
/// The contention factor.
pub contention_factor: Option<i64>,

/// The query type.
pub query_type: Option<String>,

/// Set the range options. This should only be set when the algorithm is
/// [`Algorithm::Range`].
pub range_options: Option<RangeOptions>,
Expand Down
15 changes: 14 additions & 1 deletion src/client/csfle/client_encryption/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,20 @@ impl<'a> Action for Encrypt<'a, Value> {
impl<'a> Action for Encrypt<'a, Expression> {
type Future = EncryptExpressionFuture;

async fn execute(self) -> Result<Document> {
async fn execute(mut self) -> Result<Document> {
let options = self.options.get_or_insert_with(Default::default);
match options.query_type {
Some(ref query_type) => {
if query_type != "range" {
return Err(Error::invalid_argument(format!(
"query_type cannot be set for encrypt_expression, got {}",
query_type
)));
}
}
None => options.query_type = Some("range".to_string()),
}

let ctx = self
.client_enc
.get_ctx_builder(self.key, self.algorithm, self.options.unwrap_or_default())?
Expand Down
2 changes: 2 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
//! .build();
//! ```

#[cfg(feature = "in-use-encryption")]
pub use crate::action::csfle::{DataKeyOptions, EncryptOptions};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These were missing from the options module. They're currently importable from the action::csfle module, but that's inconsistent with our other option types, so I thought it was worth it to add them here as well.

#[cfg(any(
feature = "zstd-compression",
feature = "zlib-compression",
Expand Down
49 changes: 49 additions & 0 deletions src/test/csfle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use crate::{
options::{
CollectionOptions,
Credential,
EncryptOptions,
FindOptions,
IndexOptions,
ReadConcern,
Expand Down Expand Up @@ -3602,3 +3603,51 @@ async fn fle2_example() -> Result<()> {

Ok(())
}

#[tokio::test]
async fn encrypt_expression_with_options() {
let key_vault_client = Client::for_test().await.into_client();
let client_encryption = ClientEncryption::new(
key_vault_client,
KV_NAMESPACE.clone(),
vec![LOCAL_KMS.clone()],
)
.unwrap();
let data_key = client_encryption
.create_data_key(LocalMasterKey::builder().build())
.await
.unwrap();

let expression = rawdoc! {
"$and": [
{ "a": { "$gt": 0 } },
{ "a": { "$lt": 10 } },
]
};
let range_options = RangeOptions::builder()
.min(Bson::from(0))
.max(Bson::from(10))
.build();

let invalid_encrypt_options = EncryptOptions::builder()
.contention_factor(0)
.range_options(range_options.clone())
.query_type("bad".to_string())
.build();
let error = client_encryption
.encrypt_expression(expression.clone(), data_key.clone())
.with_options(invalid_encrypt_options)
.await
.unwrap_err();
assert!(matches!(*error.kind, ErrorKind::InvalidArgument { .. }));

let valid_encrypt_options = EncryptOptions::builder()
.contention_factor(0)
.range_options(range_options)
.build();
client_encryption
.encrypt_expression(expression, data_key)
.with_options(valid_encrypt_options)
.await
.unwrap();
}