kraken_async_rs/secrets/
secrets_provider.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Trait and implementations for retrieving API keys and secrets needed for private calls
use dotenvy::dotenv;
use secrecy::Secret;
use std::env;
use std::fmt::Debug;

/// A struct containing the API key and secret (using [secrecy::Secret])
#[derive(Debug, Clone)]
pub struct Secrets {
    pub key: Secret<String>,
    pub secret: Secret<String>,
}

/// Trait that exposes a method for retrieving secrets.
///
/// Clients are generic over [SecretsProvider] so the client can specify how to retrieve the API
/// key and secret at runtime.
pub trait SecretsProvider: Send + Sync + Debug {
    fn get_secrets(&mut self) -> Secrets;
}

/// A common implementation that retrieves the key and secret from the given environment variable names.
///
/// This retrieves secrets once from the environment and caches them. If your use case requires
/// retrieving them each time, a custom implementation may be your best choice.
#[derive(Debug, Clone)]
pub struct EnvSecretsProvider<'a> {
    key_name: &'a str,
    secret_name: &'a str,
    secrets: Option<Secrets>,
}

impl<'a> EnvSecretsProvider<'a> {
    /// Creates an instance that will retrieve secrets by environment variables, looking for `key_name`
    /// and `secret_name`.
    pub fn new(key_name: &'a str, secret_name: &'a str) -> EnvSecretsProvider<'a> {
        EnvSecretsProvider {
            key_name,
            secret_name,
            secrets: None,
        }
    }
}

impl<'a> SecretsProvider for EnvSecretsProvider<'a> {
    fn get_secrets(&mut self) -> Secrets {
        if self.secrets.is_none() {
            self.set_secrets();
        }

        self.secrets.clone().unwrap()
    }
}

impl<'a> EnvSecretsProvider<'a> {
    fn set_secrets(&mut self) {
        dotenv().ok();
        let key = Secret::new(match env::var(self.key_name) {
            Ok(secret) => secret,
            _ => panic!("Error retrieving Kraken key from env"),
        });

        let secret = Secret::new(match env::var(self.secret_name) {
            Ok(secret) => secret,
            _ => panic!("Error retrieving Kraken secret from env"),
        });

        self.secrets = Some(Secrets { key, secret });
    }
}

/// A [SecretsProvider] that stores the key and secret directly. This is useful if you don't wish
/// to provide a custom implementation, and will directly instantiate a [StaticSecretsProvider] with
/// your key and secret.
///
/// *This is not recommended for use outside of testing!* It is relatively unsafe to store the key
/// and secret as plain text outside of secrecy, and would be downright unsafe to store the key
/// and secret in source-code by directly creating a [StaticSecretsProvider] with `'static` strings.
#[derive(Debug, Clone)]
pub struct StaticSecretsProvider<'a> {
    key: &'a str,
    secret: &'a str,
}

impl<'a> StaticSecretsProvider<'a> {
    pub fn new(key: &'a str, secret: &'a str) -> StaticSecretsProvider<'a> {
        StaticSecretsProvider { key, secret }
    }
}

impl<'a> SecretsProvider for StaticSecretsProvider<'a> {
    fn get_secrets(&mut self) -> Secrets {
        Secrets {
            key: Secret::new(self.key.to_string()),
            secret: Secret::new(self.secret.to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::secrets::secrets_provider::{EnvSecretsProvider, SecretsProvider};
    use secrecy::ExposeSecret;

    #[test]
    fn test_env_secrets_provider() {
        let key_name = "TEST_KEY";
        let secret_name = "TEST_SECRET";
        let key = "api-key";
        let secret = "api-secret";

        std::env::set_var(key_name, key);
        std::env::set_var(secret_name, secret);

        let mut secrets_provider = EnvSecretsProvider::new(key_name, secret_name);

        let secrets = secrets_provider.get_secrets();

        assert_eq!(key, secrets.key.expose_secret());
        assert_eq!(secret, secrets.secret.expose_secret());
    }
}