t_rust_less_lib/service/
secrets_provider.rs1use crate::api::{ClipboardProviding, SecretVersion, PROPERTY_TOTP_URL};
2use crate::clipboard::SelectionProvider;
3use crate::otp::OTPAuthUrl;
4use log::{error, info};
5use std::time::{SystemTime, UNIX_EPOCH};
6use zeroize::{Zeroize, Zeroizing};
7
8#[derive(Clone, Zeroize)]
9#[zeroize(drop)]
10pub struct SecretsProvider {
11 store_name: String,
12 block_id: String,
13 secret_version: SecretVersion,
14 properties_stack: Vec<String>,
15}
16
17impl SecretsProvider {
18 pub fn new(store_name: String, block_id: String, secret_version: SecretVersion, properties: &[&str]) -> Self {
19 let properties_stack = properties
20 .iter()
21 .filter(|p| secret_version.properties.has_non_empty(p))
22 .rev()
23 .map(ToString::to_string)
24 .collect();
25 SecretsProvider {
26 store_name,
27 block_id,
28 secret_version,
29 properties_stack,
30 }
31 }
32}
33
34impl SelectionProvider for SecretsProvider {
35 fn current_selection(&self) -> Option<ClipboardProviding> {
36 self
37 .properties_stack
38 .last()
39 .cloned()
40 .map(|property| ClipboardProviding {
41 store_name: self.store_name.clone(),
42 block_id: self.block_id.clone(),
43 secret_name: self.secret_version.name.clone(),
44 property,
45 })
46 }
47
48 fn get_selection_value(&self) -> Option<Zeroizing<String>> {
49 let property = self.properties_stack.last()?;
50 let value = self.secret_version.properties.get(property)?;
51
52 if property == PROPERTY_TOTP_URL {
53 info!("Providing TOTP of {}", self.secret_version.secret_id);
54 match OTPAuthUrl::parse(value) {
55 Ok(otpauth) => {
56 let (token, _) = otpauth.generate(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs());
57 Some(Zeroizing::new(token))
58 }
59 Err(error) => {
60 error!("Invalid OTPAuth url: {error}");
61 None
62 }
63 }
64 } else {
65 info!("Providing {} of {}", property, self.secret_version.secret_id);
66 Some(Zeroizing::new(value.clone()))
67 }
68 }
69
70 fn next_selection(&mut self) {
71 self.properties_stack.pop();
72 }
73}