aws_smithy_runtime_api/client/
identity.rs1use crate::box_error::BoxError;
7use crate::client::runtime_components::sealed::ValidateConfig;
8use crate::client::runtime_components::{RuntimeComponents, RuntimeComponentsBuilder};
9use crate::impl_shared_conversions;
10use aws_smithy_types::config_bag::ConfigBag;
11use std::any::Any;
12use std::fmt;
13use std::fmt::Debug;
14use std::sync::atomic::{AtomicUsize, Ordering};
15use std::sync::Arc;
16use std::time::SystemTime;
17
18#[cfg(feature = "http-auth")]
19pub mod http;
20
21new_type_future! {
22 #[doc = "Future for [`IdentityResolver::resolve_identity`]."]
23 pub struct IdentityFuture<'a, Identity, BoxError>;
24}
25
26static NEXT_CACHE_PARTITION: AtomicUsize = AtomicUsize::new(0);
27
28#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
41pub struct IdentityCachePartition(usize);
42
43impl IdentityCachePartition {
44 pub fn new() -> Self {
46 Self(NEXT_CACHE_PARTITION.fetch_add(1, Ordering::Relaxed))
47 }
48
49 #[cfg(feature = "test-util")]
51 pub fn new_for_tests(value: usize) -> IdentityCachePartition {
52 Self(value)
53 }
54}
55
56pub trait ResolveCachedIdentity: fmt::Debug + Send + Sync {
58 fn resolve_cached_identity<'a>(
60 &'a self,
61 resolver: SharedIdentityResolver,
62 runtime_components: &'a RuntimeComponents,
63 config_bag: &'a ConfigBag,
64 ) -> IdentityFuture<'a>;
65
66 #[doc = include_str!("../../rustdoc/validate_base_client_config.md")]
67 fn validate_base_client_config(
68 &self,
69 runtime_components: &RuntimeComponentsBuilder,
70 cfg: &ConfigBag,
71 ) -> Result<(), BoxError> {
72 let _ = (runtime_components, cfg);
73 Ok(())
74 }
75
76 #[doc = include_str!("../../rustdoc/validate_final_config.md")]
77 fn validate_final_config(
78 &self,
79 runtime_components: &RuntimeComponents,
80 cfg: &ConfigBag,
81 ) -> Result<(), BoxError> {
82 let _ = (runtime_components, cfg);
83 Ok(())
84 }
85}
86
87#[derive(Clone, Debug)]
89pub struct SharedIdentityCache(Arc<dyn ResolveCachedIdentity>);
90
91impl SharedIdentityCache {
92 pub fn new(cache: impl ResolveCachedIdentity + 'static) -> Self {
94 Self(Arc::new(cache))
95 }
96}
97
98impl ResolveCachedIdentity for SharedIdentityCache {
99 fn resolve_cached_identity<'a>(
100 &'a self,
101 resolver: SharedIdentityResolver,
102 runtime_components: &'a RuntimeComponents,
103 config_bag: &'a ConfigBag,
104 ) -> IdentityFuture<'a> {
105 self.0
106 .resolve_cached_identity(resolver, runtime_components, config_bag)
107 }
108}
109
110impl ValidateConfig for SharedIdentityResolver {}
111
112impl ValidateConfig for SharedIdentityCache {
113 fn validate_base_client_config(
114 &self,
115 runtime_components: &RuntimeComponentsBuilder,
116 cfg: &ConfigBag,
117 ) -> Result<(), BoxError> {
118 self.0.validate_base_client_config(runtime_components, cfg)
119 }
120
121 fn validate_final_config(
122 &self,
123 runtime_components: &RuntimeComponents,
124 cfg: &ConfigBag,
125 ) -> Result<(), BoxError> {
126 self.0.validate_final_config(runtime_components, cfg)
127 }
128}
129
130impl_shared_conversions!(convert SharedIdentityCache from ResolveCachedIdentity using SharedIdentityCache::new);
131
132pub trait ResolveIdentity: Send + Sync + Debug {
144 fn resolve_identity<'a>(
146 &'a self,
147 runtime_components: &'a RuntimeComponents,
148 config_bag: &'a ConfigBag,
149 ) -> IdentityFuture<'a>;
150
151 fn fallback_on_interrupt(&self) -> Option<Identity> {
160 None
161 }
162
163 fn cache_location(&self) -> IdentityCacheLocation {
169 IdentityCacheLocation::RuntimeComponents
170 }
171
172 fn cache_partition(&self) -> Option<IdentityCachePartition> {
176 None
177 }
178}
179
180#[non_exhaustive]
187#[derive(Copy, Clone, Debug, Eq, PartialEq)]
188pub enum IdentityCacheLocation {
189 RuntimeComponents,
191 IdentityResolver,
193}
194
195#[derive(Clone, Debug)]
197pub struct SharedIdentityResolver {
198 inner: Arc<dyn ResolveIdentity>,
199 cache_partition: IdentityCachePartition,
200}
201
202impl SharedIdentityResolver {
203 pub fn new(resolver: impl ResolveIdentity + 'static) -> Self {
205 let partition = match resolver.cache_partition() {
208 Some(p) => p,
209 None => IdentityCachePartition::new(),
210 };
211
212 Self {
213 inner: Arc::new(resolver),
214 cache_partition: partition,
215 }
216 }
217
218 pub fn cache_partition(&self) -> IdentityCachePartition {
223 self.cache_partition
224 }
225}
226
227impl ResolveIdentity for SharedIdentityResolver {
228 fn resolve_identity<'a>(
229 &'a self,
230 runtime_components: &'a RuntimeComponents,
231 config_bag: &'a ConfigBag,
232 ) -> IdentityFuture<'a> {
233 self.inner.resolve_identity(runtime_components, config_bag)
234 }
235
236 fn cache_location(&self) -> IdentityCacheLocation {
237 self.inner.cache_location()
238 }
239
240 fn cache_partition(&self) -> Option<IdentityCachePartition> {
241 Some(self.cache_partition())
242 }
243}
244
245impl_shared_conversions!(convert SharedIdentityResolver from ResolveIdentity using SharedIdentityResolver::new);
246
247#[derive(Clone)]
259pub struct Identity {
260 data: Arc<dyn Any + Send + Sync>,
261 #[allow(clippy::type_complexity)]
262 data_debug: Arc<dyn (Fn(&Arc<dyn Any + Send + Sync>) -> &dyn Debug) + Send + Sync>,
263 expiration: Option<SystemTime>,
264}
265
266impl Identity {
267 pub fn new<T>(data: T, expiration: Option<SystemTime>) -> Self
269 where
270 T: Any + Debug + Send + Sync,
271 {
272 Self {
273 data: Arc::new(data),
274 data_debug: Arc::new(|d| d.downcast_ref::<T>().expect("type-checked") as _),
275 expiration,
276 }
277 }
278
279 pub fn data<T: Any + Debug + Send + Sync + 'static>(&self) -> Option<&T> {
281 self.data.downcast_ref()
282 }
283
284 pub fn expiration(&self) -> Option<SystemTime> {
286 self.expiration
287 }
288}
289
290impl Debug for Identity {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 f.debug_struct("Identity")
293 .field("data", (self.data_debug)(&self.data))
294 .field("expiration", &self.expiration)
295 .finish()
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use aws_smithy_async::time::{SystemTimeSource, TimeSource};
303
304 #[test]
305 fn check_send_sync() {
306 fn is_send_sync<T: Send + Sync>(_: T) {}
307 is_send_sync(Identity::new("foo", None));
308 }
309
310 #[test]
311 fn create_retrieve_identity() {
312 #[derive(Debug)]
313 struct MyIdentityData {
314 first: String,
315 last: String,
316 }
317
318 let ts = SystemTimeSource::new();
319 let expiration = ts.now();
320 let identity = Identity::new(
321 MyIdentityData {
322 first: "foo".into(),
323 last: "bar".into(),
324 },
325 Some(expiration),
326 );
327
328 assert_eq!("foo", identity.data::<MyIdentityData>().unwrap().first);
329 assert_eq!("bar", identity.data::<MyIdentityData>().unwrap().last);
330 assert_eq!(Some(expiration), identity.expiration());
331 }
332}