kraken_async_rs/secrets/
secrets_provider.rs

1//! Trait and implementations for retrieving API keys and secrets needed for private calls
2use dotenvy::dotenv;
3use secrecy::SecretString;
4use std::env;
5use std::fmt::Debug;
6
7/// A struct containing the API key and secret (using [secrecy::Secret])
8#[derive(Debug, Clone)]
9pub struct Secrets {
10    pub key: SecretString,
11    pub secret: SecretString,
12}
13
14/// Trait that exposes a method for retrieving secrets.
15///
16/// Clients are generic over [SecretsProvider] so the client can specify how to retrieve the API
17/// key and secret at runtime.
18pub trait SecretsProvider: Send + Sync + Debug {
19    fn get_secrets(&mut self) -> Secrets;
20}
21
22/// A common implementation that retrieves the key and secret from the given environment variable names.
23///
24/// This retrieves secrets once from the environment and caches them. If your use case requires
25/// retrieving them each time, a custom implementation may be your best choice.
26#[derive(Debug, Clone)]
27pub struct EnvSecretsProvider<'a> {
28    key_name: &'a str,
29    secret_name: &'a str,
30    secrets: Option<Secrets>,
31}
32
33impl<'a> EnvSecretsProvider<'a> {
34    /// Creates an instance that will retrieve secrets by environment variables, looking for `key_name`
35    /// and `secret_name`.
36    pub fn new(key_name: &'a str, secret_name: &'a str) -> EnvSecretsProvider<'a> {
37        EnvSecretsProvider {
38            key_name,
39            secret_name,
40            secrets: None,
41        }
42    }
43}
44
45impl SecretsProvider for EnvSecretsProvider<'_> {
46    fn get_secrets(&mut self) -> Secrets {
47        if self.secrets.is_none() {
48            self.set_secrets();
49        }
50
51        self.secrets.clone().unwrap()
52    }
53}
54
55impl EnvSecretsProvider<'_> {
56    fn set_secrets(&mut self) {
57        dotenv().ok();
58        let key = SecretString::new(match env::var(self.key_name) {
59            Ok(secret) => secret.into(),
60            _ => panic!("Error retrieving Kraken key from env"),
61        });
62
63        let secret = SecretString::new(match env::var(self.secret_name) {
64            Ok(secret) => secret.into(),
65            _ => panic!("Error retrieving Kraken secret from env"),
66        });
67
68        self.secrets = Some(Secrets { key, secret });
69    }
70}
71
72/// A [SecretsProvider] that stores the key and secret directly. This is useful if you don't wish
73/// to provide a custom implementation, and will directly instantiate a [StaticSecretsProvider] with
74/// your key and secret.
75///
76/// *This is not recommended for use outside of testing!* It is relatively unsafe to store the key
77/// and secret as plain text outside of secrecy, and would be downright unsafe to store the key
78/// and secret in source-code by directly creating a [StaticSecretsProvider] with `'static` strings.
79#[derive(Debug, Clone)]
80pub struct StaticSecretsProvider<'a> {
81    key: &'a str,
82    secret: &'a str,
83}
84
85impl<'a> StaticSecretsProvider<'a> {
86    pub fn new(key: &'a str, secret: &'a str) -> StaticSecretsProvider<'a> {
87        StaticSecretsProvider { key, secret }
88    }
89}
90
91impl SecretsProvider for StaticSecretsProvider<'_> {
92    fn get_secrets(&mut self) -> Secrets {
93        Secrets {
94            key: self.key.to_string().into(),
95            secret: self.secret.to_string().into(),
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::secrets::secrets_provider::{EnvSecretsProvider, SecretsProvider};
103    use secrecy::ExposeSecret;
104
105    #[test]
106    fn test_env_secrets_provider() {
107        let key_name = "TEST_KEY";
108        let secret_name = "TEST_SECRET";
109        let key = "api-key";
110        let secret = "api-secret";
111
112        std::env::set_var(key_name, key);
113        std::env::set_var(secret_name, secret);
114
115        let mut secrets_provider = EnvSecretsProvider::new(key_name, secret_name);
116
117        let secrets = secrets_provider.get_secrets();
118
119        assert_eq!(key, secrets.key.expose_secret());
120        assert_eq!(secret, secrets.secret.expose_secret());
121    }
122}