Skip to content

Commit ee5ae95

Browse files
authored
Update CredentialsProcess to support no-expiry credentials (#3335)
## Motivation and Context - aws-sdk-rust#1021 ## Description Fix bug in CredentialsProcess provider where expiry was erroneously required ## Testing - unit test ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS SDK, generated SDK code, or SDK runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
1 parent a27be2b commit ee5ae95

File tree

2 files changed

+46
-34
lines changed

2 files changed

+46
-34
lines changed

CHANGELOG.next.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,9 @@ message = "Serialize 0/false in query parameters, and ignore actual default valu
3434
references = ["smithy-rs#3252", "smithy-rs#3312"]
3535
meta = { "breaking" = false, "tada" = false, "bug" = true }
3636
author = "milesziemer"
37+
38+
[[aws-sdk-rust]]
39+
message = "Fix bug in `CredentialsProcess` provider where `expiry` was incorrectly treated as a required field."
40+
references = ["smithy-rs#3335", "aws-sdk-rust#1021"]
41+
meta = { "breaking" = false, "tada" = false, "bug" = true }
42+
author = "rcoh"

aws/rust-runtime/aws-config/src/credential_process.rs

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
//! Credentials Provider for external process
99
10-
use crate::json_credentials::{json_parse_loop, InvalidJsonCredentials, RefreshableCredentials};
10+
use crate::json_credentials::{json_parse_loop, InvalidJsonCredentials};
1111
use crate::sensitive_command::CommandWithSensitiveArgs;
1212
use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials};
1313
use aws_credential_types::Credentials;
@@ -120,25 +120,12 @@ impl CredentialProcessProvider {
120120
))
121121
})?;
122122

123-
match parse_credential_process_json_credentials(output) {
124-
Ok(RefreshableCredentials {
125-
access_key_id,
126-
secret_access_key,
127-
session_token,
128-
expiration,
129-
..
130-
}) => Ok(Credentials::new(
131-
access_key_id,
132-
secret_access_key,
133-
Some(session_token.to_string()),
134-
expiration.into(),
135-
"CredentialProcess",
136-
)),
137-
Err(invalid) => Err(CredentialsError::provider_error(format!(
123+
parse_credential_process_json_credentials(output).map_err(|invalid| {
124+
CredentialsError::provider_error(format!(
138125
"Error retrieving credentials from external process, could not parse response: {}",
139126
invalid
140-
))),
141-
}
127+
))
128+
})
142129
}
143130
}
144131

@@ -149,7 +136,7 @@ impl CredentialProcessProvider {
149136
/// Keys are case insensitive.
150137
pub(crate) fn parse_credential_process_json_credentials(
151138
credentials_response: &str,
152-
) -> Result<RefreshableCredentials<'_>, InvalidJsonCredentials> {
139+
) -> Result<Credentials, InvalidJsonCredentials> {
153140
let mut version = None;
154141
let mut access_key_id = None;
155142
let mut secret_access_key = None;
@@ -206,25 +193,32 @@ pub(crate) fn parse_credential_process_json_credentials(
206193
let access_key_id = access_key_id.ok_or(InvalidJsonCredentials::MissingField("AccessKeyId"))?;
207194
let secret_access_key =
208195
secret_access_key.ok_or(InvalidJsonCredentials::MissingField("SecretAccessKey"))?;
209-
let session_token = session_token.ok_or(InvalidJsonCredentials::MissingField("Token"))?;
210-
let expiration = expiration.ok_or(InvalidJsonCredentials::MissingField("Expiration"))?;
211-
let expiration =
212-
SystemTime::try_from(OffsetDateTime::parse(&expiration, &Rfc3339).map_err(|err| {
196+
let expiration = expiration.map(parse_expiration).transpose()?;
197+
if expiration.is_none() {
198+
tracing::debug!("no expiration provided for credentials provider credentials. these credentials will never be refreshed.")
199+
}
200+
Ok(Credentials::new(
201+
access_key_id,
202+
secret_access_key,
203+
session_token.map(|tok| tok.to_string()),
204+
expiration,
205+
"CredentialProcess",
206+
))
207+
}
208+
209+
fn parse_expiration(expiration: impl AsRef<str>) -> Result<SystemTime, InvalidJsonCredentials> {
210+
SystemTime::try_from(
211+
OffsetDateTime::parse(expiration.as_ref(), &Rfc3339).map_err(|err| {
213212
InvalidJsonCredentials::InvalidField {
214213
field: "Expiration",
215214
err: err.into(),
216215
}
217-
})?)
218-
.map_err(|_| {
219-
InvalidJsonCredentials::Other(
220-
"credential expiration time cannot be represented by a DateTime".into(),
221-
)
222-
})?;
223-
Ok(RefreshableCredentials {
224-
access_key_id,
225-
secret_access_key,
226-
session_token,
227-
expiration,
216+
})?,
217+
)
218+
.map_err(|_| {
219+
InvalidJsonCredentials::Other(
220+
"credential expiration time cannot be represented by a DateTime".into(),
221+
)
228222
})
229223
}
230224

@@ -258,6 +252,18 @@ mod test {
258252
);
259253
}
260254

255+
#[tokio::test]
256+
async fn test_credential_process_no_expiry() {
257+
let provider = CredentialProcessProvider::new(String::from(
258+
r#"echo '{ "Version": 1, "AccessKeyId": "ASIARTESTID", "SecretAccessKey": "TESTSECRETKEY" }'"#,
259+
));
260+
let creds = provider.provide_credentials().await.expect("valid creds");
261+
assert_eq!(creds.access_key_id(), "ASIARTESTID");
262+
assert_eq!(creds.secret_access_key(), "TESTSECRETKEY");
263+
assert_eq!(creds.session_token(), None);
264+
assert_eq!(creds.expiry(), None);
265+
}
266+
261267
#[tokio::test]
262268
async fn credentials_process_timeouts() {
263269
let provider = CredentialProcessProvider::new(String::from("sleep 1000"));

0 commit comments

Comments
 (0)