aws_config/sts/
util.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use aws_credential_types::attributes::AccountId;
7use aws_credential_types::provider::{self, error::CredentialsError};
8use aws_credential_types::Credentials as AwsCredentials;
9use aws_sdk_sts::types::{AssumedRoleUser, Credentials as StsCredentials};
10
11use std::time::{SystemTime, UNIX_EPOCH};
12
13/// Convert STS credentials to aws_auth::Credentials
14pub(crate) fn into_credentials(
15    sts_credentials: Option<StsCredentials>,
16    assumed_role_user: Option<AssumedRoleUser>,
17    provider_name: &'static str,
18) -> provider::Result {
19    let sts_credentials = sts_credentials
20        .ok_or_else(|| CredentialsError::unhandled("STS credentials must be defined"))?;
21    let expiration = SystemTime::try_from(sts_credentials.expiration).map_err(|_| {
22        CredentialsError::unhandled(
23            "credential expiration time cannot be represented by a SystemTime",
24        )
25    })?;
26    let mut builder = AwsCredentials::builder()
27        .access_key_id(sts_credentials.access_key_id)
28        .secret_access_key(sts_credentials.secret_access_key)
29        .session_token(sts_credentials.session_token)
30        .expiry(expiration)
31        .provider_name(provider_name);
32    if let Some(AssumedRoleUser { arn, .. }) = assumed_role_user {
33        builder.set_account_id(Some(parse_account_id(&arn)?));
34    }
35    Ok(builder.build())
36}
37
38/// Create a default STS session name
39///
40/// STS Assume Role providers MUST assign a name to their generated session. When a user does not
41/// provide a name for the session, the provider will choose a name composed of a base + a timestamp,
42/// e.g. `profile-file-provider-123456789`
43pub(crate) fn default_session_name(base: &str, ts: SystemTime) -> String {
44    let now = ts.duration_since(UNIX_EPOCH).expect("post epoch");
45    format!("{}-{}", base, now.as_millis())
46}
47
48// A subset of functionality extracted from `endpoint_lib::arn::Arn::parse`.
49// `Arn` is `pub(crate)` within generated SDKs, making it inaccessible from `aws-config`.
50// As a result, a subset is inlined here, with less defensive verification
51// since it only deals with the string-form ARN returned by STS.
52//
53// TODO(https://github.com/smithy-lang/smithy-rs/issues/4090): Consider making a `pub` Arn parser
54fn parse_account_id(arn: &str) -> Result<AccountId, CredentialsError> {
55    let mut split = arn.splitn(6, ':');
56    let invalid_format =
57        || CredentialsError::unhandled("ARN must have 6 components delimited by `:`");
58    let _arn = split.next().ok_or_else(invalid_format)?;
59    let _partition = split.next().ok_or_else(invalid_format)?;
60    let _service = split.next().ok_or_else(invalid_format)?;
61    let _region = split.next().ok_or_else(invalid_format)?;
62    let account_id = split.next().ok_or_else(invalid_format)?;
63
64    Ok(account_id.into())
65}