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}