rusty_oss/credentials/
mod.rs

1//! Credentials management types
2//!
3//! [`RotatingCredentials`] wraps [`Credentials`] and gives the ability to
4//! rotate them at any point in the program, keeping all copies of the same
5//! [`RotatingCredentials`] in sync with the latest version.
6//!
7//! [`EcsSecurityCredentialsMetadataResponse`] parses the response from the
8//! [ECS Instance Metadata](https://help.aliyun.com/zh/ecs/user-guide/view-instance-metadata),
9//! which provides an endpoint for retrieving credentials using the permissions
10//! for the [attached RAM roles](https://help.aliyun.com/zh/ecs/developer-reference/api-ecs-2014-05-26-ram).
11
12use std::env;
13use std::fmt::{self, Debug, Formatter};
14
15#[allow(clippy::module_name_repetitions)]
16pub use self::rotating::RotatingCredentials;
17#[cfg(feature = "full")]
18pub use self::serde::EcsSecurityCredentialsMetadataResponse;
19use zeroize::Zeroizing;
20
21mod rotating;
22#[cfg(feature = "full")]
23mod serde;
24
25/// ALIYUN credentials
26#[derive(Clone, PartialEq, Eq)]
27pub struct Credentials {
28    key: String,
29    secret: Zeroizing<String>,
30    token: Option<String>,
31}
32
33impl Credentials {
34    /// Construct a new `Credentials` using the provided key and secret
35    #[inline]
36    pub fn new(key: impl Into<String>, secret: impl Into<String>) -> Self {
37        Self::new_with_maybe_token(key.into(), secret.into(), None)
38    }
39
40    /// Construct a new `Credentials` using the provided key, secret and token
41    #[inline]
42    pub fn new_with_token(
43        key: impl Into<String>,
44        secret: impl Into<String>,
45        token: impl Into<String>,
46    ) -> Self {
47        Self::new_with_maybe_token(key.into(), secret.into(), Some(token.into()))
48    }
49
50    #[inline]
51    pub(super) fn new_with_maybe_token(key: String, secret: String, token: Option<String>) -> Self {
52        Self {
53            key,
54            secret: Zeroizing::new(secret),
55            token,
56        }
57    }
58
59    /// Construct a new `Credentials` using ALIYUN's default environment variables
60    ///
61    /// Reads the key from the `ALIYUN_ACCESS_KEY_ID` environment variable and the secret
62    /// from the `ALIYUN_ACCESS_KEY_SECRET` environment variable.
63    /// If `ALIYUN_SESSION_TOKEN` is set a token is also read.
64    /// Returns `None` if either environment variables aren't set or they aren't valid utf-8.
65    ///
66    /// see: https://help.aliyun.com/zh/oss/developer-reference/oss-java-configure-access-credentials
67    #[must_use]
68    pub fn from_env() -> Option<Self> {
69        let key = env::var("ALIYUN_ACCESS_KEY_ID").ok()?;
70        let secret = env::var("ALIYUN_ACCESS_KEY_SECRET").ok()?;
71        let token = env::var("ALIYUN_SESSION_TOKEN").ok();
72        Some(Self::new_with_maybe_token(key, secret, token))
73    }
74
75    /// Get the key of this `Credentials`
76    #[inline]
77    #[must_use]
78    pub fn key(&self) -> &str {
79        &self.key
80    }
81
82    /// Get the secret of this `Credentials`
83    #[inline]
84    #[must_use]
85    pub fn secret(&self) -> &str {
86        &self.secret
87    }
88
89    /// Get the token of this `Credentials`, if present
90    #[inline]
91    #[must_use]
92    pub fn token(&self) -> Option<&str> {
93        self.token.as_deref()
94    }
95}
96
97impl Debug for Credentials {
98    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
99        f.debug_struct("Credentials")
100            .field("key", &self.key)
101            .finish_non_exhaustive()
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use pretty_assertions::assert_eq;
108
109    use super::*;
110
111    #[test]
112    fn key_secret() {
113        let credentials = Credentials::new("abcd", "1234");
114        assert_eq!(credentials.key(), "abcd");
115        assert_eq!(credentials.secret(), "1234");
116        assert!(credentials.token().is_none());
117    }
118
119    #[test]
120    fn key_secret_token() {
121        let credentials = Credentials::new_with_token("abcd", "1234", "xyz");
122        assert_eq!(credentials.key(), "abcd");
123        assert_eq!(credentials.secret(), "1234");
124        assert_eq!(credentials.token(), Some("xyz"));
125    }
126
127    #[test]
128    fn debug() {
129        let credentials = Credentials::new("abcd", "1234");
130        let debug_output = format!("{credentials:?}");
131        assert_eq!(debug_output, "Credentials { key: \"abcd\", .. }");
132    }
133
134    #[test]
135    fn debug_token() {
136        let credentials = Credentials::new_with_token("abcd", "1234", "xyz");
137        let debug_output = format!("{credentials:?}");
138        assert_eq!(debug_output, "Credentials { key: \"abcd\", .. }");
139    }
140
141    // failed in development environment
142    //
143    // #[test]
144    // fn from_env() {
145    //     env::set_var("ALIYUN_ACCESS_KEY_ID", "key");
146    //     env::set_var("ALIYUN_SECRET_ACCESS_KEY", "secret");
147
148    //     let credentials = Credentials::from_env().unwrap();
149    //     assert_eq!(credentials.key(), "key");
150    //     assert_eq!(credentials.secret(), "secret");
151    //     assert!(credentials.token().is_none());
152
153    //     env::remove_var("ALIYUN_ACCESS_KEY_ID");
154    //     env::remove_var("ALIYUN_SECRET_ACCESS_KEY");
155
156    //     assert!(Credentials::from_env().is_none());
157    // }
158}