|
1 | 1 | import { fromEnv } from "@aws-sdk/credential-provider-env";
|
2 | 2 | import { fromContainerMetadata, fromInstanceMetadata } from "@aws-sdk/credential-provider-imds";
|
| 3 | +import { fromSSO, isSsoProfile, validateSsoProfile } from "@aws-sdk/credential-provider-sso"; |
3 | 4 | import { fromTokenFile } from "@aws-sdk/credential-provider-web-identity";
|
4 | 5 | import { ENV_CONFIG_PATH, ENV_CREDENTIALS_PATH } from "@aws-sdk/shared-ini-file-loader";
|
5 | 6 | import { Credentials } from "@aws-sdk/types";
|
@@ -61,6 +62,8 @@ jest.mock("@aws-sdk/credential-provider-imds");
|
61 | 62 |
|
62 | 63 | jest.mock("@aws-sdk/credential-provider-env");
|
63 | 64 |
|
| 65 | +jest.mock("@aws-sdk/credential-provider-sso"); |
| 66 | + |
64 | 67 | const DEFAULT_CREDS = {
|
65 | 68 | accessKeyId: "AKIAIOSFODNN7EXAMPLE",
|
66 | 69 | secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
@@ -989,6 +992,120 @@ role_arn = ${roleArn}`.trim()
|
989 | 992 | });
|
990 | 993 | });
|
991 | 994 |
|
| 995 | + describe("assume role with SSO", () => { |
| 996 | + const DEFAULT_PATH = join(homedir(), ".aws", "credentials"); |
| 997 | + it("should continue if profile is not configured with an SSO credential", async () => { |
| 998 | + __addMatcher( |
| 999 | + DEFAULT_PATH, |
| 1000 | + `[default] |
| 1001 | +aws_access_key_id = ${DEFAULT_CREDS.accessKeyId} |
| 1002 | +aws_secret_access_key = ${DEFAULT_CREDS.secretAccessKey} |
| 1003 | +aws_session_token = ${DEFAULT_CREDS.sessionToken} |
| 1004 | + `.trim() |
| 1005 | + ); |
| 1006 | + await fromIni()(); |
| 1007 | + expect(fromSSO).not.toHaveBeenCalled(); |
| 1008 | + }); |
| 1009 | + |
| 1010 | + it("should throw if profile is configured with incomplete SSO credential", async () => { |
| 1011 | + (isSsoProfile as unknown as jest.Mock).mockImplementationOnce(() => true); |
| 1012 | + const originalValidator = jest.requireActual("@aws-sdk/credential-provider-sso").validateSsoProfile; |
| 1013 | + (validateSsoProfile as unknown as jest.Mock).mockImplementationOnce(originalValidator); |
| 1014 | + __addMatcher( |
| 1015 | + DEFAULT_PATH, |
| 1016 | + `[default] |
| 1017 | +sso_account_id = 1234567890 |
| 1018 | +sso_start_url = https://example.com/sso/ |
| 1019 | + `.trim() |
| 1020 | + ); |
| 1021 | + try { |
| 1022 | + await fromIni()(); |
| 1023 | + } catch (e) { |
| 1024 | + console.error(e.message); |
| 1025 | + expect(e.message).toEqual(expect.stringContaining("Profile is configured with invalid SSO credentials")); |
| 1026 | + } |
| 1027 | + }); |
| 1028 | + |
| 1029 | + it("should resolve valid SSO credential", async () => { |
| 1030 | + (isSsoProfile as unknown as jest.Mock).mockImplementationOnce(() => true); |
| 1031 | + const originalValidator = jest.requireActual("@aws-sdk/credential-provider-sso").validateSsoProfile; |
| 1032 | + (validateSsoProfile as jest.Mock).mockImplementationOnce(originalValidator); |
| 1033 | + (fromSSO as jest.Mock).mockImplementationOnce(() => async () => DEFAULT_CREDS); |
| 1034 | + const accountId = "1234567890"; |
| 1035 | + const startUrl = "https://example.com/sso/"; |
| 1036 | + const region = "us-east-1"; |
| 1037 | + const roleName = "role"; |
| 1038 | + __addMatcher( |
| 1039 | + DEFAULT_PATH, |
| 1040 | + `[default] |
| 1041 | +sso_account_id = ${accountId} |
| 1042 | +sso_start_url = ${startUrl} |
| 1043 | +sso_region = ${region} |
| 1044 | +sso_role_name = ${roleName} |
| 1045 | + `.trim() |
| 1046 | + ); |
| 1047 | + await fromIni()(); |
| 1048 | + expect(fromSSO as unknown as jest.Mock).toBeCalledWith({ |
| 1049 | + ssoAccountId: accountId, |
| 1050 | + ssoStartUrl: startUrl, |
| 1051 | + ssoRegion: region, |
| 1052 | + ssoRoleName: roleName, |
| 1053 | + }); |
| 1054 | + }); |
| 1055 | + |
| 1056 | + it("should call fromTokenFile with assume role chaining", async () => { |
| 1057 | + (isSsoProfile as unknown as jest.Mock).mockImplementationOnce( |
| 1058 | + jest.requireActual("@aws-sdk/credential-provider-sso").isSsoProfile |
| 1059 | + ); |
| 1060 | + (validateSsoProfile as unknown as jest.Mock).mockImplementationOnce( |
| 1061 | + jest.requireActual("@aws-sdk/credential-provider-sso").validateSsoProfile |
| 1062 | + ); |
| 1063 | + (fromSSO as jest.Mock).mockImplementationOnce(() => async () => DEFAULT_CREDS); |
| 1064 | + const accountId = "1234567890"; |
| 1065 | + const startUrl = "https://example.com/sso/"; |
| 1066 | + const region = "us-east-1"; |
| 1067 | + const roleName = "role"; |
| 1068 | + const roleAssumerWithWebIdentity = jest.fn(); |
| 1069 | + |
| 1070 | + const fooRoleArn = "arn:aws:iam::123456789:role/foo"; |
| 1071 | + const fooSessionName = "fooSession"; |
| 1072 | + __addMatcher( |
| 1073 | + DEFAULT_PATH, |
| 1074 | + ` |
| 1075 | +[bar] |
| 1076 | +sso_account_id = ${accountId} |
| 1077 | +sso_start_url = ${startUrl} |
| 1078 | +sso_region = ${region} |
| 1079 | +sso_role_name = ${roleName} |
| 1080 | +
|
| 1081 | +[foo] |
| 1082 | +role_arn = ${fooRoleArn} |
| 1083 | +role_session_name = ${fooSessionName} |
| 1084 | +source_profile = bar`.trim() |
| 1085 | + ); |
| 1086 | + |
| 1087 | + const provider = fromIni({ |
| 1088 | + profile: "foo", |
| 1089 | + roleAssumer(sourceCreds: Credentials, params: AssumeRoleParams): Promise<Credentials> { |
| 1090 | + expect(sourceCreds).toEqual(DEFAULT_CREDS); |
| 1091 | + expect(params.RoleArn).toEqual(fooRoleArn); |
| 1092 | + expect(params.RoleSessionName).toEqual(fooSessionName); |
| 1093 | + return Promise.resolve(FOO_CREDS); |
| 1094 | + }, |
| 1095 | + roleAssumerWithWebIdentity, |
| 1096 | + }); |
| 1097 | + |
| 1098 | + expect(await provider()).toEqual(FOO_CREDS); |
| 1099 | + expect(fromSSO).toHaveBeenCalledTimes(1); |
| 1100 | + expect(fromSSO).toHaveBeenCalledWith({ |
| 1101 | + ssoAccountId: accountId, |
| 1102 | + ssoStartUrl: startUrl, |
| 1103 | + ssoRegion: region, |
| 1104 | + ssoRoleName: roleName, |
| 1105 | + }); |
| 1106 | + }); |
| 1107 | + }); |
| 1108 | + |
992 | 1109 | it("should prefer credentials in ~/.aws/credentials to those in ~/.aws/config", async () => {
|
993 | 1110 | __addMatcher(
|
994 | 1111 | join(homedir(), ".aws", "credentials"),
|
|
0 commit comments