1use askar_storage::backend::{copy_profile, OrderBy};
2
3use crate::{
4 error::Error,
5 kms::{KeyEntry, KeyParams, KeyReference, KmsCategory, LocalKey},
6 storage::{
7 any::{AnyBackend, AnyBackendSession},
8 backend::{Backend, BackendSession, ManageBackend},
9 entry::{Entry, EntryKind, EntryOperation, EntryTag, Scan, TagFilter},
10 generate_raw_store_key,
11 },
12};
13
14pub use crate::storage::{entry, PassKey, StoreKeyMethod};
15
16#[derive(Debug, Clone)]
17pub struct Store(AnyBackend);
19
20impl Store {
21 pub(crate) fn new(inner: AnyBackend) -> Self {
22 Self(inner)
23 }
24
25 pub async fn provision(
27 db_url: &str,
28 key_method: StoreKeyMethod,
29 pass_key: PassKey<'_>,
30 profile: Option<String>,
31 recreate: bool,
32 ) -> Result<Self, Error> {
33 let backend = db_url
34 .provision_backend(key_method, pass_key, profile, recreate)
35 .await?;
36 Ok(Self::new(backend))
37 }
38
39 pub async fn open(
41 db_url: &str,
42 key_method: Option<StoreKeyMethod>,
43 pass_key: PassKey<'_>,
44 profile: Option<String>,
45 ) -> Result<Self, Error> {
46 let backend = db_url.open_backend(key_method, pass_key, profile).await?;
47 Ok(Self::new(backend))
48 }
49
50 pub async fn remove(db_url: &str) -> Result<bool, Error> {
52 Ok(db_url.remove_backend().await?)
53 }
54
55 pub fn new_raw_key(seed: Option<&[u8]>) -> Result<PassKey<'static>, Error> {
57 Ok(generate_raw_store_key(seed)?)
58 }
59
60 pub fn get_active_profile(&self) -> String {
62 self.0.get_active_profile()
63 }
64
65 pub async fn get_default_profile(&self) -> Result<String, Error> {
67 Ok(self.0.get_default_profile().await?)
68 }
69
70 pub async fn set_default_profile(&self, profile: String) -> Result<(), Error> {
72 Ok(self.0.set_default_profile(profile).await?)
73 }
74
75 pub async fn rekey(
77 &mut self,
78 method: StoreKeyMethod,
79 pass_key: PassKey<'_>,
80 ) -> Result<(), Error> {
81 Ok(self.0.rekey(method, pass_key).await?)
82 }
83
84 pub async fn copy_to(
86 &self,
87 target_url: &str,
88 key_method: StoreKeyMethod,
89 pass_key: PassKey<'_>,
90 recreate: bool,
91 ) -> Result<Self, Error> {
92 let default_profile = self.get_default_profile().await?;
93 let profile_ids = self.list_profiles().await?;
94 let target = target_url
95 .provision_backend(key_method, pass_key, Some(default_profile), recreate)
96 .await?;
97 for profile in profile_ids {
98 copy_profile(&self.0, &target, &profile, &profile).await?;
99 }
100 Ok(Self::new(target))
101 }
102
103 pub async fn copy_profile_to(
105 &self,
106 target: &Store,
107 from_name: &str,
108 to_name: &str,
109 ) -> Result<(), Error> {
110 copy_profile(&self.0, &target.0, from_name, to_name).await?;
111 Ok(())
112 }
113
114 pub async fn create_profile(&self, name: Option<String>) -> Result<String, Error> {
116 Ok(self.0.create_profile(name).await?)
117 }
118
119 pub async fn list_profiles(&self) -> Result<Vec<String>, Error> {
121 Ok(self.0.list_profiles().await?)
122 }
123
124 pub async fn remove_profile(&self, name: String) -> Result<bool, Error> {
126 Ok(self.0.remove_profile(name).await?)
127 }
128
129 pub async fn rename_profile(
131 &self,
132 from_profile: String,
133 to_profile: String,
134 ) -> Result<bool, Error> {
135 Ok(self.0.rename_profile(from_profile, to_profile).await?)
136 }
137 #[allow(clippy::too_many_arguments)]
141 pub async fn scan(
142 &self,
143 profile: Option<String>,
144 category: Option<String>,
145 tag_filter: Option<TagFilter>,
146 offset: Option<i64>,
147 limit: Option<i64>,
148 order_by: Option<OrderBy>,
149 descending: bool,
150 ) -> Result<Scan<'static, Entry>, Error> {
151 Ok(self
152 .0
153 .scan(
154 profile,
155 Some(EntryKind::Item),
156 category,
157 tag_filter,
158 offset,
159 limit,
160 order_by,
161 descending,
162 )
163 .await?)
164 }
165
166 pub async fn session(&self, profile: Option<String>) -> Result<Session, Error> {
168 let mut sess = Session::new(self.0.session(profile, false)?);
169 if let Err(e) = sess.ping().await {
170 sess.0.close(false).await?;
171 Err(e)
172 } else {
173 Ok(sess)
174 }
175 }
176
177 pub async fn transaction(&self, profile: Option<String>) -> Result<Session, Error> {
179 let mut txn = Session::new(self.0.session(profile, true)?);
180 if let Err(e) = txn.ping().await {
181 txn.0.close(false).await?;
182 Err(e)
183 } else {
184 Ok(txn)
185 }
186 }
187
188 pub async fn close(self) -> Result<(), Error> {
190 Ok(self.0.close().await?)
191 }
192}
193
194impl From<AnyBackend> for Store {
195 fn from(backend: AnyBackend) -> Self {
196 Self::new(backend)
197 }
198}
199
200#[derive(Debug)]
202pub struct Session(AnyBackendSession);
203
204impl Session {
205 pub(crate) fn new(inner: AnyBackendSession) -> Self {
206 Self(inner)
207 }
208
209 pub async fn count(
211 &mut self,
212 category: Option<&str>,
213 tag_filter: Option<TagFilter>,
214 ) -> Result<i64, Error> {
215 Ok(self
216 .0
217 .count(Some(EntryKind::Item), category, tag_filter)
218 .await?)
219 }
220
221 pub async fn fetch(
226 &mut self,
227 category: &str,
228 name: &str,
229 for_update: bool,
230 ) -> Result<Option<Entry>, Error> {
231 Ok(self
232 .0
233 .fetch(EntryKind::Item, category, name, for_update)
234 .await?)
235 }
236
237 pub async fn fetch_all(
243 &mut self,
244 category: Option<&str>,
245 tag_filter: Option<TagFilter>,
246 limit: Option<i64>,
247 order_by: Option<OrderBy>,
248 descending: bool,
249 for_update: bool,
250 ) -> Result<Vec<Entry>, Error> {
251 Ok(self
252 .0
253 .fetch_all(
254 Some(EntryKind::Item),
255 category,
256 tag_filter,
257 limit,
258 order_by,
259 descending,
260 for_update,
261 )
262 .await?)
263 }
264
265 pub async fn insert(
267 &mut self,
268 category: &str,
269 name: &str,
270 value: &[u8],
271 tags: Option<&[EntryTag]>,
272 expiry_ms: Option<i64>,
273 ) -> Result<(), Error> {
274 Ok(self
275 .0
276 .update(
277 EntryKind::Item,
278 EntryOperation::Insert,
279 category,
280 name,
281 Some(value),
282 tags,
283 expiry_ms,
284 )
285 .await?)
286 }
287
288 pub async fn remove(&mut self, category: &str, name: &str) -> Result<(), Error> {
290 Ok(self
291 .0
292 .update(
293 EntryKind::Item,
294 EntryOperation::Remove,
295 category,
296 name,
297 None,
298 None,
299 None,
300 )
301 .await?)
302 }
303
304 pub async fn replace(
306 &mut self,
307 category: &str,
308 name: &str,
309 value: &[u8],
310 tags: Option<&[EntryTag]>,
311 expiry_ms: Option<i64>,
312 ) -> Result<(), Error> {
313 Ok(self
314 .0
315 .update(
316 EntryKind::Item,
317 EntryOperation::Replace,
318 category,
319 name,
320 Some(value),
321 tags,
322 expiry_ms,
323 )
324 .await?)
325 }
326
327 pub async fn remove_all(
329 &mut self,
330 category: Option<&str>,
331 tag_filter: Option<TagFilter>,
332 ) -> Result<i64, Error> {
333 Ok(self
334 .0
335 .remove_all(Some(EntryKind::Item), category, tag_filter)
336 .await?)
337 }
338
339 pub async fn update(
344 &mut self,
345 operation: EntryOperation,
346 category: &str,
347 name: &str,
348 value: Option<&[u8]>,
349 tags: Option<&[EntryTag]>,
350 expiry_ms: Option<i64>,
351 ) -> Result<(), Error> {
352 Ok(self
353 .0
354 .update(
355 EntryKind::Item,
356 operation,
357 category,
358 name,
359 value,
360 tags,
361 expiry_ms,
362 )
363 .await?)
364 }
365
366 pub async fn insert_key(
368 &mut self,
369 name: &str,
370 key: &LocalKey,
371 metadata: Option<&str>,
372 reference: Option<KeyReference>,
373 tags: Option<&[EntryTag]>,
374 expiry_ms: Option<i64>,
375 ) -> Result<(), Error> {
376 let data = if key.is_hardware_backed() {
377 key.inner.key_id()?
378 } else {
379 key.encode()?
380 };
381 let params = KeyParams {
382 metadata: metadata.map(str::to_string),
383 reference,
384 data: Some(data),
385 };
386 let value = params.to_bytes()?;
387 let mut ins_tags = Vec::with_capacity(10);
388 let alg = key.algorithm().as_str();
389 if !alg.is_empty() {
390 ins_tags.push(EntryTag::Encrypted("alg".to_string(), alg.to_string()));
391 }
392 let thumbs = key.to_jwk_thumbprints()?;
393 for thumb in thumbs {
394 ins_tags.push(EntryTag::Encrypted("thumb".to_string(), thumb));
395 }
396 if let Some(tags) = tags {
397 for t in tags {
398 ins_tags.push(t.map_ref(|k, v| (format!("user:{}", k), v.to_string())));
399 }
400 }
401 self.0
402 .update(
403 EntryKind::Kms,
404 EntryOperation::Insert,
405 KmsCategory::CryptoKey.as_str(),
406 name,
407 Some(value.as_ref()),
408 Some(ins_tags.as_slice()),
409 expiry_ms,
410 )
411 .await?;
412 Ok(())
413 }
414
415 pub async fn fetch_key(
420 &mut self,
421 name: &str,
422 for_update: bool,
423 ) -> Result<Option<KeyEntry>, Error> {
424 Ok(
425 if let Some(row) = self
426 .0
427 .fetch(
428 EntryKind::Kms,
429 KmsCategory::CryptoKey.as_str(),
430 name,
431 for_update,
432 )
433 .await?
434 {
435 Some(KeyEntry::from_entry(row)?)
436 } else {
437 None
438 },
439 )
440 }
441
442 pub async fn fetch_all_keys(
444 &mut self,
445 algorithm: Option<&str>,
446 thumbprint: Option<&str>,
447 tag_filter: Option<TagFilter>,
448 limit: Option<i64>,
449 for_update: bool,
450 ) -> Result<Vec<KeyEntry>, Error> {
451 let mut query_parts = Vec::with_capacity(3);
452 if let Some(query) = tag_filter.map(|f| f.into_query()) {
453 query_parts.push(TagFilter::from(
454 query
455 .map_names(|mut k| {
456 k.replace_range(0..0, "user:");
457 Result::<_, ()>::Ok(k)
458 })
459 .unwrap(),
460 ));
461 }
462 if let Some(algorithm) = algorithm {
463 query_parts.push(TagFilter::is_eq("alg", algorithm));
464 }
465 if let Some(thumbprint) = thumbprint {
466 query_parts.push(TagFilter::is_eq("thumb", thumbprint));
467 }
468 let tag_filter = if query_parts.is_empty() {
469 None
470 } else {
471 Some(TagFilter::all_of(query_parts))
472 };
473 let rows = self
474 .0
475 .fetch_all(
476 Some(EntryKind::Kms),
477 Some(KmsCategory::CryptoKey.as_str()),
478 tag_filter,
479 limit,
480 None,
481 false,
482 for_update,
483 )
484 .await?;
485 let mut entries = Vec::with_capacity(rows.len());
486 for row in rows {
487 entries.push(KeyEntry::from_entry(row)?)
488 }
489 Ok(entries)
490 }
491
492 pub async fn remove_key(&mut self, name: &str) -> Result<(), Error> {
494 Ok(self
495 .0
496 .update(
497 EntryKind::Kms,
498 EntryOperation::Remove,
499 KmsCategory::CryptoKey.as_str(),
500 name,
501 None,
502 None,
503 None,
504 )
505 .await?)
506 }
507
508 pub async fn update_key(
510 &mut self,
511 name: &str,
512 metadata: Option<&str>,
513 tags: Option<&[EntryTag]>,
514 expiry_ms: Option<i64>,
515 ) -> Result<(), Error> {
516 let row = self
517 .0
518 .fetch(EntryKind::Kms, KmsCategory::CryptoKey.as_str(), name, true)
519 .await?
520 .ok_or_else(|| err_msg!(NotFound, "Key entry not found"))?;
521
522 let mut params = KeyParams::from_slice(&row.value)?;
523 params.metadata = metadata.map(str::to_string);
524 let value = params.to_bytes()?;
525
526 let mut upd_tags = Vec::with_capacity(10);
527 if let Some(tags) = tags {
528 for t in tags {
529 upd_tags.push(t.map_ref(|k, v| (format!("user:{}", k), v.to_string())));
530 }
531 }
532 for t in row.tags {
533 if !t.name().starts_with("user:") {
534 upd_tags.push(t);
535 }
536 }
537
538 self.0
539 .update(
540 EntryKind::Kms,
541 EntryOperation::Replace,
542 KmsCategory::CryptoKey.as_str(),
543 name,
544 Some(value.as_ref()),
545 Some(upd_tags.as_slice()),
546 expiry_ms,
547 )
548 .await?;
549
550 Ok(())
551 }
552
553 pub async fn ping(&mut self) -> Result<(), Error> {
555 Ok(self.0.ping().await?)
556 }
557
558 pub async fn commit(mut self) -> Result<(), Error> {
560 Ok(self.0.close(true).await?)
561 }
562
563 pub async fn rollback(mut self) -> Result<(), Error> {
565 Ok(self.0.close(false).await?)
566 }
567}