Skip to content

Commit b87f5e0

Browse files
authoredJan 30, 2024
fix(credential-provider-ini): support sso-session based profile as source_profile (#4820)
Previously, when credential-provider-ini resolved credentials for a source profile, it loaded the profile properties, checked if the profile was an SSO profile, validated that all required sso_* properties were present and then resolved credentials with credential-provider-sso module. When source profile was an SSO profile that did not use an SSO session, this logic worked fine as the profile must include all the sso_* properties for SSO to succeed. However, when source profile was an SSO profile that used an SSO session, credential-provider-ini only resolved profile properties directly from the profile but not from the related SSO session. This caused sso-session based profiles to fail validation as some of the required sso_* properties are only defined in the referenced sso-session section. And hence the provider failed to resolve credentials for SSO session based source_profiles. This commit changes credential-provider-ini module to not resolve or validate SSO profile properties but delegate that all to the credential-provider-sso module that already contains all the logic needed to load and resolve profile properties and AWS credentials for both sso-session and non-sso-session based profiles. Instead of passing profile properties to credential-provider-sso fromSSO() method, new version only passes the profile name there and lets the credential-provider-sso module load and resolve the SSO profile with the logic that already exist in that module. Unit tests have been adjusted to function with the updated behavior. Following scenarios were also tested manually: * Resolve credentials for SSO profile without sso_session option * Resolve credentials for SSO profile with sso_session option * Resolve credentials for an assumed role profile whose source profile is an SSO profile without sso_session option * Resolve credentials for an assumed role profile whose source profile is an SSO profile with sso_session option AWS SDK resolved credentials correctly in all four cases. Fixes #4757.
1 parent 7d66163 commit b87f5e0

File tree

4 files changed

+12
-84
lines changed

4 files changed

+12
-84
lines changed
 

‎packages/credential-provider-ini/src/resolveProfileData.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,6 @@ describe(resolveProfileData.name, () => {
117117
(resolveSsoCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds));
118118
const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions);
119119
expect(receivedCreds).toStrictEqual(mockCreds);
120-
expect(resolveSsoCredentials).toHaveBeenCalledWith(mockProfiles[mockProfileName]);
120+
expect(resolveSsoCredentials).toHaveBeenCalledWith(mockProfileName);
121121
});
122122
});

‎packages/credential-provider-ini/src/resolveProfileData.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const resolveProfileData = async (
5151
}
5252

5353
if (isSsoProfile(data)) {
54-
return await resolveSsoCredentials(data);
54+
return await resolveSsoCredentials(profileName);
5555
}
5656

5757
// If the profile cannot be parsed or contains neither static credentials

‎packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts

+7-74
Original file line numberDiff line numberDiff line change
@@ -19,107 +19,40 @@ describe(isSsoProfile.name, () => {
1919
});
2020

