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;
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
154const fn normalize_auth_scheme_id(s: &'static str) -> &'static str {
159 match s.as_bytes() {
160 b"no_auth" => "noAuth",
161 _ => s,
162 }
163}
164
165impl AuthSchemeId {
166 pub const fn new(scheme_id: &'static str) -> Self {
168 Self {
169 scheme_id: Cow::Borrowed(normalize_auth_scheme_id(scheme_id)),
170 }
171 }
172
173 #[deprecated(
175 note = "This function is no longer functional. Use `inner` instead",
176 since = "1.8.0"
177 )]
178 pub const fn as_str(&self) -> &'static str {
179 match self.scheme_id {
180 Cow::Borrowed(val) => val,
181 Cow::Owned(_) => {
182 ""
184 }
185 }
186 }
187
188 pub fn inner(&self) -> &str {
190 &self.scheme_id
191 }
192}
193
194impl From<&'static str> for AuthSchemeId {
195 fn from(scheme_id: &'static str) -> Self {
196 Self::new(scheme_id)
197 }
198}
199
200impl From<Cow<'static, str>> for AuthSchemeId {
201 fn from(scheme_id: Cow<'static, str>) -> Self {
202 let normalized_scheme_id = match &scheme_id {
203 Cow::Borrowed(s) => Cow::Borrowed(normalize_auth_scheme_id(s)),
204 Cow::Owned(s) => {
205 if s == "no_auth" {
206 Cow::Borrowed("noAuth")
207 } else {
208 scheme_id
209 }
210 }
211 };
212 Self {
213 scheme_id: normalized_scheme_id,
214 }
215 }
216}
217
218#[derive(Debug)]
228pub struct AuthSchemeOptionResolverParams(TypeErasedBox);
229
230impl AuthSchemeOptionResolverParams {
231 pub fn new<T: fmt::Debug + Send + Sync + 'static>(params: T) -> Self {
233 Self(TypeErasedBox::new(params))
234 }
235
236 pub fn get<T: fmt::Debug + Send + Sync + 'static>(&self) -> Option<&T> {
238 self.0.downcast_ref()
239 }
240}
241
242impl Storable for AuthSchemeOptionResolverParams {
243 type Storer = StoreReplace<Self>;
244}
245
246new_type_future! {
247 #[doc = "Future for [`ResolveAuthSchemeOptions::resolve_auth_scheme_options_v2`]."]
248 pub struct AuthSchemeOptionsFuture<'a, Vec<AuthSchemeOption>, BoxError>;
249}
250
251pub trait ResolveAuthSchemeOptions: Send + Sync + fmt::Debug {
265 #[deprecated(
266 note = "This method is deprecated, use `resolve_auth_scheme_options_v2` instead.",
267 since = "1.8.0"
268 )]
269 fn resolve_auth_scheme_options(
271 &self,
272 _params: &AuthSchemeOptionResolverParams,
273 ) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
274 unimplemented!("This method is deprecated, use `resolve_auth_scheme_options_v2` instead.");
275 }
276
277 #[allow(deprecated)]
278 fn resolve_auth_scheme_options_v2<'a>(
280 &'a self,
281 params: &'a AuthSchemeOptionResolverParams,
282 _cfg: &'a ConfigBag,
283 _runtime_components: &'a RuntimeComponents,
284 ) -> AuthSchemeOptionsFuture<'a> {
285 AuthSchemeOptionsFuture::ready({
286 self.resolve_auth_scheme_options(params).map(|options| {
287 options
288 .iter()
289 .cloned()
290 .map(|scheme_id| {
291 AuthSchemeOption::builder()
292 .scheme_id(scheme_id)
293 .build()
294 .expect("required fields set")
295 })
296 .collect::<Vec<_>>()
297 })
298 })
299 }
300}
301
302#[derive(Clone, Debug)]
304pub struct SharedAuthSchemeOptionResolver(Arc<dyn ResolveAuthSchemeOptions>);
305
306impl SharedAuthSchemeOptionResolver {
307 pub fn new(auth_scheme_option_resolver: impl ResolveAuthSchemeOptions + 'static) -> Self {
309 Self(Arc::new(auth_scheme_option_resolver))
310 }
311}
312
313impl ResolveAuthSchemeOptions for SharedAuthSchemeOptionResolver {
314 #[allow(deprecated)]
315 fn resolve_auth_scheme_options(
316 &self,
317 params: &AuthSchemeOptionResolverParams,
318 ) -> Result<Cow<'_, [AuthSchemeId]>, BoxError> {
319 (*self.0).resolve_auth_scheme_options(params)
320 }
321
322 fn resolve_auth_scheme_options_v2<'a>(
323 &'a self,
324 params: &'a AuthSchemeOptionResolverParams,
325 cfg: &'a ConfigBag,
326 runtime_components: &'a RuntimeComponents,
327 ) -> AuthSchemeOptionsFuture<'a> {
328 (*self.0).resolve_auth_scheme_options_v2(params, cfg, runtime_components)
329 }
330}
331
332impl_shared_conversions!(
333 convert SharedAuthSchemeOptionResolver
334 from ResolveAuthSchemeOptions
335 using SharedAuthSchemeOptionResolver::new
336);
337
338pub trait AuthScheme: Send + Sync + fmt::Debug {
343 fn scheme_id(&self) -> AuthSchemeId;
349
350 fn identity_resolver(
359 &self,
360 identity_resolvers: &dyn GetIdentityResolver,
361 ) -> Option<SharedIdentityResolver>;
362
363 fn signer(&self) -> &dyn Sign;
365}
366
367#[derive(Clone, Debug)]
369pub struct SharedAuthScheme(Arc<dyn AuthScheme>);
370
371impl SharedAuthScheme {
372 pub fn new(auth_scheme: impl AuthScheme + 'static) -> Self {
374 Self(Arc::new(auth_scheme))
375 }
376}
377
378impl AuthScheme for SharedAuthScheme {
379 fn scheme_id(&self) -> AuthSchemeId {
380 self.0.scheme_id()
381 }
382
383 fn identity_resolver(
384 &self,
385 identity_resolvers: &dyn GetIdentityResolver,
386 ) -> Option<SharedIdentityResolver> {
387 self.0.identity_resolver(identity_resolvers)
388 }
389
390 fn signer(&self) -> &dyn Sign {
391 self.0.signer()
392 }
393}
394
395impl ValidateConfig for SharedAuthScheme {}
396
397impl_shared_conversions!(convert SharedAuthScheme from AuthScheme using SharedAuthScheme::new);
398
399pub trait Sign: Send + Sync + fmt::Debug {
401 fn sign_http_request(
405 &self,
406 request: &mut HttpRequest,
407 identity: &Identity,
408 auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>,
409 runtime_components: &RuntimeComponents,
410 config_bag: &ConfigBag,
411 ) -> Result<(), BoxError>;
412}
413
414#[non_exhaustive]
420#[derive(Clone, Debug)]
421pub struct AuthSchemeEndpointConfig<'a> {
422 inner: AuthSchemeEndpointConfigInner<'a>,
423}
424
425#[derive(Clone, Debug)]
426enum AuthSchemeEndpointConfigInner<'a> {
427 Empty,
428 Typed(&'a aws_smithy_types::endpoint::EndpointAuthScheme),
429 Document(&'a Document),
430}
431
432impl<'a> AuthSchemeEndpointConfig<'a> {
433 pub fn empty() -> Self {
435 Self {
436 inner: AuthSchemeEndpointConfigInner::Empty,
437 }
438 }
439
440 pub fn from_typed(auth_scheme: &'a aws_smithy_types::endpoint::EndpointAuthScheme) -> Self {
442 Self {
443 inner: AuthSchemeEndpointConfigInner::Typed(auth_scheme),
444 }
445 }
446
447 pub fn as_document(&self) -> Option<&'a Document> {
449 match &self.inner {
450 AuthSchemeEndpointConfigInner::Document(doc) => Some(doc),
451 _ => None,
452 }
453 }
454
455 pub fn get(&self, key: &str) -> Option<&'a Document> {
457 match &self.inner {
458 AuthSchemeEndpointConfigInner::Typed(scheme) => scheme.get(key),
459 AuthSchemeEndpointConfigInner::Document(doc) => {
460 doc.as_object().and_then(|obj| obj.get(key))
461 }
462 AuthSchemeEndpointConfigInner::Empty => None,
463 }
464 }
465}
466
467impl<'a> From<Option<&'a Document>> for AuthSchemeEndpointConfig<'a> {
468 fn from(value: Option<&'a Document>) -> Self {
469 match value {
470 Some(doc) => Self {
471 inner: AuthSchemeEndpointConfigInner::Document(doc),
472 },
473 None => Self::empty(),
474 }
475 }
476}
477
478impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> {
479 fn from(value: &'a Document) -> Self {
480 Self {
481 inner: AuthSchemeEndpointConfigInner::Document(value),
482 }
483 }
484}
485
486#[derive(Clone, Debug, Default, Eq, PartialEq)]
492pub struct AuthSchemePreference {
493 preference_list: Vec<AuthSchemeId>,
494}
495
496impl Storable for AuthSchemePreference {
497 type Storer = StoreReplace<Self>;
498}
499
500impl IntoIterator for AuthSchemePreference {
501 type Item = AuthSchemeId;
502 type IntoIter = std::vec::IntoIter<Self::Item>;
503
504 fn into_iter(self) -> Self::IntoIter {
505 self.preference_list.into_iter()
506 }
507}
508
509impl<T> From<T> for AuthSchemePreference
510where
511 T: AsRef<[AuthSchemeId]>,
512{
513 fn from(slice: T) -> Self {
514 AuthSchemePreference {
515 preference_list: slice.as_ref().to_vec(),
516 }
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523
524 #[test]
525 fn test_auth_scheme_id_new_normalizes_no_auth() {
526 let auth_scheme_id = AuthSchemeId::new("no_auth");
528 assert_eq!(auth_scheme_id.inner(), "noAuth");
529 }
530
531 #[test]
532 fn test_auth_scheme_id_new_preserves_no_auth_camel_case() {
533 let auth_scheme_id = AuthSchemeId::new("noAuth");
535 assert_eq!(auth_scheme_id.inner(), "noAuth");
536 }
537
538 #[test]
539 fn test_auth_scheme_id_new_preserves_other_schemes() {
540 let test_cases = [
542 "sigv4",
543 "sigv4a",
544 "httpBearerAuth",
545 "httpBasicAuth",
546 "custom_auth",
547 "bearer",
548 "basic",
549 ];
550
551 for scheme in test_cases {
552 let auth_scheme_id = AuthSchemeId::new(scheme);
553 assert_eq!(auth_scheme_id.inner(), scheme);
554 }
555 }
556
557 #[test]
558 fn test_auth_scheme_id_equality_after_normalization() {
559 let no_auth_underscore = AuthSchemeId::new("no_auth");
561 let no_auth_camel = AuthSchemeId::new("noAuth");
562
563 assert_eq!(no_auth_underscore, no_auth_camel);
564 assert_eq!(no_auth_underscore.inner(), no_auth_camel.inner());
565 }
566
567 #[test]
568 fn test_auth_scheme_id_hash_consistency_after_normalization() {
569 use std::collections::HashMap;
570
571 let mut map = HashMap::new();
573 let no_auth_underscore = AuthSchemeId::new("no_auth");
574 let no_auth_camel = AuthSchemeId::new("noAuth");
575
576 map.insert(no_auth_underscore.clone(), "value1");
577 map.insert(no_auth_camel.clone(), "value2");
578
579 assert_eq!(map.len(), 1);
581 assert_eq!(map.get(&no_auth_underscore), Some(&"value2"));
582 assert_eq!(map.get(&no_auth_camel), Some(&"value2"));
583 }
584
585 #[test]
586 fn test_auth_scheme_id_ordering_after_normalization() {
587 let no_auth_underscore = AuthSchemeId::new("no_auth");
589 let no_auth_camel = AuthSchemeId::new("noAuth");
590 let other_scheme = AuthSchemeId::new("sigv4");
591
592 assert_eq!(
593 no_auth_underscore.cmp(&no_auth_camel),
594 std::cmp::Ordering::Equal
595 );
596 assert_eq!(no_auth_underscore.cmp(&other_scheme), "noAuth".cmp("sigv4"));
597 }
598
599 #[test]
600 fn test_normalize_auth_scheme_id_function() {
601 assert_eq!(normalize_auth_scheme_id("no_auth"), "noAuth");
603 assert_eq!(normalize_auth_scheme_id("noAuth"), "noAuth");
604 assert_eq!(normalize_auth_scheme_id("sigv4"), "sigv4");
605 assert_eq!(normalize_auth_scheme_id("custom"), "custom");
606 }
607
608 #[test]
609 fn test_auth_scheme_id_from_cow_borrowed_normalizes_no_auth() {
610 let auth_scheme_id = AuthSchemeId::from(Cow::Borrowed("no_auth"));
612 assert_eq!(auth_scheme_id.inner(), "noAuth");
613 }
614
615 #[test]
616 fn test_auth_scheme_id_from_cow_borrowed_preserves_no_auth_camel_case() {
617 let auth_scheme_id = AuthSchemeId::from(Cow::Borrowed("noAuth"));
619 assert_eq!(auth_scheme_id.inner(), "noAuth");
620 }
621
622 #[test]
623 fn test_auth_scheme_id_from_cow_owned_normalizes_no_auth() {
624 let auth_scheme_id = AuthSchemeId::from(Cow::Owned(String::from("no_auth")));
626 assert_eq!(auth_scheme_id.inner(), "noAuth");
627 }
628
629 #[test]
630 fn test_auth_scheme_id_from_cow_owned_preserves_no_auth_camel_case() {
631 let auth_scheme_id = AuthSchemeId::from(Cow::Owned(String::from("noAuth")));
633 assert_eq!(auth_scheme_id.inner(), "noAuth");
634 }
635
636 #[test]
637 fn test_auth_scheme_id_from_cow_between_borrowed_and_owned_mixing_updated_and_legacy() {
638 let borrowed_no_auth = AuthSchemeId::from(Cow::Borrowed("noAuth"));
639 let owned_no_auth = AuthSchemeId::from(Cow::Owned(String::from("no_auth")));
640
641 assert_eq!(borrowed_no_auth, owned_no_auth);
642 assert_eq!(borrowed_no_auth.inner(), owned_no_auth.inner());
643 }
644}