mountpoint_s3_crt/auth/
credentials.rs

1//! AWS credentials providers
2
3use std::fmt::Debug;
4use std::ptr::NonNull;
5
6use mountpoint_s3_crt_sys::{
7    aws_credentials_provider, aws_credentials_provider_acquire, aws_credentials_provider_cached_options,
8    aws_credentials_provider_chain_default_options, aws_credentials_provider_new_anonymous,
9    aws_credentials_provider_new_cached, aws_credentials_provider_new_chain_default,
10    aws_credentials_provider_new_profile, aws_credentials_provider_new_static,
11    aws_credentials_provider_profile_options, aws_credentials_provider_release,
12    aws_credentials_provider_static_options,
13};
14
15use crate::auth::auth_library_init;
16use crate::common::allocator::Allocator;
17use crate::common::error::Error;
18use crate::io::channel_bootstrap::ClientBootstrap;
19use crate::{CrtError as _, ToAwsByteCursor as _};
20
21/// Options for creating a default credentials provider
22#[derive(Debug)]
23pub struct CredentialsProviderChainDefaultOptions<'a> {
24    /// The client bootstrap this credentials provider should use to setup channels
25    pub bootstrap: &'a mut ClientBootstrap,
26}
27
28/// Options for creating a profile credentials provider
29#[derive(Debug)]
30pub struct CredentialsProviderProfileOptions<'a> {
31    /// The client bootstrap this credentials provider should use to setup channels
32    pub bootstrap: &'a mut ClientBootstrap,
33    /// The name of profile to use.
34    pub profile_name_override: &'a str,
35}
36
37/// Options for creating a static credentials provider
38pub struct CredentialsProviderStaticOptions<'a> {
39    /// AWS access key ID
40    pub access_key_id: &'a str,
41    /// AWS secret access key
42    pub secret_access_key: &'a str,
43    /// AWS session token (only required for some credentials sources, e.g. STS)
44    pub session_token: Option<&'a str>,
45}
46
47impl Debug for CredentialsProviderStaticOptions<'_> {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.debug_struct("CredentialsProviderStaticOptions")
50            .field("access_key_id", &"** redacted **")
51            .field("secret_access_key", &"** redacted **")
52            .field("session_token", &self.session_token.map(|_| "** redacted **"))
53            .finish()
54    }
55}
56
57/// A credentials provider is an object that has an asynchronous query function for retrieving AWS
58/// credentials
59#[derive(Debug)]
60pub struct CredentialsProvider {
61    pub(crate) inner: NonNull<aws_credentials_provider>,
62}
63
64// SAFETY: aws_credentials_provider is thread-safe.
65unsafe impl Send for CredentialsProvider {}
66// SAFETY: aws_credentials_provider is thread-safe.
67unsafe impl Sync for CredentialsProvider {}
68
69impl CredentialsProvider {
70    /// Creates the default credential provider chain as used by most AWS SDKs
71    pub fn new_chain_default(
72        allocator: &Allocator,
73        options: CredentialsProviderChainDefaultOptions,
74    ) -> Result<Self, Error> {
75        auth_library_init(allocator);
76
77        let inner_options = aws_credentials_provider_chain_default_options {
78            bootstrap: options.bootstrap.inner.as_ptr(),
79            ..Default::default()
80        };
81
82        // SAFETY: aws_credentials_provider_new_chain_default makes a copy of the bootstrap options.
83        let inner = unsafe {
84            aws_credentials_provider_new_chain_default(allocator.inner.as_ptr(), &inner_options).ok_or_last_error()?
85        };
86
87        Ok(Self { inner })
88    }
89
90    /// Creates the anonymous credential provider.
91    /// Anonynous credentials provider gives you anonymous credentials which can be used to skip the signing process.
92    pub fn new_anonymous(allocator: &Allocator) -> Result<Self, Error> {
93        auth_library_init(allocator);
94
95        // SAFETY: allocator is a valid aws_allocator and shutdown_options is optional
96        let inner = unsafe {
97            aws_credentials_provider_new_anonymous(allocator.inner.as_ptr(), std::ptr::null_mut()).ok_or_last_error()?
98        };
99
100        Ok(Self { inner })
101    }
102
103    /// Creates the profile credential provider.
104    pub fn new_profile(allocator: &Allocator, options: CredentialsProviderProfileOptions) -> Result<Self, Error> {
105        auth_library_init(allocator);
106
107        // SAFETY: aws_credentials_provider_new_profile makes a copy of bootstrap
108        // and contents of profile_name_override.
109        // SAFETY: aws_credentials_provider_new_cached increments the reference counter of
110        // profile_provider.
111        let inner = unsafe {
112            let inner_options = aws_credentials_provider_profile_options {
113                bootstrap: options.bootstrap.inner.as_ptr(),
114                profile_name_override: options.profile_name_override.as_aws_byte_cursor(),
115                ..Default::default()
116            };
117
118            let profile_provider =
119                aws_credentials_provider_new_profile(allocator.inner.as_ptr(), &inner_options).ok_or_last_error()?;
120
121            let inner_options = aws_credentials_provider_cached_options {
122                source: profile_provider.as_ptr(),
123                refresh_time_in_milliseconds: 900_000, // Same as `aws_credentials_provider_new_chain_default`, 15 minutes
124                ..Default::default()
125            };
126
127            let cached_provider =
128                aws_credentials_provider_new_cached(allocator.inner.as_ptr(), &inner_options).ok_or_last_error()?;
129
130            // transfer ownership
131            aws_credentials_provider_release(profile_provider.as_ptr());
132
133            cached_provider
134        };
135
136        Ok(Self { inner })
137    }
138
139    /// Creates a static credential provider that always returns the given credentials
140    pub fn new_static(allocator: &Allocator, options: CredentialsProviderStaticOptions) -> Result<Self, Error> {
141        auth_library_init(allocator);
142
143        // SAFETY: aws_credentials_provider_new_static makes a copy of the strings
144        let inner = unsafe {
145            let inner_options = aws_credentials_provider_static_options {
146                access_key_id: options.access_key_id.as_aws_byte_cursor(),
147                secret_access_key: options.secret_access_key.as_aws_byte_cursor(),
148                session_token: options
149                    .session_token
150                    .map(|t| t.as_aws_byte_cursor())
151                    .unwrap_or_default(),
152                ..Default::default()
153            };
154
155            aws_credentials_provider_new_static(allocator.inner.as_ptr(), &inner_options).ok_or_last_error()?
156        };
157
158        Ok(Self { inner })
159    }
160}
161
162impl Clone for CredentialsProvider {
163    fn clone(&self) -> Self {
164        // SAFETY: `self.inner` is a valid `aws_credentials_provider` for as long as `self` exists
165        unsafe {
166            aws_credentials_provider_acquire(self.inner.as_ptr());
167        }
168
169        Self { inner: self.inner }
170    }
171}
172
173impl Drop for CredentialsProvider {
174    fn drop(&mut self) {
175        // SAFETY: `self.inner` is a valid `aws_credentials_provider` and we're in drop so it's safe
176        // to decrement the reference count.
177        unsafe {
178            aws_credentials_provider_release(self.inner.as_ptr());
179        }
180    }
181}