remoteit_api/
credentials.rs

1//! This module is related to loading remote.it credentials from the user's home directory.
2//! This is of course not the most secure way to store credentials, but it is the most convenient and recommended by remote.it.
3//! If you store your credentials in a different way, you can pass them to the functions in this module directly instead of using this module to load them.
4
5use base64::Engine;
6use base64::prelude::BASE64_STANDARD;
7use bon::bon;
8
9/// Credentials for the remote.it API.
10/// Remote.it credentials consist of an access key ID and a base64 encoded secret access key.
11///
12/// # Example
13/// You can directly create a new [`Credentials`] struct using the builder pattern:
14/// ```
15/// # use remoteit_api::Credentials;
16/// let credentials = Credentials::builder()
17///     .r3_access_key_id("foo".to_owned())
18///     .r3_secret_access_key("YmFy".to_owned())
19///     .build();
20/// ```
21/// If you enable the `credentials_loader` feature, you can also load the credentials from the default, or a custom file:
22/// ```
23/// # use std::path::PathBuf;
24/// # use remoteit_api::Credentials;
25/// let creds_from_default_loc = Credentials::load_from_disk().call().unwrap();
26/// let creds_from_custom_loc = Credentials::load_from_disk().custom_credentials_path(PathBuf::from(".env.remoteit")).call().unwrap();
27/// ```
28#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
29pub struct Credentials {
30    r3_access_key_id: String,
31    r3_secret_access_key: String,
32    #[serde(skip)] // Don't want to serialize this one
33    key: Vec<u8>,
34}
35
36#[bon]
37impl Credentials {
38    /// Validated the given secret access key and creates a new [`Credentials`] struct.
39    ///
40    /// # Errors
41    /// - [`base64::DecodeError`] if the secret access key is not base64 encoded.
42    ///
43    /// # Example
44    /// ```
45    /// # use remoteit_api::Credentials;
46    /// let credentials = Credentials::builder()
47    ///     .r3_access_key_id("foo".to_owned())
48    ///     .r3_secret_access_key("YmFy".to_owned())
49    ///     .build();
50    /// ```
51    #[builder]
52    pub fn new(
53        r3_access_key_id: String,
54        r3_secret_access_key: String,
55    ) -> Result<Self, base64::DecodeError> {
56        let key = BASE64_STANDARD.decode(&r3_secret_access_key)?;
57        Ok(Self {
58            r3_access_key_id,
59            r3_secret_access_key,
60            key,
61        })
62    }
63
64    /// # Returns
65    /// The base64 decoded secret access key.
66    #[must_use]
67    pub fn key(&self) -> &[u8] {
68        &self.key
69    }
70
71    /// # Returns
72    /// A reference to the r3_access_key_id
73    #[allow(clippy::must_use_candidate)]
74    pub fn access_key_id(&self) -> &str {
75        &self.r3_access_key_id
76    }
77
78    /// # Returns
79    /// The base64 encoded r3_secret_access_key
80    #[allow(clippy::must_use_candidate)]
81    pub fn secret_access_key(&self) -> &str {
82        &self.r3_secret_access_key
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_credentials_builder() {
92        let credentials = Credentials::builder()
93            .r3_access_key_id("foo".to_owned())
94            .r3_secret_access_key("YmFy".to_owned())
95            .build()
96            .unwrap();
97
98        assert_eq!(credentials.r3_access_key_id, "foo");
99        assert_eq!(credentials.r3_secret_access_key, "YmFy");
100    }
101
102    #[test]
103    fn test_get_key() {
104        let key = vec![1, 2, 3, 4];
105        let credentials = Credentials::builder()
106            .r3_access_key_id(String::new())
107            .r3_secret_access_key(BASE64_STANDARD.encode(&key))
108            .build()
109            .unwrap();
110
111        assert_eq!(key.as_slice(), credentials.key());
112    }
113
114    #[test]
115    fn test_get_access_key_id() {
116        let credentials = Credentials::builder()
117            .r3_access_key_id("foo".to_string())
118            .r3_secret_access_key("YmFy".to_owned())
119            .build()
120            .unwrap();
121
122        assert_eq!("foo", credentials.access_key_id());
123    }
124
125    #[test]
126    fn test_get_secret_access_key() {
127        let credentials = Credentials::builder()
128            .r3_access_key_id(String::new())
129            .r3_secret_access_key("YmFy".to_owned())
130            .build()
131            .unwrap();
132
133        assert_eq!(credentials.secret_access_key(), "YmFy");
134    }
135}