Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/credential-provider-ini/src/fromIni.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { AssumeRoleWithWebIdentityParams } from "@aws-sdk/credential-provider-web-identity";
import { getProfileName, parseKnownFiles, SourceProfileInit } from "@aws-sdk/shared-ini-file-loader";
import { getProfileName, parseKnownFiles } from "@aws-sdk/shared-ini-file-loader";
import { CredentialProvider, Credentials } from "@aws-sdk/types";

import { AssumeRoleParams } from "./resolveAssumeRoleCredentials";
import { resolveProfileData } from "./resolveProfileData";

export interface FromIniInit extends SourceProfileInit {
export interface FromIniInit {
/**
* A function that returns a promise fulfilled with an MFA token code for
* the provided MFA Serial code. If a profile requires an MFA code and
Expand Down Expand Up @@ -42,6 +42,6 @@ export interface FromIniInit extends SourceProfileInit {
export const fromIni =
(init: FromIniInit = {}): CredentialProvider =>
async () => {
const profiles = await parseKnownFiles(init);
return resolveProfileData(getProfileName(init), profiles, init);
const profiles = await parseKnownFiles();
return resolveProfileData(getProfileName(), profiles, init);
};
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const resolveAssumeRoleCredentials = async (
if (source_profile && source_profile in visitedProfiles) {
throw new CredentialsProviderError(
`Detected a cycle attempting to resolve credentials for profile` +
` ${getProfileName(options)}. Profiles visited: ` +
` ${getProfileName()}. Profiles visited: ` +
Object.keys(visitedProfiles).join(", "),
false
);
Expand Down
4 changes: 2 additions & 2 deletions packages/credential-provider-node/src/defaultProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ export const defaultProvider = (
const options = {
profile: process.env[ENV_PROFILE],
...init,
...(!init.loadedConfig && { loadedConfig: loadSharedConfigFiles(init) }),
...(!init.loadedConfig && { loadedConfig: loadSharedConfigFiles() }),
};

const providerChain = chain(
...(options.profile ? [] : [fromEnv()]),
fromSSO(options),
fromIni(options),
fromProcess(options),
fromProcess(),
fromTokenFile(options),
remoteProvider(options),
async () => {
Expand Down
14 changes: 5 additions & 9 deletions packages/credential-provider-process/src/fromProcess.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { getProfileName, parseKnownFiles, SourceProfileInit } from "@aws-sdk/shared-ini-file-loader";
import { getProfileName, parseKnownFiles } from "@aws-sdk/shared-ini-file-loader";
import { CredentialProvider } from "@aws-sdk/types";

import { resolveProcessCredentials } from "./resolveProcessCredentials";

export interface FromProcessInit extends SourceProfileInit {}

/**
* Creates a credential provider that will read from a credential_process specified
* in ini files.
*/
export const fromProcess =
(init: FromProcessInit = {}): CredentialProvider =>
async () => {
const profiles = await parseKnownFiles(init);
return resolveProcessCredentials(getProfileName(init), profiles);
};
export const fromProcess = (): CredentialProvider => async () => {
const profiles = await parseKnownFiles();
return resolveProcessCredentials(getProfileName(), profiles);
};
8 changes: 4 additions & 4 deletions packages/credential-provider-sso/src/fromSSO.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SSOClient } from "@aws-sdk/client-sso";
import { CredentialsProviderError } from "@aws-sdk/property-provider";
import { getProfileName, parseKnownFiles, SourceProfileInit } from "@aws-sdk/shared-ini-file-loader";
import { getProfileName, parseKnownFiles } from "@aws-sdk/shared-ini-file-loader";
import { CredentialProvider } from "@aws-sdk/types";

import { isSsoProfile } from "./isSsoProfile";
Expand Down Expand Up @@ -29,7 +29,7 @@ export interface SsoCredentialsParameters {
ssoRoleName: string;
}

export interface FromSSOInit extends SourceProfileInit {
export interface FromSSOInit {
ssoClient?: SSOClient;
}

Expand All @@ -43,8 +43,8 @@ export const fromSSO =
const { ssoStartUrl, ssoAccountId, ssoRegion, ssoRoleName, ssoClient } = init;
if (!ssoStartUrl && !ssoAccountId && !ssoRegion && !ssoRoleName) {
// Load the SSO config from shared AWS config file.
const profiles = await parseKnownFiles(init);
const profileName = getProfileName(init);
const profiles = await parseKnownFiles();
const profileName = getProfileName();
const profile = profiles[profileName];

if (!isSsoProfile(profile)) {
Expand Down
19 changes: 6 additions & 13 deletions packages/node-config-provider/src/configLoader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ jest.mock("./fromSharedConfigFiles");
jest.mock("@aws-sdk/property-provider");

describe("loadConfig", () => {
const configuration: SharedConfigInit = {
profile: "profile",
};

afterEach(() => {
jest.clearAllMocks();
});
Expand All @@ -28,18 +24,15 @@ describe("loadConfig", () => {
const envVarSelector = (env: NodeJS.ProcessEnv) => env["AWS_CONFIG_FOO"];
const configKey = (profile: Profile) => profile["aws_config_foo"];
const defaultValue = "foo-value";
loadConfig(
{
environmentVariableSelector: envVarSelector,
configFileSelector: configKey,
default: defaultValue,
},
configuration
);
loadConfig({
environmentVariableSelector: envVarSelector,
configFileSelector: configKey,
default: defaultValue,
});
expect(fromEnv).toHaveBeenCalledTimes(1);
expect(fromEnv).toHaveBeenCalledWith(envVarSelector);
expect(fromSharedConfigFiles).toHaveBeenCalledTimes(1);
expect(fromSharedConfigFiles).toHaveBeenCalledWith(configKey, configuration);
expect(fromSharedConfigFiles).toHaveBeenCalledWith(configKey, {});
expect(fromStatic).toHaveBeenCalledTimes(1);
expect(fromStatic).toHaveBeenCalledWith(defaultValue);
expect(chain).toHaveBeenCalledTimes(1);
Expand Down
50 changes: 14 additions & 36 deletions packages/node-config-provider/src/fromSharedConfigFiles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe("fromSharedConfigFiles", () => {
} & SharedConfigInit;

const loadedConfigResolves: (LoadedConfigTestData & {
profile?: string;
configValueToVerify: string;
})[] = [
{
Expand Down Expand Up @@ -97,7 +98,9 @@ describe("fromSharedConfigFiles", () => {
},
];

const loadedConfigRejects: LoadedConfigTestData[] = [
const loadedConfigRejects: (LoadedConfigTestData & {
profile?: string;
})[] = [
{
message: "rejects if default profile is not present and profile value is not passed",
iniDataInConfig: {
Expand All @@ -123,9 +126,8 @@ describe("fromSharedConfigFiles", () => {
configFile: iniDataInConfig,
credentialsFile: iniDataInCredentials,
});
return expect(fromSharedConfigFiles(configGetter, { profile, preferredFile })()).resolves.toBe(
configValueToVerify
);
if (profile) process.env[ENV_PROFILE] = profile;
return expect(fromSharedConfigFiles(configGetter, { preferredFile })()).resolves.toBe(configValueToVerify);
});
}
);
Expand All @@ -136,41 +138,14 @@ describe("fromSharedConfigFiles", () => {
configFile: iniDataInConfig,
credentialsFile: iniDataInCredentials,
});
return expect(fromSharedConfigFiles(configGetter, { profile, preferredFile })()).rejects.toMatchObject(
if (profile) process.env[ENV_PROFILE] = profile;
return expect(fromSharedConfigFiles(configGetter, { preferredFile })()).rejects.toMatchObject(
getCredentialsProviderError(profile ?? "default", configGetter)
);
});
});
});

describe("uses pre-loaded config if supplied", () => {
loadedConfigResolves.forEach(
({ message, iniDataInConfig, iniDataInCredentials, configValueToVerify, profile, preferredFile }) => {
it(`${message} from config file`, () => {
const loadedConfig = Promise.resolve({
configFile: iniDataInConfig,
credentialsFile: iniDataInCredentials,
});
return expect(
fromSharedConfigFiles(configGetter, { loadedConfig, profile, preferredFile })()
).resolves.toBe(configValueToVerify);
});
}
);

loadedConfigRejects.forEach(({ message, iniDataInConfig, iniDataInCredentials, profile, preferredFile }) => {
it(message, () => {
const loadedConfig = Promise.resolve({
configFile: iniDataInConfig,
credentialsFile: iniDataInCredentials,
});
return expect(
fromSharedConfigFiles(configGetter, { loadedConfig, profile, preferredFile })()
).rejects.toMatchObject(getCredentialsProviderError(profile ?? "default", configGetter));
});
});
});

it("rejects if getter throws", () => {
const message = "Cannot load config";
const failGetter = () => {
Expand All @@ -194,19 +169,22 @@ describe("fromSharedConfigFiles", () => {
default: { [configKey]: "credentialsFileDefault" },
},
};
const loadedConfig = Promise.resolve(loadedConfigData);

describe("when profile is not defined", () => {
beforeEach(() => {
(loadSharedConfigFiles as jest.Mock).mockResolvedValueOnce(loadedConfigData);
});

it(`returns configValue from value in '${ENV_PROFILE}' env var if it is set`, () => {
const profile = "foo";
process.env[ENV_PROFILE] = profile;
return expect(fromSharedConfigFiles(configGetter, { loadedConfig })()).resolves.toBe(
return expect(fromSharedConfigFiles(configGetter, {})()).resolves.toBe(
loadedConfigData.configFile[profile][configKey]
);
});

it(`returns configValue from default profile if '${ENV_PROFILE}' env var is not set`, () => {
return expect(fromSharedConfigFiles(configGetter, { loadedConfig })()).resolves.toBe(
return expect(fromSharedConfigFiles(configGetter, {})()).resolves.toBe(
loadedConfigData.configFile.default[configKey]
);
});
Expand Down
27 changes: 6 additions & 21 deletions packages/node-config-provider/src/fromSharedConfigFiles.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
import { CredentialsProviderError } from "@aws-sdk/property-provider";
import { loadSharedConfigFiles, SharedConfigInit as BaseSharedConfigInit } from "@aws-sdk/shared-ini-file-loader";
import { Profile, Provider, SharedConfigFiles } from "@aws-sdk/types";
import { loadSharedConfigFiles } from "@aws-sdk/shared-ini-file-loader";
import { Profile, Provider } from "@aws-sdk/types";

const DEFAULT_PROFILE = "default";
export const ENV_PROFILE = "AWS_PROFILE";

export interface SharedConfigInit extends BaseSharedConfigInit {
/**
* The configuration profile to use.
*/
profile?: string;

export interface SharedConfigInit {
/**
* The preferred shared ini file to load the config. "config" option refers to
* the shared config file(defaults to `~/.aws/config`). "credentials" option
* refers to the shared credentials file(defaults to `~/.aws/credentials`)
*/
preferredFile?: "config" | "credentials";

/**
* A promise that will be resolved with loaded and parsed credentials files.
* Used to avoid loading shared config files multiple times.
*
* @internal
*/
loadedConfig?: Promise<SharedConfigFiles>;
}

export type GetterFromConfig<T> = (profile: Profile) => T | undefined;
Expand All @@ -33,12 +20,10 @@ export type GetterFromConfig<T> = (profile: Profile) => T | undefined;
* Get config value from the shared config files with inferred profile name.
*/
export const fromSharedConfigFiles =
<T = string>(
configSelector: GetterFromConfig<T>,
{ preferredFile = "config", ...init }: SharedConfigInit = {}
): Provider<T> =>
<T = string>(configSelector: GetterFromConfig<T>, { preferredFile = "config" }: SharedConfigInit = {}): Provider<T> =>
async () => {
const { loadedConfig = loadSharedConfigFiles(init), profile = process.env[ENV_PROFILE] || DEFAULT_PROFILE } = init;
const loadedConfig = loadSharedConfigFiles();
const profile = process.env[ENV_PROFILE] || DEFAULT_PROFILE;

const { configFile, credentialsFile } = await loadedConfig;

Expand Down
3 changes: 1 addition & 2 deletions packages/shared-ini-file-loader/src/getProfileName.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export const ENV_PROFILE = "AWS_PROFILE";
export const DEFAULT_PROFILE = "default";

export const getProfileName = (init: { profile?: string }): string =>
init.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE;
export const getProfileName = (): string => process.env[ENV_PROFILE] || DEFAULT_PROFILE;
24 changes: 3 additions & 21 deletions packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,11 @@ import { slurpFile } from "./slurpFile";
export const ENV_CREDENTIALS_PATH = "AWS_SHARED_CREDENTIALS_FILE";
export const ENV_CONFIG_PATH = "AWS_CONFIG_FILE";

export interface SharedConfigInit {
/**
* The path at which to locate the ini credentials file. Defaults to the
* value of the `AWS_SHARED_CREDENTIALS_FILE` environment variable (if
* defined) or `~/.aws/credentials` otherwise.
*/
filepath?: string;

/**
* The path at which to locate the ini config file. Defaults to the value of
* the `AWS_CONFIG_FILE` environment variable (if defined) or
* `~/.aws/config` otherwise.
*/
configFilepath?: string;
}

const swallowError = () => ({});

export const loadSharedConfigFiles = async (init: SharedConfigInit = {}): Promise<SharedConfigFiles> => {
const {
filepath = process.env[ENV_CREDENTIALS_PATH] || join(getHomeDir(), ".aws", "credentials"),
configFilepath = process.env[ENV_CONFIG_PATH] || join(getHomeDir(), ".aws", "config"),
} = init;
export const loadSharedConfigFiles = async (): Promise<SharedConfigFiles> => {
const filepath = process.env[ENV_CREDENTIALS_PATH] || join(getHomeDir(), ".aws", "credentials");
const configFilepath = process.env[ENV_CONFIG_PATH] || join(getHomeDir(), ".aws", "config");

const parsedFiles = await Promise.all([
slurpFile(configFilepath).then(parseIni).then(normalizeConfigFile).catch(swallowError),
Expand Down
23 changes: 4 additions & 19 deletions packages/shared-ini-file-loader/src/parseKnownFiles.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
import { ParsedIniData, SharedConfigFiles } from "@aws-sdk/types";
import { ParsedIniData } from "@aws-sdk/types";

import { loadSharedConfigFiles, SharedConfigInit } from "./loadSharedConfigFiles";

export interface SourceProfileInit extends SharedConfigInit {
/**
* The configuration profile to use.
*/
profile?: string;

/**
* A promise that will be resolved with loaded and parsed credentials files.
* Used to avoid loading shared config files multiple times.
*
* @internal
*/
loadedConfig?: Promise<SharedConfigFiles>;
}
import { loadSharedConfigFiles } from "./loadSharedConfigFiles";

/**
* Load profiles from credentials and config INI files and normalize them into a
* single profile list.
*
* @internal
*/
export const parseKnownFiles = async (init: SourceProfileInit): Promise<ParsedIniData> => {
const { loadedConfig = loadSharedConfigFiles(init) } = init;
export const parseKnownFiles = async (): Promise<ParsedIniData> => {
const loadedConfig = loadSharedConfigFiles();

const parsedFiles = await loadedConfig;
return {
Expand Down
10 changes: 1 addition & 9 deletions packages/util-credentials/src/parse-known-profiles.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import {
parseKnownFiles as __parseKnownFiles,
SourceProfileInit as __SourceProfileInit,
} from "@aws-sdk/shared-ini-file-loader";

/**
* @deprecated Use SourceProfileInit from "@aws-sdk/shared-ini-file-loader" instead.
*/
export interface SourceProfileInit extends __SourceProfileInit {}
import { parseKnownFiles as __parseKnownFiles } from "@aws-sdk/shared-ini-file-loader";

/**
* @deprecated Use parseKnownFiles from "@aws-sdk/shared-ini-file-loader" instead.
Expand Down