2121
describe(resolveSsoCredentials.name, () => {
22-
const getMockOriginalSsoProfile = () => ({
23-
sso_start_url: "mock_sso_start_url",
24-
sso_account_id: "mock_sso_account_id",
25-
sso_region: "mock_sso_region",
26-
sso_role_name: "mock_sso_role_name",
27-
});
28-
29-
const getMockValidatedSsoProfile = <T>(add: T = {} as T) => ({
30-
sso_start_url: "mock_validated_sso_start_url",
31-
sso_account_id: "mock_validated_sso_account_id",
32-
sso_region: "mock_validated_sso_region",
33-
sso_role_name: "mock_validated_sso_role_name",
34-
...add,
35-
});
36-
3722
afterEach(() => {
3823
jest.clearAllMocks();
3924
});
4025

41-
it("throws error when validation fails", async () => {
42-
const mockProfile = getMockOriginalSsoProfile();
43-
const expectedError = new Error("error from validateSsoProfile");
44-
(validateSsoProfile as jest.Mock).mockImplementation(() => {
45-
throw expectedError;
46-
});
47-
try {
48-
await resolveSsoCredentials(mockProfile);
49-
fail(`expected ${expectedError}`);
50-
} catch (error) {
51-
expect(error).toStrictEqual(expectedError);
52-
}
53-
expect(validateSsoProfile).toHaveBeenCalledWith(mockProfile);
54-
});
55-
5626
it("throws error when fromSSO throws error", async () => {
57-
const mockProfile = getMockOriginalSsoProfile();
58-
const mockValidatedProfile = getMockValidatedSsoProfile();
27+
const mockProfileName = "mockProfileName";
5928
const expectedError = new Error("error from fromSSO");
6029

61-
(validateSsoProfile as jest.Mock).mockReturnValue(mockValidatedProfile);
6230
(fromSSO as jest.Mock).mockReturnValue(() => Promise.reject(expectedError));
6331

6432
try {
65-
await resolveSsoCredentials(mockProfile);
33+
await resolveSsoCredentials(mockProfileName);
6634
fail(`expected ${expectedError}`);
6735
} catch (error) {
6836
expect(error).toStrictEqual(expectedError);
6937
}
70-
expect(validateSsoProfile).toHaveBeenCalledWith(mockProfile);
7138
expect(fromSSO).toHaveBeenCalledWith({
72-
ssoStartUrl: mockValidatedProfile.sso_start_url,
73-
ssoAccountId: mockValidatedProfile.sso_account_id,
74-
ssoRegion: mockValidatedProfile.sso_region,
75-
ssoRoleName: mockValidatedProfile.sso_role_name,
39+
profile: mockProfileName,
7640
});
7741
});
7842

79-
it("calls fromSSO when validation succeeds", async () => {
80-
const mockProfile = getMockOriginalSsoProfile();
81-
const mockValidatedProfile = getMockValidatedSsoProfile();
82-
43+
it("calls fromSSO", async () => {
44+
const mockProfileName = "mockProfileName";
8345
const mockCreds: AwsCredentialIdentity = {
8446
accessKeyId: "mockAccessKeyId",
8547
secretAccessKey: "mockSecretAccessKey",
8648
};
8749

88-
(validateSsoProfile as jest.Mock).mockReturnValue(mockValidatedProfile);
8950
(fromSSO as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds));
9051

91-
const receivedCreds = await resolveSsoCredentials(mockProfile);
52+
const receivedCreds = await resolveSsoCredentials(mockProfileName);
9253
expect(receivedCreds).toStrictEqual(mockCreds);
93-
expect(validateSsoProfile).toHaveBeenCalledWith(mockProfile);
94-
expect(fromSSO).toHaveBeenCalledWith({
95-
ssoStartUrl: mockValidatedProfile.sso_start_url,
96-
ssoAccountId: mockValidatedProfile.sso_account_id,
97-
ssoRegion: mockValidatedProfile.sso_region,
98-
ssoRoleName: mockValidatedProfile.sso_role_name,
99-
});
100-
});
101-
102-
it("calls fromSSO with optional sso session name", async () => {
103-
const mockProfile = getMockOriginalSsoProfile();
104-
const mockValidatedProfile = getMockValidatedSsoProfile({
105-
sso_session: "test-session",
106-
});
107-
108-
const mockCreds: AwsCredentialIdentity = {
109-
accessKeyId: "mockAccessKeyId",
110-
secretAccessKey: "mockSecretAccessKey",
111-
};
112-
113-
(validateSsoProfile as jest.Mock).mockReturnValue(mockValidatedProfile);
114-
(fromSSO as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds));
115-
116-
await resolveSsoCredentials(mockProfile);
11754
expect(fromSSO).toHaveBeenCalledWith({
118-
ssoStartUrl: mockValidatedProfile.sso_start_url,
119-
ssoAccountId: mockValidatedProfile.sso_account_id,
120-
ssoRegion: mockValidatedProfile.sso_region,
121-
ssoRoleName: mockValidatedProfile.sso_role_name,
122-
ssoSession: mockValidatedProfile.sso_session,
55+
profile: mockProfileName,
12356
});
12457
});
12558
});

‎packages/credential-provider-ini/src/resolveSsoCredentials.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,10 @@ import type { Profile } from "@smithy/types";
44
/**
55
* @internal
66
*/
7-
export const resolveSsoCredentials = async (data: Partial<SsoProfile>) => {
8-
const { fromSSO, validateSsoProfile } = await import("@aws-sdk/credential-provider-sso");
9-
const { sso_start_url, sso_account_id, sso_session, sso_region, sso_role_name } = validateSsoProfile(data);
7+
export const resolveSsoCredentials = async (profile: string) => {
8+
const { fromSSO } = await import("@aws-sdk/credential-provider-sso");
109
return fromSSO({
11-
ssoStartUrl: sso_start_url,
12-
ssoAccountId: sso_account_id,
13-
ssoSession: sso_session,
14-
ssoRegion: sso_region,
15-
ssoRoleName: sso_role_name,
10+
profile,
1611
})();
1712
};
1813

0 commit comments

Comments
 (0)
Please sign in to comment.