Skip to content

Commit fa819c5

Browse files
author
Nicklas Warming Jacobsen
committed
Add helper functions to Prepare* structs PreparedQuery
1 parent 64a9c0e commit fa819c5

File tree

4 files changed

+77
-32
lines changed

4 files changed

+77
-32
lines changed

src/crypto/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ where
107107
}
108108

109109
// Contains all the necessary information to encrypt the primary key pair
110+
#[derive(Clone)]
110111
pub struct PreparedPrimaryKey {
111112
pub primary_key_parts: PrimaryKeyParts,
112113
pub is_pk_encrypted: bool,

src/encrypted_table/mod.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,26 @@ pub struct PreparedDelete {
112112
}
113113

114114
impl PreparedDelete {
115-
pub fn new<S: Searchable + Identifiable>(k: impl Into<S::PrimaryKey>) -> Self {
116-
let primary_key = PreparedPrimaryKey::new::<S>(k);
115+
pub fn new<S: Searchable>(k: impl Into<S::PrimaryKey>) -> Self {
116+
Self::new_from_parts::<S>(
117+
k.into()
118+
.into_parts(&S::type_name(), S::sort_key_prefix().as_deref()),
119+
)
120+
}
121+
122+
pub fn new_from_parts<S: Searchable>(k: PrimaryKeyParts) -> Self {
123+
let primary_key = PreparedPrimaryKey::new_from_parts::<S>(k);
117124
let protected_indexes = S::protected_indexes();
118125

119126
Self {
120127
primary_key,
121128
protected_indexes,
122129
}
123130
}
131+
132+
pub fn prepared_primary_key(&self) -> PreparedPrimaryKey {
133+
self.primary_key.clone()
134+
}
124135
}
125136

126137
impl PreparedRecord {
@@ -202,6 +213,17 @@ impl PreparedRecord {
202213
sealer,
203214
))
204215
}
216+
217+
pub fn primary_key_parts(&self) -> PrimaryKeyParts {
218+
PrimaryKeyParts {
219+
pk: self.sealer.pk.clone(),
220+
sk: self.sealer.sk.clone(),
221+
}
222+
}
223+
224+
pub fn type_name(&self) -> &str {
225+
&self.sealer.type_name
226+
}
205227
}
206228

207229
impl DynamoRecordPatch {

src/encrypted_table/query.rs

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use cipherstash_client::{
77
},
88
};
99
use itertools::Itertools;
10-
use std::{collections::HashMap, marker::PhantomData};
10+
use std::{borrow::Cow, collections::HashMap, marker::PhantomData};
1111

1212
use crate::{
1313
traits::{Decryptable, Searchable},
@@ -131,11 +131,56 @@ where
131131
S: Searchable,
132132
{
133133
pub fn build(self) -> Result<PreparedQuery, QueryError> {
134-
let items_len = self.parts.len();
134+
PreparedQueryBuilder::new::<S>().build(self.parts)
135+
}
136+
}
137+
138+
impl<S> QueryBuilder<S, &EncryptedTable<Dynamo>>
139+
where
140+
S: Searchable + Identifiable,
141+
{
142+
pub async fn load<T: Decryptable>(self) -> Result<Vec<T>, QueryError> {
143+
let table = self.backend;
144+
let query = self.build()?;
145+
146+
let items = query.send(table).await?;
147+
let results = table.decrypt_all(items).await?;
148+
149+
Ok(results)
150+
}
151+
}
152+
153+
impl<S> QueryBuilder<S, &EncryptedTable<Dynamo>>
154+
where
155+
S: Searchable + Decryptable + Identifiable,
156+
{
157+
pub async fn send(self) -> Result<Vec<S>, QueryError> {
158+
self.load::<S>().await
159+
}
160+
}
161+
162+
pub struct PreparedQueryBuilder {
163+
pub type_name: Cow<'static, str>,
164+
pub index_by_name: fn(&str, IndexType) -> Option<Box<dyn ComposableIndex>>,
165+
}
166+
167+
impl PreparedQueryBuilder {
168+
pub fn new<S: Searchable>() -> Self {
169+
Self {
170+
type_name: S::type_name(),
171+
index_by_name: S::index_by_name,
172+
}
173+
}
174+
175+
pub fn build(
176+
&self,
177+
parts: Vec<(String, SingleIndex, Plaintext)>,
178+
) -> Result<PreparedQuery, QueryError> {
179+
let items_len = parts.len();
135180

136181
// this is the simplest way to brute force the index names but relies on some gross
137182
// stringly typing which doesn't feel good
138-
for perm in self.parts.iter().permutations(items_len) {
183+
for perm in parts.iter().permutations(items_len) {
139184
let (indexes, plaintexts): (Vec<(&String, &SingleIndex)>, Vec<&Plaintext>) =
140185
perm.into_iter().map(|x| ((&x.0, &x.1), &x.2)).unzip();
141186

@@ -170,7 +215,7 @@ where
170215
}
171216
};
172217

173-
if let Some(composed_index) = S::index_by_name(index_name.as_str(), index_type) {
218+
if let Some(composed_index) = (self.index_by_name)(index_name.as_str(), index_type) {
174219
let mut plaintext = ComposablePlaintext::new(plaintexts[0].clone());
175220

176221
for p in plaintexts[1..].iter() {
@@ -181,41 +226,17 @@ where
181226

182227
return Ok(PreparedQuery {
183228
index_name,
184-
type_name: S::type_name().to_string(),
229+
type_name: self.type_name.to_string(),
185230
plaintext,
186231
composed_index,
187232
});
188233
}
189234
}
190235

191-
let fields = self.parts.iter().map(|x| &x.0).join(",");
236+
let fields = parts.iter().map(|x| &x.0).join(",");
192237

193238
Err(QueryError::InvalidQuery(format!(
194239
"Could not build query for fields: {fields}"
195240
)))
196241
}
197242
}
198-
199-
impl<S> QueryBuilder<S, &EncryptedTable<Dynamo>>
200-
where
201-
S: Searchable + Identifiable,
202-
{
203-
pub async fn load<T: Decryptable>(self) -> Result<Vec<T>, QueryError> {
204-
let table = self.backend;
205-
let query = self.build()?;
206-
207-
let items = query.send(table).await?;
208-
let results = table.decrypt_all(items).await?;
209-
210-
Ok(results)
211-
}
212-
}
213-
214-
impl<S> QueryBuilder<S, &EncryptedTable<Dynamo>>
215-
where
216-
S: Searchable + Decryptable + Identifiable,
217-
{
218-
pub async fn send(self) -> Result<Vec<S>, QueryError> {
219-
self.load::<S>().await
220-
}
221-
}

src/traits/primary_key.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#[derive(Clone)]
12
pub struct PrimaryKeyParts {
23
pub pk: String,
34
pub sk: String,

0 commit comments

Comments
 (0)