rusoto_sts/custom/
web_identity.rs1use crate::custom::credential::NewAwsCredsForStsCreds;
2use crate::{AssumeRoleWithWebIdentityRequest, Sts, StsClient, PolicyDescriptorType};
3use rusoto_core::credential::{
4 AwsCredentials, CredentialsError, ProvideAwsCredentials, Secret, Variable,
5};
6use rusoto_core::request::HttpClient;
7use rusoto_core::{Client, Region};
8
9use async_trait::async_trait;
10
11const AWS_WEB_IDENTITY_TOKEN_FILE: &str = "AWS_WEB_IDENTITY_TOKEN_FILE";
12
13const AWS_ROLE_ARN: &str = "AWS_ROLE_ARN";
14
15const AWS_ROLE_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME";
16
17#[derive(Debug, Clone)]
22pub struct WebIdentityProvider {
23 pub web_identity_token: Variable<Secret, CredentialsError>,
27 pub role_arn: Variable<String, CredentialsError>,
29 pub role_session_name: Option<Variable<Option<String>, CredentialsError>>,
34
35 pub duration_seconds: Option<i64>,
38 pub policy: Option<String>,
40 pub policy_arns: Option<Vec<PolicyDescriptorType>>,
42}
43
44impl WebIdentityProvider {
45 pub fn new<A, B, C>(web_identity_token: A, role_arn: B, role_session_name: Option<C>) -> Self
47 where
48 A: Into<Variable<Secret, CredentialsError>>,
49 B: Into<Variable<String, CredentialsError>>,
50 C: Into<Variable<Option<String>, CredentialsError>>,
51 {
52 Self {
53 web_identity_token: web_identity_token.into(),
54 role_arn: role_arn.into(),
55 role_session_name: role_session_name.map(|v| v.into()),
56 duration_seconds: None,
57 policy: None,
58 policy_arns: None
59 }
60 }
61
62 pub fn from_k8s_env() -> Self {
71 Self::_from_k8s_env(
72 Variable::from_env_var(AWS_WEB_IDENTITY_TOKEN_FILE),
73 Variable::from_env_var(AWS_ROLE_ARN),
74 Variable::from_env_var_optional(AWS_ROLE_SESSION_NAME),
75 )
76 }
77
78 pub(crate) fn _from_k8s_env(
80 token_file: Variable<String, CredentialsError>,
81 role: Variable<String, CredentialsError>,
82 session_name: Variable<Option<String>, CredentialsError>,
83 ) -> Self {
84 Self::new(
85 Variable::dynamic(move || Variable::from_text_file(token_file.resolve()?).resolve()),
86 role,
87 Some(session_name),
88 )
89 }
90
91 #[cfg(test)]
92 pub(crate) fn load_token(&self) -> Result<Secret, CredentialsError> {
93 self.web_identity_token.resolve()
94 }
95
96 fn create_session_name() -> String {
97 "WebIdentitySession".to_string()
105 }
106}
107
108#[async_trait]
109impl ProvideAwsCredentials for WebIdentityProvider {
110 async fn credentials(&self) -> Result<AwsCredentials, CredentialsError> {
111 let http_client = match HttpClient::new() {
112 Ok(c) => c,
113 Err(e) => return Err(CredentialsError::new(e)),
114 };
115 let client = Client::new_not_signing(http_client);
116 let sts = StsClient::new_with_client(client, Region::default());
117 let mut req = AssumeRoleWithWebIdentityRequest::default();
118
119 req.role_arn = self.role_arn.resolve()?;
120 req.web_identity_token = self.web_identity_token.resolve()?.as_ref().to_string();
121 req.policy = self.policy.to_owned();
122 req.duration_seconds = self.duration_seconds.to_owned();
123 req.policy_arns = self.policy_arns.to_owned();
124 req.role_session_name = match self.role_session_name {
125 Some(ref role_session_name) => match role_session_name.resolve()? {
126 Some(session_name) => session_name,
127 None => Self::create_session_name(),
128 },
129 None => Self::create_session_name(),
130 };
131
132 let assume_role = sts.assume_role_with_web_identity(req).await;
133 match assume_role {
134 Err(e) => Err(CredentialsError::new(e)),
135 Ok(role) => match role.credentials {
136 None => Err(CredentialsError::new(format!(
137 "No credentials found in AssumeRoleWithWebIdentityResponse: {:?}",
138 role
139 ))),
140 Some(c) => AwsCredentials::new_for_credentials(c),
141 },
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use std::io::Write;
150 use tempfile::NamedTempFile;
151
152 #[test]
153 fn api_ergonomy() {
154 WebIdentityProvider::new(Secret::from("".to_string()), "", Some(Some("".to_string())));
155 }
156
157 #[test]
158 fn from_k8s_env() -> Result<(), CredentialsError> {
159 const TOKEN_VALUE: &str = "secret";
160 const ROLE_ARN: &str = "role";
161 const SESSION_NAME: &str = "session";
162 let mut file = NamedTempFile::new()?;
163 writeln!(file, "{}", TOKEN_VALUE)?;
166 let p = WebIdentityProvider::_from_k8s_env(
167 Variable::with_value(file.path().to_string_lossy().to_string()),
168 Variable::with_value(ROLE_ARN.to_string()),
169 Variable::with_value(SESSION_NAME.to_string()),
170 );
171 let token = p.load_token()?;
172 assert_eq!(token.as_ref(), TOKEN_VALUE);
173 Ok(())
174 }
175}