Unverified Commit ed6f6215 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Add support for sts (#453)

Adds support for sts and a basic example of how an STS based credentials provider could be wired up
parent b4f57b7c
Loading
Loading
Loading
Loading
+1255 −0

File added.

Preview size limit exceeded, changes collapsed.

+14 −0
Original line number Diff line number Diff line
[package]
name = "sts"
version = "0.1.0"
authors = ["Russell Cohen <rcoh@amazon.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
sts = { package = "aws-sdk-sts", path = "../../build/aws-sdk/sts" }
dynamodb = { package = "aws-sdk-dynamodb", path = "../../build/aws-sdk/dynamodb"}
aws-auth = { package = "aws-auth", path = "../../build/aws-sdk/aws-auth" }
tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.2.18"
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_auth::{CredentialsError, ProvideCredentials};
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime};
use sts::Credentials;

#[tokio::main]
async fn main() -> Result<(), dynamodb::Error> {
    tracing_subscriber::fmt::init();
    let client = sts::Client::from_env();
    let sts_provider = StsCredentialsProvider {
        client,
        credentials: Arc::new(Mutex::new(None)),
    };
    sts_provider.spawn_refresh_loop().await;

    let dynamodb_conf = dynamodb::Config::builder()
        .credentials_provider(sts_provider)
        .build();
    let client = dynamodb::Client::from_conf(dynamodb_conf);
    println!("tables: {:?}", client.list_tables().send().await?);
    Ok(())
}

/// This is a rough example of how you could implement ProvideCredentials with Sts
///
/// Do not use this in production! A high quality implementation is in the roadmap.
#[derive(Clone)]
struct StsCredentialsProvider {
    client: sts::Client,
    credentials: Arc<Mutex<Option<Credentials>>>,
}

impl ProvideCredentials for StsCredentialsProvider {
    fn provide_credentials(&self) -> Result<Credentials, CredentialsError> {
        let inner = self.credentials.lock().unwrap().clone();
        inner.ok_or(CredentialsError::CredentialsNotLoaded)
    }
}

impl StsCredentialsProvider {
    pub async fn spawn_refresh_loop(&self) {
        let _ = self
            .refresh()
            .await
            .map_err(|e| eprintln!("failed to load credentials! {}", e));
        let this = self.clone();
        tokio::spawn(async move {
            loop {
                let needs_refresh = {
                    let creds = this.credentials.lock().unwrap();
                    let expiry = creds.as_ref().and_then(|creds| creds.expiry());
                    if creds.is_none() {
                        true
                    } else {
                        expiry
                            .map(|expiry| SystemTime::now() > expiry)
                            .unwrap_or(false)
                    }
                };
                if needs_refresh {
                    let _ = this
                        .refresh()
                        .await
                        .map_err(|e| eprintln!("failed to load credentials! {}", e));
                }
                tokio::time::sleep(Duration::from_secs(5)).await;
            }
        });
    }
    pub async fn refresh(&self) -> Result<(), sts::Error> {
        let session_token = self.client.get_session_token().send().await?;
        let sts_credentials = session_token
            .credentials
            .expect("should include credentials");
        *self.credentials.lock().unwrap() = Some(Credentials::new(
            sts_credentials.access_key_id.unwrap(),
            sts_credentials.secret_access_key.unwrap(),
            sts_credentials.session_token,
            sts_credentials
                .expiration
                .map(|expiry| expiry.to_system_time().expect("sts sent a time < 0")),
            "Sts",
        ));
        Ok(())
    }
}
+17 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
use crate::instant::format::DateParseError;
use chrono::{DateTime, NaiveDateTime, SecondsFormat, Utc};
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

mod format;

@@ -99,6 +99,22 @@ impl Instant {
        )
    }

    /// Convert this `Instant` to a [`SystemTime`](std::time::SystemTime)
    ///
    /// Since SystemTime cannot represent times prior to the unix epoch, if this time is before
    /// 1/1/1970, this function will return `None`.
    pub fn to_system_time(&self) -> Option<SystemTime> {
        if self.seconds < 0 {
            None
        } else {
            Some(
                UNIX_EPOCH
                    + Duration::from_secs(self.seconds as u64)
                    + Duration::from_nanos(self.subsecond_nanos as u64),
            )
        }
    }

    pub fn has_nanos(&self) -> bool {
        self.subsecond_nanos != 0
    }