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::prelude::BASE64_STANDARD;
6use base64::Engine;
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(
29 Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize,
30)]
31pub struct Credentials {
32 r3_access_key_id: String,
33 r3_secret_access_key: String,
34 #[serde(skip)] // Don't want to serialize this one
35 key: Vec<u8>,
36}
37
38#[bon]
39impl Credentials {
40 /// Validated the given secret access key and creates a new [`Credentials`] struct.
41 ///
42 /// # Errors
43 /// - [`base64::DecodeError`] if the secret access key is not base64 encoded.
44 ///
45 /// # Example
46 /// ```
47 /// # use remoteit_api::Credentials;
48 /// let credentials = Credentials::builder()
49 /// .r3_access_key_id("foo".to_owned())
50 /// .r3_secret_access_key("YmFy".to_owned())
51 /// .build();
52 /// ```
53 #[builder]
54 pub fn new(
55 r3_access_key_id: String,
56 r3_secret_access_key: String,
57 ) -> Result<Self, base64::DecodeError> {
58 let key = BASE64_STANDARD.decode(&r3_secret_access_key)?;
59 Ok(Self {
60 r3_access_key_id,
61 r3_secret_access_key,
62 key,
63 })
64 }
65
66 /// # Returns
67 /// The base64 decoded secret access key.
68 #[must_use]
69 pub fn key(&self) -> &[u8] {
70 &self.key
71 }
72
73 /// # Returns
74 /// A reference to the r3_access_key_id
75 #[allow(clippy::must_use_candidate)]
76 pub fn access_key_id(&self) -> &str {
77 &self.r3_access_key_id
78 }
79
80 /// # Returns
81 /// The base64 encoded r3_secret_access_key
82 #[allow(clippy::must_use_candidate)]
83 pub fn secret_access_key(&self) -> &str {
84 &self.r3_secret_access_key
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_credentials_builder() {
94 let credentials = Credentials::builder()
95 .r3_access_key_id("foo".to_owned())
96 .r3_secret_access_key("YmFy".to_owned())
97 .build()
98 .unwrap();
99
100 assert_eq!(credentials.r3_access_key_id, "foo");
101 assert_eq!(credentials.r3_secret_access_key, "YmFy");
102 }
103
104 #[test]
105 fn test_get_key() {
106 let key = vec![1, 2, 3, 4];
107 let credentials = Credentials::builder()
108 .r3_access_key_id(String::new())
109 .r3_secret_access_key(BASE64_STANDARD.encode(&key))
110 .build()
111 .unwrap();
112
113 assert_eq!(key.as_slice(), credentials.key());
114 }
115
116 #[test]
117 fn test_get_access_key_id() {
118 let credentials = Credentials::builder()
119 .r3_access_key_id("foo".to_string())
120 .r3_secret_access_key("YmFy".to_owned())
121 .build()
122 .unwrap();
123
124 assert_eq!("foo", credentials.access_key_id());
125 }
126
127 #[test]
128 fn test_get_secret_access_key() {
129 let credentials = Credentials::builder()
130 .r3_access_key_id(String::new())
131 .r3_secret_access_key("YmFy".to_owned())
132 .build()
133 .unwrap();
134
135 assert_eq!(credentials.secret_access_key(), "YmFy");
136 }
137}