Skip to content

fix(credential-provider-ini): refactor provider options interfaces #2048

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 19, 2021
Merged
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
92 changes: 47 additions & 45 deletions packages/credential-provider-ini/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export interface AssumeRoleParams {
TokenCode?: string;
}

export interface FromIniInit extends SharedConfigInit {
export interface SourceProfileInit extends SharedConfigInit {
/**
* The configuration profile to use.
*/
Expand All @@ -57,7 +57,9 @@ export interface FromIniInit extends SharedConfigInit {
* @internal
*/
loadedConfig?: Promise<SharedConfigFiles>;
}

export interface FromIniInit extends SourceProfileInit {
/**
* A function that returna a promise fulfilled with an MFA token code for
* the provided MFA Serial code. If a profile requires an MFA code and
Expand All @@ -84,51 +86,64 @@ interface StaticCredsProfile extends Profile {
aws_session_token?: string;
}

function isStaticCredsProfile(arg: any): arg is StaticCredsProfile {
return (
Boolean(arg) &&
typeof arg === "object" &&
typeof arg.aws_access_key_id === "string" &&
typeof arg.aws_secret_access_key === "string" &&
["undefined", "string"].indexOf(typeof arg.aws_session_token) > -1
);
}
const isStaticCredsProfile = (arg: any): arg is StaticCredsProfile =>
Boolean(arg) &&
typeof arg === "object" &&
typeof arg.aws_access_key_id === "string" &&
typeof arg.aws_secret_access_key === "string" &&
["undefined", "string"].indexOf(typeof arg.aws_session_token) > -1;

interface AssumeRoleProfile extends Profile {
role_arn: string;
source_profile: string;
}

function isAssumeRoleProfile(arg: any): arg is AssumeRoleProfile {
return (
Boolean(arg) &&
typeof arg === "object" &&
typeof arg.role_arn === "string" &&
typeof arg.source_profile === "string" &&
["undefined", "string"].indexOf(typeof arg.role_session_name) > -1 &&
["undefined", "string"].indexOf(typeof arg.external_id) > -1 &&
["undefined", "string"].indexOf(typeof arg.mfa_serial) > -1
);
}
const isAssumeRoleProfile = (arg: any): arg is AssumeRoleProfile =>
Boolean(arg) &&
typeof arg === "object" &&
typeof arg.role_arn === "string" &&
typeof arg.source_profile === "string" &&
["undefined", "string"].indexOf(typeof arg.role_session_name) > -1 &&
["undefined", "string"].indexOf(typeof arg.external_id) > -1 &&
["undefined", "string"].indexOf(typeof arg.mfa_serial) > -1;

/**
* Creates a credential provider that will read from ini files and supports
* role assumption and multi-factor authentication.
*/
export function fromIni(init: FromIniInit = {}): CredentialProvider {
return () => parseKnownFiles(init).then((profiles) => resolveProfileData(getMasterProfileName(init), profiles, init));
}
export const fromIni = (init: FromIniInit = {}): CredentialProvider => async () => {
const profiles = await parseKnownFiles(init);
return resolveProfileData(getMasterProfileName(init), profiles, init);
};

export function getMasterProfileName(init: FromIniInit): string {
return init.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE;
}
/**
* 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;

async function resolveProfileData(
const parsedFiles = await loadedConfig;
return {
...parsedFiles.configFile,
...parsedFiles.credentialsFile,
};
};

/**
* @internal
*/
export const getMasterProfileName = (init: { profile?: string }): string =>
init.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE;

const resolveProfileData = async (
profileName: string,
profiles: ParsedIniData,
options: FromIniInit,
visitedProfiles: { [profileName: string]: true } = {}
): Promise<Credentials> {
): Promise<Credentials> => {
const data = profiles[profileName];

// If this is not the first profile visited, static credentials should be
Expand Down Expand Up @@ -196,24 +211,11 @@ async function resolveProfileData(
// (whether via a parameter, an environment variable, or another profile's
// `source_profile` key).
throw new ProviderError(`Profile ${profileName} could not be found or parsed in shared` + ` credentials file.`);
}

export function parseKnownFiles(init: FromIniInit): Promise<ParsedIniData> {
const { loadedConfig = loadSharedConfigFiles(init) } = init;
};

return loadedConfig.then((parsedFiles) => {
const { configFile, credentialsFile } = parsedFiles;
return {
...configFile,
...credentialsFile,
};
});
}

function resolveStaticCredentials(profile: StaticCredsProfile): Promise<Credentials> {
return Promise.resolve({
const resolveStaticCredentials = (profile: StaticCredsProfile): Promise<Credentials> =>
Promise.resolve({
accessKeyId: profile.aws_access_key_id,
secretAccessKey: profile.aws_secret_access_key,
sessionToken: profile.aws_session_token,
});
}
41 changes: 11 additions & 30 deletions packages/credential-provider-process/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getMasterProfileName, parseKnownFiles } from "@aws-sdk/credential-provider-ini";
import { getMasterProfileName, parseKnownFiles, SourceProfileInit } from "@aws-sdk/credential-provider-ini";
import { ProviderError } from "@aws-sdk/property-provider";
import { ParsedIniData, SharedConfigFiles, SharedConfigInit } from "@aws-sdk/shared-ini-file-loader";
import { ParsedIniData } from "@aws-sdk/shared-ini-file-loader";
import { CredentialProvider, Credentials } from "@aws-sdk/types";
import { exec } from "child_process";

Expand All @@ -9,36 +9,18 @@ import { exec } from "child_process";
*/
export const ENV_PROFILE = "AWS_PROFILE";

export interface FromProcessInit 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>;
}
export interface FromProcessInit extends SourceProfileInit {}

/**
* Creates a credential provider that will read from a credential_process specified
* in ini files.
*/
export function fromProcess(init: FromProcessInit = {}): CredentialProvider {
return () =>
parseKnownFiles(init).then((profiles) => resolveProcessCredentials(getMasterProfileName(init), profiles, init));
}
export const fromProcess = (init: FromProcessInit = {}): CredentialProvider => async () => {
const profiles = await parseKnownFiles(init);
return resolveProcessCredentials(getMasterProfileName(init), profiles);
};

async function resolveProcessCredentials(
profileName: string,
profiles: ParsedIniData,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options: FromProcessInit
): Promise<Credentials> {
const resolveProcessCredentials = async (profileName: string, profiles: ParsedIniData): Promise<Credentials> => {
const profile = profiles[profileName];

if (profiles[profileName]) {
Expand Down Expand Up @@ -100,10 +82,10 @@ async function resolveProcessCredentials(
// a parameter, anenvironment variable, or another profile's `source_profile` key).
throw new ProviderError(`Profile ${profileName} could not be found in shared credentials file.`);
}
}
};

function execPromise(command: string) {
return new Promise(function (resolve, reject) {
const execPromise = (command: string) =>
new Promise(function (resolve, reject) {
exec(command, (error, stdout) => {
if (error) {
reject(error);
Expand All @@ -113,4 +95,3 @@ function execPromise(command: string) {
resolve(stdout.trim());
});
});
}