cipherstash_dynamodb/crypto/
unsealed.rs1use super::{
2 attrs::{FlattenedProtectedAttributes, NormalizedProtectedAttributes},
3 SealError,
4};
5use crate::{
6 encrypted_table::{AttributeName, TableAttribute, TableAttributes},
7 Decryptable,
8};
9use cipherstash_client::encryption::Plaintext;
10use std::collections::HashMap;
11
12pub struct Unsealed {
16 protected: NormalizedProtectedAttributes,
18 unprotected: TableAttributes,
19}
20
21impl Default for Unsealed {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Unsealed {
28 pub fn new() -> Self {
29 Self {
30 protected: NormalizedProtectedAttributes::new(),
31 unprotected: Default::default(),
32 }
33 }
34
35 pub fn new_with_descriptor(descriptor: impl Into<String>) -> Self {
37 Self {
38 protected: NormalizedProtectedAttributes::new_with_prefix(descriptor),
39 unprotected: Default::default(),
40 }
41 }
42
43 pub(crate) fn new_from_parts(
45 protected: NormalizedProtectedAttributes,
46 unprotected: TableAttributes,
47 ) -> Self {
48 let mut unsealed = Self::new();
49 unsealed.protected = protected;
50 unsealed.unprotected = unprotected;
51 unsealed
52 }
53
54 pub(crate) fn new_from_unprotected(unprotected: TableAttributes) -> Self {
56 let mut unsealed = Self::new();
57 unsealed.unprotected = unprotected;
58 unsealed
59 }
60
61 #[deprecated(since = "0.7.3", note = "Use `Unsealed::take_unprotected` instead")]
62 pub fn get_plaintext(&self, name: impl Into<AttributeName>) -> TableAttribute {
63 self.unprotected
64 .get(name)
65 .cloned()
66 .unwrap_or(TableAttribute::Null)
67 }
68
69 pub fn add_protected(&mut self, name: impl Into<String>, plaintext: impl Into<Plaintext>) {
71 self.protected.insert(name, plaintext.into());
72 }
73
74 pub fn add_protected_map(&mut self, name: impl Into<String>, map: HashMap<String, Plaintext>) {
76 self.protected.insert_map(name, map);
77 }
78
79 pub fn add_protected_map_field(
84 &mut self,
85 name: impl Into<String>,
86 subkey: impl Into<String>,
87 value: impl Into<Plaintext>,
88 ) {
89 self.protected
90 .insert_and_update_map(name, subkey, value.into());
91 }
92
93 pub fn add_unprotected(
95 &mut self,
96 name: impl Into<AttributeName>,
97 attribute: impl Into<TableAttribute>,
98 ) {
99 self.unprotected.insert(name, attribute);
100 }
101
102 pub fn take_unprotected(&mut self, name: impl Into<AttributeName>) -> TableAttribute {
107 self.unprotected
108 .remove(name)
109 .unwrap_or(TableAttribute::Null)
110 }
111
112 pub fn take_protected(&mut self, name: &str) -> Option<Plaintext> {
114 self.protected.take(name)
115 }
116
117 pub fn take_protected_map(&mut self, name: &str) -> Option<HashMap<String, Plaintext>> {
120 self.protected.take_map(name)
121 }
122
123 pub(crate) fn flatten_into_parts(self) -> (FlattenedProtectedAttributes, TableAttributes) {
125 (self.protected.flatten(), self.unprotected)
126 }
127
128 pub fn into_value<T: Decryptable>(self) -> Result<T, SealError> {
131 T::from_unsealed(self)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use std::collections::BTreeMap;
139
140 #[test]
141 fn test_protected_field() {
142 let mut unsealed = Unsealed::new_with_descriptor("test");
143 unsealed.add_protected("test", "value");
144
145 let plaintext = unsealed.take_protected("test").unwrap();
146 assert_eq!(plaintext, Plaintext::from("value"));
147 }
148
149 #[test]
150 fn test_protected_map() {
151 let mut unsealed = Unsealed::new_with_descriptor("test");
152 let mut map = HashMap::new();
153 map.insert("a".to_string(), Plaintext::from("value-a"));
154 map.insert("b".to_string(), Plaintext::from("value-b"));
155 map.insert("c".to_string(), Plaintext::from("value-c"));
156 unsealed.add_protected_map("test", map);
157
158 let nested: BTreeMap<String, Plaintext> = unsealed
159 .take_protected_map("test")
160 .unwrap()
161 .into_iter()
162 .collect();
163
164 assert_eq!(nested.len(), 3);
165 assert_eq!(nested["a"], Plaintext::from("value-a"));
166 assert_eq!(nested["b"], Plaintext::from("value-b"));
167 assert_eq!(nested["c"], Plaintext::from("value-c"));
168 }
169
170 #[test]
171 fn test_protected_map_field() {
172 let mut unsealed = Unsealed::new_with_descriptor("test");
173 unsealed.add_protected_map_field("test", "a", "value-a");
174 unsealed.add_protected_map_field("test", "b", "value-b");
175 unsealed.add_protected_map_field("test", "c", "value-c");
176
177 let nested: BTreeMap<String, Plaintext> = unsealed
178 .take_protected_map("test")
179 .unwrap()
180 .into_iter()
181 .collect();
182
183 assert_eq!(nested.len(), 3);
184 assert_eq!(nested["a"], Plaintext::from("value-a"));
185 assert_eq!(nested["b"], Plaintext::from("value-b"));
186 assert_eq!(nested["c"], Plaintext::from("value-c"));
187 }
188
189 #[test]
190 fn test_protected_mixed() {
191 let mut unsealed = Unsealed::new_with_descriptor("test");
192 unsealed.add_protected("test", "value");
193 unsealed.add_protected_map_field("attrs", "a", "value-a");
194 unsealed.add_protected_map_field("attrs", "b", "value-b");
195 unsealed.add_protected_map_field("attrs", "c", "value-c");
196
197 let plaintext = unsealed.take_protected("test").unwrap();
198 assert_eq!(plaintext, Plaintext::from("value"));
199
200 let nested: BTreeMap<String, Plaintext> = unsealed
201 .take_protected_map("attrs")
202 .unwrap()
203 .into_iter()
204 .collect();
205
206 assert_eq!(nested.len(), 3);
207 assert_eq!(nested["a"], Plaintext::from("value-a"));
208 assert_eq!(nested["b"], Plaintext::from("value-b"));
209 assert_eq!(nested["c"], Plaintext::from("value-c"));
210 }
211
212 #[test]
213 #[should_panic]
214 fn test_protected_map_override() {
215 let mut unsealed = Unsealed::new_with_descriptor("test");
216 unsealed.add_protected("test", "value");
217 unsealed.add_protected_map_field("test", "a", "value-a");
219 }
220
221 #[test]
222 fn test_unprotected() {
223 let mut unsealed = Unsealed::new_with_descriptor("test");
224 unsealed.add_unprotected("test", "value");
225
226 let attribute = unsealed.take_unprotected("test");
227 assert!(attribute == "value".into(), "values do not match");
228 }
229}