Skip to main content

redshift_iam/
iam_provider.rs

1use aws_credential_types::Credentials;
2use aws_sdk_redshift as redshift;
3use aws_sdk_sts as sts;
4use tokio::runtime::Runtime;
5
6/// Exchanges temporary AWS credentials for short-lived Redshift cluster credentials
7/// via `redshift:GetClusterCredentials`.
8#[derive(Debug)]
9pub struct IamProvider {
10    user: String,
11    database: String,
12    cluster: String,
13    autocreate: bool,
14    region: String,
15}
16
17impl IamProvider {
18    /// Creates a new `IamProvider`.
19    ///
20    /// - `autocreate`: When `true`, the Redshift user is created automatically if it
21    ///   does not already exist.
22    /// - The AWS region defaults to `"us-east-1"`; change it with [`set_region`](Self::set_region).
23    ///
24    /// # Examples
25    /// ```
26    /// use redshift_iam::IamProvider;
27    /// let iam = IamProvider::new("alice", "analytics", "my-cluster", false);
28    /// assert_eq!(iam.region(), "us-east-1");
29    /// ```
30    pub fn new(
31        user: impl ToString,
32        database: impl ToString,
33        cluster: impl ToString,
34        autocreate: bool,
35    ) -> Self {
36        Self {
37            user: user.to_string(),
38            database: database.to_string(),
39            cluster: cluster.to_string(),
40            autocreate,
41            region: "us-east-1".to_string(),
42        }
43    }
44
45    fn user(&self) -> String {
46        self.user.clone()
47    }
48
49    /// Overrides the AWS region used when calling `GetClusterCredentials`.
50    ///
51    /// # Examples
52    /// ```
53    /// use redshift_iam::IamProvider;
54    /// let iam = IamProvider::new("alice", "analytics", "my-cluster", false)
55    ///     .set_region("eu-west-1");
56    /// assert_eq!(iam.region(), "eu-west-1");
57    /// ```
58    pub fn set_region(mut self, region: impl ToString) -> Self {
59        self.region = region.to_string();
60        self
61    }
62
63    /// Returns the configured AWS region.
64    pub fn region(&self) -> String {
65        self.region.clone()
66    }
67
68    /// Calls `redshift:GetClusterCredentials` with the provided AWS credentials and
69    /// returns a `(username, password)` pair valid for 3600 seconds.
70    pub fn auth(&self, aws_credentials: sts::types::Credentials) -> (String, String) {
71        let rt = Runtime::new().unwrap();
72        rt.block_on(async {
73            let creds = Credentials::from_keys(
74                aws_credentials.access_key_id().to_string(),
75                aws_credentials.secret_access_key().to_string(),
76                Some(aws_credentials.session_token().to_string()),
77            );
78            let config = redshift::Config::builder()
79                .credentials_provider(creds)
80                .region(Some(redshift::config::Region::new(self.region())))
81                .build();
82            let client = redshift::Client::from_conf(config);
83            let cluster_creds = client
84                .get_cluster_credentials()
85                .set_db_user(Some(self.user()))
86                .set_db_name(Some(self.database.clone()))
87                .set_cluster_identifier(Some(self.cluster.clone()))
88                .set_duration_seconds(Some(3600)) // can be 900-3600
89                .set_auto_create(Some(self.autocreate))
90                .send()
91                .await
92                .unwrap(); //?
93
94            (
95                cluster_creds.db_user.unwrap(),
96                cluster_creds.db_password.unwrap(),
97            )
98        })
99    }
100}