1 + | /*
|
2 + | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3 + | * SPDX-License-Identifier: Apache-2.0
|
4 + | */
|
5 + |
|
6 + | use crate::provider_config::ProviderConfig;
|
7 + | use aws_runtime::env_config::EnvConfigValue;
|
8 + | use aws_smithy_runtime_api::client::auth::{AuthSchemeId, AuthSchemePreference};
|
9 + | use aws_smithy_types::error::display::DisplayErrorContext;
|
10 + | use std::borrow::Cow;
|
11 + | use std::fmt;
|
12 + |
|
13 + | mod env {
|
14 + | pub(super) const AUTH_SCHEME_PREFERENCE: &str = "AWS_AUTH_SCHEME_PREFERENCE";
|
15 + | }
|
16 + |
|
17 + | mod profile_key {
|
18 + | pub(super) const AUTH_SCHEME_PREFERENCE: &str = "auth_scheme_preference";
|
19 + | }
|
20 + |
|
21 + | /// Load the value for the auth scheme preference
|
22 + | ///
|
23 + | /// This checks the following sources:
|
24 + | /// 1. The environment variable `AWS_AUTH_SCHEME_PREFERENCE=scheme1,scheme2,scheme3`
|
25 + | /// 2. The profile key `auth_scheme_preference=scheme1,scheme2,scheme3`
|
26 + | ///
|
27 + | /// A scheme name can be either a fully qualified name or a shorthand with the namespace prefix trimmed.
|
28 + | /// For example, valid scheme names include "aws.auth#sigv4", "smithy.api#httpBasicAuth", "sigv4", and "httpBasicAuth".
|
29 + | /// Whitespace (spaces or tabs), including leading, trailing, and between names, is ignored.
|
30 + | ///
|
31 + | /// Returns `None` if a parsed string component is empty when creating an `AuthSchemeId`.
|
32 + | pub(crate) async fn auth_scheme_preference_provider(
|
33 + | provider_config: &ProviderConfig,
|
34 + | ) -> Option<AuthSchemePreference> {
|
35 + | let env = provider_config.env();
|
36 + | let profiles = provider_config.profile().await;
|
37 + |
|
38 + | EnvConfigValue::new()
|
39 + | .env(env::AUTH_SCHEME_PREFERENCE)
|
40 + | .profile(profile_key::AUTH_SCHEME_PREFERENCE)
|
41 + | .validate(&env, profiles, parse_auth_scheme_names)
|
42 + | .map_err(|err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for `AuthSchemePreference`"))
|
43 + | .unwrap_or(None)
|
44 + | }
|
45 + |
|
46 + | fn parse_auth_scheme_names(csv: &str) -> Result<AuthSchemePreference, InvalidAuthSchemeNamesCsv> {
|
47 + | csv.split(',')
|
48 + | .map(|s| {
|
49 + | let trimmed = s.trim().replace([' ', '\t'], "");
|
50 + | if trimmed.is_empty() {
|
51 + | return Err(InvalidAuthSchemeNamesCsv {
|
52 + | value: format!("Empty name found in `{csv}`."),
|
53 + | });
|
54 + | }
|
55 + | let scheme_name = trimmed.split('#').next_back().unwrap_or(&trimmed);
|
56 + | Ok(AuthSchemeId::from(Cow::Owned(scheme_name.to_owned())))
|
57 + | })
|
58 + | .collect::<Result<Vec<_>, _>>()
|
59 + | .map(AuthSchemePreference::from)
|
60 + | }
|
61 + |
|
62 + | #[derive(Debug)]
|
63 + | pub(crate) struct InvalidAuthSchemeNamesCsv {
|
64 + | value: String,
|
65 + | }
|
66 + |
|
67 + | impl fmt::Display for InvalidAuthSchemeNamesCsv {
|
68 + | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
69 + | write!(
|
70 + | f,
|
71 + | "Not a valid comma-separated auth scheme names: {}",
|
72 + | self.value
|
73 + | )
|
74 + | }
|
75 + | }
|
76 + |
|
77 + | impl std::error::Error for InvalidAuthSchemeNamesCsv {}
|
78 + |
|
79 + | #[cfg(test)]
|
80 + | mod test {
|
81 + | use super::env;
|
82 + | use crate::{
|
83 + | default_provider::auth_scheme_preference::auth_scheme_preference_provider,
|
84 + | provider_config::ProviderConfig,
|
85 + | };
|
86 + | use aws_types::os_shim_internal::Env;
|
87 + | use tracing_test::traced_test;
|
88 + |
|
89 + | #[tokio::test]
|
90 + | #[traced_test]
|
91 + | async fn log_error_on_invalid_value() {
|
92 + | let conf = ProviderConfig::empty().with_env(Env::from_slice(&[(
|
93 + | env::AUTH_SCHEME_PREFERENCE,
|
94 + | "scheme1, , \tscheme2",
|
95 + | )]));
|
96 + | assert_eq!(None, auth_scheme_preference_provider(&conf).await);
|
97 + | assert!(logs_contain(
|
98 + | "Not a valid comma-separated auth scheme names: Empty name found"
|
99 + | ));
|
100 + | }
|
101 + |
|
102 + | #[cfg(feature = "sso")] // for aws-smithy-runtime-api/http-auth
|
103 + | mod http_auth_tests {
|
104 + | use super::env;
|
105 + | #[allow(deprecated)]
|
106 + | use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
|
107 + | use crate::{
|
108 + | default_provider::auth_scheme_preference::auth_scheme_preference_provider,
|
109 + | provider_config::ProviderConfig,
|
110 + | };
|
111 + | use aws_smithy_runtime_api::client::auth::AuthSchemePreference;
|
112 + | use aws_types::os_shim_internal::{Env, Fs};
|
113 + |
|
114 + | #[tokio::test]
|
115 + | async fn environment_priority() {
|
116 + | let conf = ProviderConfig::empty()
|
117 + | .with_env(Env::from_slice(&[(
|
118 + | env::AUTH_SCHEME_PREFERENCE,
|
119 + | "aws.auth#sigv4, smithy.api#httpBasicAuth, smithy.api#httpDigestAuth, smithy.api#httpBearerAuth, smithy.api#httpApiKeyAuth",
|
120 + | )]))
|
121 + | .with_profile_config(
|
122 + | Some(
|
123 + | #[allow(deprecated)]
|
124 + | ProfileFiles::builder()
|
125 + | .with_file(
|
126 + | #[allow(deprecated)]
|
127 + | ProfileFileKind::Config,
|
128 + | "conf",
|
129 + | )
|
130 + | .build(),
|
131 + | ),
|
132 + | None,
|
133 + | )
|
134 + | .with_fs(Fs::from_slice(&[(
|
135 + | "conf",
|
136 + | "[default]\nauth_scheme_preference = scheme1, scheme2 , \tscheme3 \t",
|
137 + | )]));
|
138 + | assert_eq!(
|
139 + | AuthSchemePreference::from([
|
140 + | aws_runtime::auth::sigv4::SCHEME_ID,
|
141 + | aws_smithy_runtime_api::client::auth::http::HTTP_BASIC_AUTH_SCHEME_ID,
|
142 + | aws_smithy_runtime_api::client::auth::http::HTTP_DIGEST_AUTH_SCHEME_ID,
|
143 + | aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID,
|
144 + | aws_smithy_runtime_api::client::auth::http::HTTP_API_KEY_AUTH_SCHEME_ID,
|
145 + | ]),
|
146 + | auth_scheme_preference_provider(&conf).await.unwrap()
|
147 + | );
|
148 + | }
|
149 + |
|
150 + | #[tokio::test]
|
151 + | async fn load_from_profile() {
|
152 + | let conf = ProviderConfig::empty()
|
153 + | .with_profile_config(
|
154 + | Some(
|
155 + | #[allow(deprecated)]
|
156 + | ProfileFiles::builder()
|
157 + | .with_file(
|
158 + | #[allow(deprecated)]
|
159 + | ProfileFileKind::Config,
|
160 + | "conf",
|
161 + | )
|
162 + | .build(),
|
163 + | ),
|
164 + | None,
|
165 + | )
|
166 + | .with_fs(Fs::from_slice(&[(
|
167 + | "conf",
|
168 + | "[default]\nauth_scheme_preference = sigv4, httpBasicAuth, httpDigestAuth, \thttpBearerAuth \t, httpApiKeyAuth ",
|
169 + | )]));
|
170 + | assert_eq!(
|
171 + | AuthSchemePreference::from([
|
172 + | aws_runtime::auth::sigv4::SCHEME_ID,
|
173 + | aws_smithy_runtime_api::client::auth::http::HTTP_BASIC_AUTH_SCHEME_ID,
|
174 + | aws_smithy_runtime_api::client::auth::http::HTTP_DIGEST_AUTH_SCHEME_ID,
|
175 + | aws_smithy_runtime_api::client::auth::http::HTTP_BEARER_AUTH_SCHEME_ID,
|
176 + | aws_smithy_runtime_api::client::auth::http::HTTP_API_KEY_AUTH_SCHEME_ID,
|
177 + | ]),
|
178 + | auth_scheme_preference_provider(&conf).await.unwrap()
|
179 + | );
|
180 + | }
|
181 + | }
|
182 + | }
|