Skip to content

Commit b74ce8e

Browse files
authored
Use the SSOTokenProvider in the SSOCredentialProvider (#3532)
* Use the SSOTokenProvider in the SSOCredentialProvider * Use the SSOTokenProvider in the SSOCredentialProvider , Handled review comments * Adding sso and auth-test in tests-coverage-reporting/pom.xml * Added sso_start_url validation check such that profile and sso-session has same sso_start_url * Updated the error message when common properties mismatch
1 parent a7d0930 commit b74ce8e

File tree

15 files changed

+647
-51
lines changed

15 files changed

+647
-51
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "The SSOCredentialProvider should resolve to SSO Token Provider if the profile has sso_session property defined in it."
6+
}

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProviderFactory.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616
package software.amazon.awssdk.auth.credentials;
1717

1818
import software.amazon.awssdk.annotations.SdkProtectedApi;
19-
import software.amazon.awssdk.profiles.Profile;
2019

2120
/**
22-
* A factory for {@link AwsCredentialsProvider}s, which can be used to create different credentials providers with
23-
* different Profile properties.
21+
* A factory for {@link AwsCredentialsProvider}s, which can be used to create different credentials providers with different
22+
* Provider specifications like profile properties.
2423
*/
2524
@FunctionalInterface
2625
@SdkProtectedApi
2726
public interface ProfileCredentialsProviderFactory {
28-
AwsCredentialsProvider create(Profile profile);
27+
AwsCredentialsProvider create(ProfileProviderCredentialsContext profileProviderCredentialsContext);
2928
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.auth.credentials;
17+
18+
19+
import java.util.Objects;
20+
import software.amazon.awssdk.annotations.SdkProtectedApi;
21+
import software.amazon.awssdk.profiles.Profile;
22+
import software.amazon.awssdk.profiles.ProfileFile;
23+
24+
/**
25+
* Context class that defines the required properties for creation of a Credentials provider*
26+
*/
27+
@SdkProtectedApi
28+
public final class ProfileProviderCredentialsContext {
29+
30+
private final Profile profile;
31+
private final ProfileFile profileFile;
32+
33+
private ProfileProviderCredentialsContext(Profile profile, ProfileFile profileFile) {
34+
this.profile = profile;
35+
this.profileFile = profileFile;
36+
}
37+
38+
public static Builder builder() {
39+
return new Builder();
40+
}
41+
42+
/**
43+
* Getter method for profile.
44+
* @return The profile that should be used to load the configuration necessary to create the credential provider.
45+
*/
46+
public Profile profile() {
47+
return profile;
48+
}
49+
50+
/**
51+
* Getter for profileFile.
52+
* @return ProfileFile that has the profile which is used to create the credential provider.
53+
*/
54+
public ProfileFile profileFile() {
55+
return profileFile;
56+
}
57+
58+
@Override
59+
public boolean equals(Object o) {
60+
if (this == o) {
61+
return true;
62+
}
63+
if (o == null || getClass() != o.getClass()) {
64+
return false;
65+
}
66+
67+
ProfileProviderCredentialsContext that = (ProfileProviderCredentialsContext) o;
68+
return Objects.equals(profile, that.profile) && Objects.equals(profileFile, that.profileFile);
69+
}
70+
71+
@Override
72+
public int hashCode() {
73+
int result = profile != null ? profile.hashCode() : 0;
74+
result = 31 * result + (profileFile != null ? profileFile.hashCode() : 0);
75+
return result;
76+
}
77+
78+
public static final class Builder {
79+
private Profile profile;
80+
private ProfileFile profileFile;
81+
82+
private Builder() {
83+
}
84+
85+
/**
86+
* Builder interface to set profile.
87+
* @param profile The profile that should be used to load the configuration necessary to create the credential provider.
88+
* @return Returns a reference to this object so that method calls can be chained together.
89+
*/
90+
public Builder profile(Profile profile) {
91+
this.profile = profile;
92+
return this;
93+
}
94+
95+
/**
96+
* Builder interface to set ProfileFile.
97+
* @param profileFile The ProfileFile that has the profile which is used to create the credential provider. This is *
98+
* required to fetch the titles like sso-session defined in profile property* *
99+
* @return Returns a reference to this object so that method calls can be chained together.
100+
*/
101+
public Builder profileFile(ProfileFile profileFile) {
102+
this.profileFile = profileFile;
103+
return this;
104+
}
105+
106+
public ProfileProviderCredentialsContext build() {
107+
return new ProfileProviderCredentialsContext(profile, profileFile);
108+
}
109+
}
110+
}

core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@
3636
import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
3737
import software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider;
3838
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProviderFactory;
39+
import software.amazon.awssdk.auth.credentials.ProfileProviderCredentialsContext;
3940
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
4041
import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider;
4142
import software.amazon.awssdk.core.internal.util.ClassLoaderHelper;
4243
import software.amazon.awssdk.profiles.Profile;
4344
import software.amazon.awssdk.profiles.ProfileFile;
4445
import software.amazon.awssdk.profiles.ProfileProperty;
46+
import software.amazon.awssdk.profiles.internal.ProfileSection;
4547
import software.amazon.awssdk.utils.SdkAutoCloseable;
4648
import software.amazon.awssdk.utils.Validate;
4749

@@ -110,8 +112,11 @@ private Optional<AwsCredentialsProvider> credentialsProvider(Set<String> childre
110112
return Optional.ofNullable(roleAndWebIdentityTokenProfileCredentialsProvider());
111113
}
112114

113-
if (properties.containsKey(ProfileProperty.SSO_ROLE_NAME) || properties.containsKey(ProfileProperty.SSO_ACCOUNT_ID)
114-
|| properties.containsKey(ProfileProperty.SSO_REGION) || properties.containsKey(ProfileProperty.SSO_START_URL)) {
115+
if (properties.containsKey(ProfileProperty.SSO_ROLE_NAME)
116+
|| properties.containsKey(ProfileProperty.SSO_ACCOUNT_ID)
117+
|| properties.containsKey(ProfileProperty.SSO_REGION)
118+
|| properties.containsKey(ProfileProperty.SSO_START_URL)
119+
|| properties.containsKey(ProfileSection.SSO_SESSION.getPropertyKeyName())) {
115120
return Optional.ofNullable(ssoProfileCredentialsProvider());
116121
}
117122

@@ -182,11 +187,21 @@ private AwsCredentialsProvider credentialProcessCredentialsProvider() {
182187
* Create the SSO credentials provider based on the related profile properties.
183188
*/
184189
private AwsCredentialsProvider ssoProfileCredentialsProvider() {
190+
validateRequiredPropertiesForSsoCredentialsProvider();
191+
return ssoCredentialsProviderFactory().create(
192+
ProfileProviderCredentialsContext.builder()
193+
.profile(profile)
194+
.profileFile(profileFile)
195+
.build());
196+
}
197+
198+
private void validateRequiredPropertiesForSsoCredentialsProvider() {
185199
requireProperties(ProfileProperty.SSO_ACCOUNT_ID,
186-
ProfileProperty.SSO_REGION,
187-
ProfileProperty.SSO_ROLE_NAME,
188-
ProfileProperty.SSO_START_URL);
189-
return ssoCredentialsProviderFactory().create(profile);
200+
ProfileProperty.SSO_ROLE_NAME);
201+
202+
if (!properties.containsKey(ProfileSection.SSO_SESSION.getPropertyKeyName())) {
203+
requireProperties(ProfileProperty.SSO_REGION, ProfileProperty.SSO_START_URL);
204+
}
190205
}
191206

192207
private AwsCredentialsProvider roleAndWebIdentityTokenProfileCredentialsProvider() {

core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtilsTest.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,22 @@
1919
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2020

2121
import java.io.File;
22+
import java.nio.charset.StandardCharsets;
2223
import java.util.Arrays;
2324
import java.util.List;
2425
import java.util.function.Consumer;
26+
import java.util.stream.Stream;
2527
import org.junit.jupiter.api.AfterAll;
2628
import org.junit.jupiter.api.BeforeAll;
2729
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.params.ParameterizedTest;
31+
import org.junit.jupiter.params.provider.Arguments;
32+
import org.junit.jupiter.params.provider.MethodSource;
2833
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
2934
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
3035
import software.amazon.awssdk.auth.credentials.ProcessCredentialsProviderTest;
36+
import software.amazon.awssdk.core.checksums.Algorithm;
37+
import software.amazon.awssdk.core.checksums.SdkChecksum;
3138
import software.amazon.awssdk.profiles.ProfileFile;
3239
import software.amazon.awssdk.profiles.ProfileProperty;
3340
import software.amazon.awssdk.utils.StringInputStream;
@@ -205,6 +212,60 @@ public void profileFileWithCircularDependencyThrowsExceptionWhenResolvingCredent
205212
.hasMessageContaining("Invalid profile file: Circular relationship detected with profiles");
206213
}
207214

215+
private static Stream<Arguments> roleProfileWithIncompleteSsoRoleNameSSOCredentialsProperties() {
216+
return Stream.of(
217+
// No sso_region
218+
Arguments.of(configFile("[profile test]\n" +
219+
"sso_account_id=accountId\n" +
220+
"sso_role_name=roleName\n" +
221+
"sso_start_url=https//d-abc123.awsapps.com/start"),
222+
"Profile property 'sso_region' was not configured for 'test'"),
223+
// No sso_account_id
224+
Arguments.of(configFile("[profile test]\n" +
225+
"sso_region=region\n" +
226+
"sso_role_name=roleName\n" +
227+
"sso_start_url=https//d-abc123.awsapps.com/start"),
228+
"Profile property 'sso_account_id' was not configured for 'test'"),
229+
// No sso_start_url
230+
Arguments.of(configFile("[profile test]\n" +
231+
"sso_account_id=accountId\n" +
232+
"sso_region=region\n" +
233+
"sso_role_name=roleName\n" +
234+
"sso_region=region"),
235+
"Profile property 'sso_start_url' was not configured for 'test'"),
236+
// No sso_role_name
237+
Arguments.of(configFile("[profile test]\n" +
238+
"sso_account_id=accountId\n" +
239+
"sso_region=region\n" +
240+
"sso_start_url=https//d-abc123.awsapps.com/start"),
241+
"Profile property 'sso_role_name' was not configured for 'test'"),
242+
// sso_session with No sso_account_id
243+
Arguments.of(configFile("[profile test]\n" +
244+
"sso_session=session\n" +
245+
"sso_role_name=roleName"),
246+
"Profile property 'sso_account_id' was not configured for 'test'"),
247+
// sso_session with No sso_role_name
248+
Arguments.of(configFile("[profile test]\n" +
249+
"sso_session=session\n" +
250+
"sso_account_id=accountId\n" +
251+
"sso_start_url=https//d-abc123.awsapps.com/start"),
252+
"Profile property 'sso_role_name' was not configured for 'test'"),
253+
Arguments.of(configFile("[profile test]\n" +
254+
"sso_session=session"),
255+
"Profile property 'sso_account_id' was not configured for 'test'")
256+
);
257+
}
258+
259+
@ParameterizedTest
260+
@MethodSource("roleProfileWithIncompleteSsoRoleNameSSOCredentialsProperties")
261+
void validateCheckSumValues(ProfileFile profiles, String expectedValue) {
262+
assertThat(profiles.profile("test")).hasValueSatisfying(profile -> {
263+
ProfileCredentialsUtils profileCredentialsUtils = new ProfileCredentialsUtils(profiles, profile, profiles::profile);
264+
assertThatThrownBy(profileCredentialsUtils::credentialsProvider)
265+
.hasMessageContaining(expectedValue);
266+
});
267+
}
268+
208269
@Test
209270
public void profileWithBothCredentialSourceAndSourceProfileThrowsException() {
210271
ProfileFile configFile = configFile("[profile test]\n" +
@@ -279,7 +340,7 @@ private ProfileFile allTypesProfile() {
279340
"role_arn=arn:aws:iam::123456789012:role/testRole\n");
280341
}
281342

282-
private ProfileFile configFile(String configFile) {
343+
private static ProfileFile configFile(String configFile) {
283344
return ProfileFile.builder()
284345
.content(new StringInputStream(configFile))
285346
.type(ProfileFile.Type.CONFIGURATION)

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@
609609
<exclude>software.amazon.awssdk.thirdparty.*</exclude>
610610
<exclude>software.amazon.awssdk.regions.*</exclude>
611611
<exclude>software.amazon.awssdk.core.signer.NoOpSigner</exclude>
612+
<exclude>software.amazon.awssdk.auth.credentials.ProfileCredentialsProviderFactory</exclude>
612613
</excludes>
613614

614615
<ignoreMissingOldVersion>true</ignoreMissingOldVersion>

services/sso/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,10 @@
7979
<artifactId>guava</artifactId>
8080
<scope>test</scope>
8181
</dependency>
82+
<dependency>
83+
<groupId>org.mockito</groupId>
84+
<artifactId>mockito-junit-jupiter</artifactId>
85+
<scope>test</scope>
86+
</dependency>
8287
</dependencies>
8388
</project>

0 commit comments

Comments
 (0)