aws_smithy_runtime_api/client/
auth.rs1use crate::box_error::BoxError;
9use crate::client::identity::{Identity, SharedIdentityResolver};
10use crate::client::orchestrator::HttpRequest;
11use crate::client::runtime_components::sealed::ValidateConfig;
12use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents};
13use crate::impl_shared_conversions;
14use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Storable, StoreReplace};
15use aws_smithy_types::type_erasure::TypeErasedBox;
16use aws_smithy_types::Document;
17use std::borrow::Cow;
18use std::fmt::{self, Debug};
19use std::sync::Arc;
20
21#[cfg(feature = "http-auth")]
23pub mod http;
24
25pub mod static_resolver;
27
28#[derive(Clone, Debug)]
32pub struct AuthSchemeOption {
33 scheme_id: AuthSchemeId,
34 properties: Option<FrozenLayer>,
35}
36
37impl AuthSchemeOption {
38 pub fn builder() -> AuthSchemeOptionBuilder {
40 AuthSchemeOptionBuilder::default()
41 }
42
43 pub fn scheme_id(&self) -> &AuthSchemeId {
45 &self.scheme_id
46 }
47
48 pub fn properties(&self) -> Option<FrozenLayer> {
53 self.properties.clone()
54 }
55}
56
57impl From<AuthSchemeId> for AuthSchemeOption {
58 fn from(auth_scheme_id: AuthSchemeId) -> Self {
59 AuthSchemeOption::builder()
60 .scheme_id(auth_scheme_id)
61 .build()
62 .expect("required fields set")
63 }
64}
65
66#[derive(Debug, Default)]
68pub struct AuthSchemeOptionBuilder {
69 scheme_id: Option<AuthSchemeId>,
70 properties: Option<FrozenLayer>,
71}
72
73impl AuthSchemeOptionBuilder {
74 pub fn scheme_id(mut self, auth_scheme_id: AuthSchemeId) -> Self {
76 self.set_scheme_id(Some(auth_scheme_id));
77 self
78 }
79
80 pub fn set_scheme_id(&mut self, auth_scheme_id: Option<AuthSchemeId>) {
82 self.scheme_id = auth_scheme_id;
83 }
84
85 pub fn properties(mut self, properties: FrozenLayer) -> Self {
87 self.set_properties(Some(properties));
88 self
89 }
90
91 pub fn set_properties(&mut self, properties: Option<FrozenLayer>) {
93 self.properties = properties;
94 }
95
96 pub fn build(self) -> Result<AuthSchemeOption, AuthSchemeOptionBuilderError> {
98 let scheme_id = self
99 .scheme_id
100 .ok_or(ErrorKind::MissingRequiredField("auth_scheme_id"))?;
101 Ok(AuthSchemeOption {
102 scheme_id,
103 properties: self.properties,
104 })
105 }
106}
107
108#[derive(Debug)]
109enum ErrorKind {
110 MissingRequiredField(&'static str),
111}
112
113impl From<ErrorKind> for AuthSchemeOptionBuilderError {
114 fn from(kind: ErrorKind) -> Self {
115 Self { kind }
116 }
117}
118
119#[derive(Debug)]
121pub struct AuthSchemeOptionBuilderError {
122 kind: ErrorKind,
123}
124
125impl fmt::Display for AuthSchemeOptionBuilderError {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self.kind {
128 ErrorKind::MissingRequiredField(name) => {
129 write!(f, "`{name}` is required")
130 }
131 }
132 }
133}
134
135impl std::error::Error for AuthSchemeOptionBuilderError {}
136
137#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
143pub struct AuthSchemeId {
144 scheme_id: Cow<'static, str>,
145}
146
147impl AsRef<AuthSchemeId> for AuthSchemeId {
149 fn as_ref(&self) -> &AuthSchemeId {
150 self
151 }
152}
153
154impl AuthSchemeId {
155 pub const fn new(scheme_id: &'static str) -> Self {
157 Self {
158 scheme_id: Cow::Borrowed(scheme_id),
159 }
160 }
161
162 #[deprecated(
164 note = "This function is no longer functional. Use `inner` instead",
165 since = "1.8.0"
166 )]
167 pub const fn as_str(&self) -> &'static str {
168 match self.scheme_id {
169 Cow::Borrowed(val) => val,
170 Cow::Owned(_) => {
171 ""
173 }
174 }
175 }
176
177 pub fn inner(&self) -> &str {
179 &self.scheme_id
180 }
181}
182
183impl From<&'static str> for AuthSchemeId {
184 fn from(scheme_id: &'static str) -> Self {
185 Self::new(scheme_id)
186 }
187}
188
189impl From<Cow<'static, str>> for AuthSchemeId {
190 fn from(scheme_id: Cow<'static, str>) -> Self {
191 Self { scheme_id }
192 }
193}
194
195#[derive(Debug)]
205pub struct AuthSchemeOptionResolverParams(TypeErasedBox);
206
207impl AuthSchemeOptionResolverParams {
208 pub fn new<T: fmt::Debug + Send + Sync + 'static>(params: T) -> Self {
210 Self(TypeErasedBox::new(params))
211 }
212
213 pub fn get<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
215 self.0.downcast_ref()
216 }
217}
218
219impl Storable for AuthSchemeOptionResolverParams {
220 type Storer = StoreReplace<Self>;
221}
222
223new_type_future! {
224 #[doc = "Future for [`ResolveAuthSchemeOptions::resolve_auth_scheme_options_v2`]."]
225 pub struct AuthSchemeOptionsFuture<'a, Vec<AuthSchemeOption>, BoxError>;
226}
227
228impl<'a> AuthSchemeOptionsFuture<'a> {
231 pub fn map_ok<F>(self, f: F) -> AuthSchemeOptionsFuture<'a>
239 where
240 F: FnOnce(Vec<AuthSchemeOption>) -> Vec<AuthSchemeOption> + Send + 'a,
241 {
242 let inner = self.inner.map_boxed(|result| result.map(f));
243 Self { inner }
244 }
245}
246
247pub trait ResolveAuthSchemeOptions: Send + Sync + fmt::Debug {
261 #[deprecated(
262 note = "This method is deprecated, use `resolve_auth_scheme_options_v2` instead.",
263 since = "1.8.0"
264 )]
265 fn resolve_auth_scheme_options(
267 &self,
268 _params: &AuthSchemeOptionResolverParams,
269 ) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
270 unimplemented!("This method is deprecated, use `resolve_auth_scheme_options_v2` instead.");
271 }
272
273 #[allow(deprecated)]
274 fn resolve_auth_scheme_options_v2<'a>(
276 &'a self,
277 params: &'a AuthSchemeOptionResolverParams,
278 _cfg: &'a ConfigBag,
279 _runtime_components: &'a RuntimeComponents,
280 ) -> AuthSchemeOptionsFuture<'a> {
281 AuthSchemeOptionsFuture::ready({
282 self.resolve_auth_scheme_options(params).map(|options| {
283 options
284 .iter()
285 .cloned()
286 .map(|scheme_id| {
287 AuthSchemeOption::builder()
288 .scheme_id(scheme_id)
289 .build()
290 .expect("required fields set")
291 })
292 .collect::<Vec<_>>()
293 })
294 })
295 }
296}
297
298#[derive(Clone, Debug)]
300pub struct SharedAuthSchemeOptionResolver(Arc<dyn ResolveAuthSchemeOptions>);
301
302impl SharedAuthSchemeOptionResolver {
303 pub fn new(auth_scheme_option_resolver: impl ResolveAuthSchemeOptions + 'static) -> Self {
305 Self(Arc::new(auth_scheme_option_resolver))
306 }
307}
308
309impl ResolveAuthSchemeOptions for SharedAuthSchemeOptionResolver {
310 #[allow(deprecated)]
311 fn resolve_auth_scheme_options(
312 &self,
313 params: &AuthSchemeOptionResolverParams,
314 ) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
315 (*self.0).resolve_auth_scheme_options(params)
316 }
317
318 fn resolve_auth_scheme_options_v2<'a>(
319 &'a self,
320 params: &'a AuthSchemeOptionResolverParams,
321 cfg: &'a ConfigBag,
322 runtime_components: &'a RuntimeComponents,
323 ) -> AuthSchemeOptionsFuture<'a> {
324 (*self.0).resolve_auth_scheme_options_v2(params, cfg, runtime_components)
325 }
326}
327
328impl_shared_conversions!(
329 convert SharedAuthSchemeOptionResolver
330 from ResolveAuthSchemeOptions
331 using SharedAuthSchemeOptionResolver::new
332);
333
334pub trait AuthScheme: Send + Sync + fmt::Debug {
339 fn scheme_id(&self) -> AuthSchemeId;
345
346 fn identity_resolver(
355 &self,
356 identity_resolvers: &dyn GetIdentityResolver,
357 ) -> Option<SharedIdentityResolver>;
358
359 fn signer(&self) -> &dyn Sign;
361}
362
363#[derive(Clone, Debug)]
365pub struct SharedAuthScheme(Arc<dyn AuthScheme>);
366
367impl SharedAuthScheme {
368 pub fn new(auth_scheme: impl AuthScheme + 'static) -> Self {
370 Self(Arc::new(auth_scheme))
371 }
372}
373
374impl AuthScheme for SharedAuthScheme {
375 fn scheme_id(&self) -> AuthSchemeId {
376 self.0.scheme_id()
377 }
378
379 fn identity_resolver(
380 &self,
381 identity_resolvers: &dyn GetIdentityResolver,
382 ) -> Option<SharedIdentityResolver> {
383 self.0.identity_resolver(identity_resolvers)
384 }
385
386 fn signer(&self) -> &dyn Sign {
387 self.0.signer()
388 }
389}
390
391impl ValidateConfig for SharedAuthScheme {}
392
393impl_shared_conversions!(convert SharedAuthScheme from AuthScheme using SharedAuthScheme::new);
394
395pub trait Sign: Send + Sync + fmt::Debug {
397 fn sign_http_request(
401 &self,
402 request: &mut HttpRequest,
403 identity: &Identity,
404 auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>,
405 runtime_components: &RuntimeComponents,
406 config_bag: &ConfigBag,
407 ) -> Result<(), BoxError>;
408}
409
410#[non_exhaustive]
416#[derive(Clone, Debug)]
417pub struct AuthSchemeEndpointConfig<'a>(Option<&'a Document>);
418
419impl<'a> AuthSchemeEndpointConfig<'a> {
420 pub fn empty() -> Self {
422 Self(None)
423 }
424
425 pub fn as_document(&self) -> Option<&'a Document> {
427 self.0
428 }
429}
430
431impl<'a> From<Option<&'a Document>> for AuthSchemeEndpointConfig<'a> {
432 fn from(value: Option<&'a Document>) -> Self {
433 Self(value)
434 }
435}
436
437impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> {
438 fn from(value: &'a Document) -> Self {
439 Self(Some(value))
440 }
441}
442
443#[derive(Clone, Debug, Default, Eq, PartialEq)]
449pub struct AuthSchemePreference {
450 preference_list: Vec<AuthSchemeId>,
451}
452
453impl Storable for AuthSchemePreference {
454 type Storer = StoreReplace<Self>;
455}
456
457impl IntoIterator for AuthSchemePreference {
458 type Item = AuthSchemeId;
459 type IntoIter = std::vec::IntoIter<Self::Item>;
460
461 fn into_iter(self) -> Self::IntoIter {
462 self.preference_list.into_iter()
463 }
464}
465
466impl<T> From<T> for AuthSchemePreference
467where
468 T: AsRef<[AuthSchemeId]>,
469{
470 fn from(slice: T) -> Self {
471 AuthSchemePreference {
472 preference_list: slice.as_ref().to_vec(),
473 }
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480
481 #[tokio::test]
482 async fn test_map_ok_now_variant() {
483 let input = ["sigv4", "http"]
484 .map(|s| AuthSchemeOption::from(AuthSchemeId::from(s)))
485 .to_vec();
486 let fut = AuthSchemeOptionsFuture::ready(Ok(input));
487
488 let mapped = fut.map_ok(|opts| {
489 opts.into_iter()
490 .filter(|opt| opt.scheme_id().inner() != "http")
491 .collect()
492 });
493
494 let result = mapped.await;
495 assert!(result.is_ok());
496 let vec = result.unwrap();
497 assert_eq!(1, vec.len());
498 assert_eq!("sigv4", vec[0].scheme_id().inner());
499 }
500
501 #[tokio::test]
502 async fn test_map_ok_now_variant_error_no_op() {
503 let fut = AuthSchemeOptionsFuture::ready(Err(BoxError::from("oops")));
504
505 let mapped = fut.map_ok(|opts| opts); let result = mapped.await;
508 assert!(result.is_err());
509 assert_eq!(result.unwrap_err().to_string(), "oops");
510 }
511
512 #[tokio::test]
513 async fn test_map_ok_later_variant() {
514 let input = ["foo", "bar"]
515 .map(|s| AuthSchemeOption::from(AuthSchemeId::from(s)))
516 .to_vec();
517 let fut = AuthSchemeOptionsFuture::new(async move { Ok(input) });
518
519 let mapped = fut.map_ok(|opts| {
520 opts.into_iter()
521 .map(|mut opt| {
522 opt.scheme_id =
523 AuthSchemeId::from(Cow::Owned(opt.scheme_id().inner().to_uppercase()));
524 opt
525 })
526 .collect()
527 });
528
529 let result = mapped.await;
530 assert!(result.is_ok());
531 let vec = result.unwrap();
532 assert_eq!(vec[0].scheme_id().inner(), "FOO");
533 assert_eq!(vec[1].scheme_id().inner(), "BAR");
534 }
535
536 #[tokio::test]
537 async fn test_map_ok_later_variant_error_no_op() {
538 let fut = AuthSchemeOptionsFuture::new(async move { Err(BoxError::from("later fail")) });
539
540 let mapped = fut.map_ok(|opts| opts); let result = mapped.await;
543 assert!(result.is_err());
544 assert_eq!(result.unwrap_err().to_string(), "later fail");
545 }
546}