1+ /*
2+ * Copyright 2013-2022 the original author or authors.
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * https://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+ package io .awspring .cloud .autoconfigure .core ;
17+
18+ import java .nio .file .Paths ;
19+ import java .util .ArrayList ;
20+ import java .util .List ;
21+ import org .slf4j .Logger ;
22+ import org .slf4j .LoggerFactory ;
23+ import org .springframework .beans .factory .ObjectProvider ;
24+ import org .springframework .boot .autoconfigure .AutoConfiguration ;
25+ import org .springframework .boot .autoconfigure .EnableAutoConfiguration ;
26+ import org .springframework .boot .autoconfigure .condition .ConditionalOnClass ;
27+ import org .springframework .boot .autoconfigure .condition .ConditionalOnMissingBean ;
28+ import org .springframework .boot .context .properties .EnableConfigurationProperties ;
29+ import org .springframework .boot .context .properties .PropertyMapper ;
30+ import org .springframework .context .annotation .Bean ;
31+ import org .springframework .lang .Nullable ;
32+ import org .springframework .util .ClassUtils ;
33+ import org .springframework .util .StringUtils ;
34+ import software .amazon .awssdk .auth .credentials .AnonymousCredentialsProvider ;
35+ import software .amazon .awssdk .auth .credentials .AwsBasicCredentials ;
36+ import software .amazon .awssdk .auth .credentials .AwsCredentialsProvider ;
37+ import software .amazon .awssdk .auth .credentials .AwsCredentialsProviderChain ;
38+ import software .amazon .awssdk .auth .credentials .DefaultCredentialsProvider ;
39+ import software .amazon .awssdk .auth .credentials .InstanceProfileCredentialsProvider ;
40+ import software .amazon .awssdk .auth .credentials .ProfileCredentialsProvider ;
41+ import software .amazon .awssdk .auth .credentials .StaticCredentialsProvider ;
42+ import software .amazon .awssdk .profiles .ProfileFile ;
43+ import software .amazon .awssdk .regions .providers .AwsRegionProvider ;
44+ import software .amazon .awssdk .services .sts .StsClient ;
45+ import software .amazon .awssdk .services .sts .auth .StsWebIdentityTokenFileCredentialsProvider ;
46+
47+ /**
48+ * {@link EnableAutoConfiguration} for {@link AwsCredentialsProvider}.
49+ *
50+ * @author Maciej Walkowiak
51+ * @author Eddú Meléndez
52+ * @author Eduan Bekker
53+ */
54+ @ AutoConfiguration
55+ @ ConditionalOnClass ({ AwsCredentialsProvider .class , ProfileFile .class })
56+ @ ConditionalOnMissingBean (AwsCredentialsProvider .class )
57+ @ EnableConfigurationProperties (CredentialsProperties .class )
58+ public class CredentialsProviderAutoConfiguration {
59+
60+ private static final Logger LOGGER = LoggerFactory .getLogger (CredentialsProviderAutoConfiguration .class );
61+
62+ private static final String STS_WEB_IDENTITY_TOKEN_FILE_CREDENTIALS_PROVIDER = "software.amazon.awssdk.services.sts.auth.StsWebIdentityTokenFileCredentialsProvider" ;
63+
64+ private final CredentialsProperties properties ;
65+
66+ private final AwsRegionProvider regionProvider ;
67+
68+ private final ObjectProvider <AwsConnectionDetails > connectionDetails ;
69+
70+ public CredentialsProviderAutoConfiguration (CredentialsProperties properties , AwsRegionProvider regionProvider ,
71+ ObjectProvider <AwsConnectionDetails > connectionDetails ) {
72+ this .properties = properties ;
73+ this .regionProvider = regionProvider ;
74+ this .connectionDetails = connectionDetails ;
75+ }
76+
77+ @ Bean
78+ public AwsCredentialsProvider credentialsProvider () {
79+ return createCredentialsProvider (this .properties , this .regionProvider , this .connectionDetails .getIfAvailable ());
80+ }
81+
82+ public static AwsCredentialsProvider createCredentialsProvider (CredentialsProperties properties ,
83+ AwsRegionProvider regionProvider ) {
84+ return createCredentialsProvider (properties , regionProvider , null );
85+ }
86+
87+ public static AwsCredentialsProvider createCredentialsProvider (CredentialsProperties properties ,
88+ AwsRegionProvider regionProvider , @ Nullable AwsConnectionDetails connectionDetails ) {
89+ final List <AwsCredentialsProvider > providers = new ArrayList <>();
90+
91+ if (connectionDetails != null && StringUtils .hasText (connectionDetails .getAccessKey ())
92+ && StringUtils .hasText (connectionDetails .getSecretKey ())) {
93+ providers .add (createStaticCredentialsProvider (connectionDetails ));
94+ }
95+
96+ if (StringUtils .hasText (properties .getAccessKey ()) && StringUtils .hasText (properties .getSecretKey ())) {
97+ providers .add (createStaticCredentialsProvider (properties ));
98+ }
99+
100+ if (properties .isInstanceProfile ()) {
101+ providers .add (InstanceProfileCredentialsProvider .create ());
102+ }
103+
104+ Profile profile = properties .getProfile ();
105+ if (profile != null && profile .getName () != null ) {
106+ providers .add (createProfileCredentialProvider (profile ));
107+ }
108+
109+ StsProperties sts = properties .getSts ();
110+ if (ClassUtils .isPresent (STS_WEB_IDENTITY_TOKEN_FILE_CREDENTIALS_PROVIDER , null )) {
111+ try {
112+ providers .add (StsCredentialsProviderFactory .create (sts , regionProvider ));
113+ }
114+ catch (IllegalStateException e ) {
115+ LOGGER .warn (
116+ "Skipping creating `StsCredentialsProvider`. `software.amazon.awssdk:sts` is on the classpath, but neither `spring.cloud.aws.credentials.sts` properties are configured nor `AWS_WEB_IDENTITY_TOKEN_FILE` or the javaproperty `aws.webIdentityTokenFile` is set" );
117+ }
118+ }
119+
120+ if (providers .isEmpty ()) {
121+ return DefaultCredentialsProvider .create ();
122+ }
123+ else if (providers .size () == 1 ) {
124+ return providers .get (0 );
125+ }
126+ else {
127+ return AwsCredentialsProviderChain .builder ().credentialsProviders (providers ).build ();
128+ }
129+ }
130+
131+ private static StaticCredentialsProvider createStaticCredentialsProvider (CredentialsProperties properties ) {
132+ return StaticCredentialsProvider
133+ .create (AwsBasicCredentials .create (properties .getAccessKey (), properties .getSecretKey ()));
134+ }
135+
136+ private static StaticCredentialsProvider createStaticCredentialsProvider (AwsConnectionDetails connectionDetails ) {
137+ return StaticCredentialsProvider
138+ .create (AwsBasicCredentials .create (connectionDetails .getAccessKey (), connectionDetails .getSecretKey ()));
139+ }
140+
141+ private static ProfileCredentialsProvider createProfileCredentialProvider (Profile profile ) {
142+ ProfileFile profileFile ;
143+ if (profile .getPath () != null ) {
144+ profileFile = ProfileFile .builder ()
145+ .type (ProfileFile .Type .CREDENTIALS )
146+ .content (Paths .get (profile .getPath ()))
147+ .build ();
148+ }
149+ else {
150+ profileFile = ProfileFile .defaultProfileFile ();
151+ }
152+ return ProfileCredentialsProvider .builder ().profileName (profile .getName ()).profileFile (profileFile ).build ();
153+ }
154+
155+ /**
156+ * Wrapper class to avoid {@link NoClassDefFoundError}.
157+ */
158+ private static class StsCredentialsProviderFactory {
159+
160+ private static AwsCredentialsProvider create (@ Nullable StsProperties stsProperties ,
161+ AwsRegionProvider regionProvider ) {
162+ PropertyMapper propertyMapper = PropertyMapper .get ();
163+ StsWebIdentityTokenFileCredentialsProvider .Builder builder = StsWebIdentityTokenFileCredentialsProvider
164+ .builder ()
165+ .stsClient (StsClient .builder ()
166+ .credentialsProvider (AnonymousCredentialsProvider .create ())
167+ .region (regionProvider .getRegion ())
168+ .build ());
169+
170+ if (stsProperties != null ) {
171+ builder .asyncCredentialUpdateEnabled (stsProperties .isAsyncCredentialsUpdate ());
172+ propertyMapper .from (stsProperties ::getRoleArn ).to (builder ::roleArn );
173+ propertyMapper .from (stsProperties ::getWebIdentityTokenFile )
174+ .to (b -> builder .webIdentityTokenFile (Paths .get (b )));
175+ propertyMapper .from (stsProperties ::getRoleSessionName ).to (builder ::roleSessionName );
176+ }
177+ return builder .build ();
178+ }
179+
180+ }
181+
182+ }
0 commit comments