feat: support custom STS endpoints
Closes #1067. This is a advanced option and is not needed for most deployments.
This commit is contained in:
parent
681892c11b
commit
76185a9109
6 changed files with 201 additions and 117 deletions
|
|
@ -467,6 +467,12 @@ with:
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### Custom STS endpoint
|
||||||
|
|
||||||
|
Use the `sts-endpoint` input to override the AWS STS endpoint URL. Most users
|
||||||
|
should not set this option and instead let the SDK derive the correct endpoint
|
||||||
|
from the specified region.
|
||||||
|
|
||||||
## OIDC Configuration Details
|
## OIDC Configuration Details
|
||||||
|
|
||||||
We recommend using
|
We recommend using
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,9 @@ inputs:
|
||||||
custom-tags:
|
custom-tags:
|
||||||
description: Additional tags to apply to the assumed role session. Must be a JSON object provided as a string.
|
description: Additional tags to apply to the assumed role session. Must be a JSON object provided as a string.
|
||||||
required: false
|
required: false
|
||||||
|
sts-endpoint:
|
||||||
|
description: Custom STS endpoint URL. Use this to point to an STS-compatible API (e.g. MinIO, LocalStack) instead of the default AWS STS endpoint for the region.
|
||||||
|
required: false
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
aws-account-id:
|
aws-account-id:
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export interface CredentialsClientProps {
|
||||||
region?: string;
|
region?: string;
|
||||||
proxyServer?: string;
|
proxyServer?: string;
|
||||||
noProxy?: string;
|
noProxy?: string;
|
||||||
|
stsEndpoint?: string;
|
||||||
roleChaining: boolean;
|
roleChaining: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ export class CredentialsClient {
|
||||||
public region?: string;
|
public region?: string;
|
||||||
private _stsClient?: STSClient;
|
private _stsClient?: STSClient;
|
||||||
private readonly requestHandler?: NodeHttpHandler;
|
private readonly requestHandler?: NodeHttpHandler;
|
||||||
|
private readonly stsEndpoint?: string;
|
||||||
private roleChaining?: boolean;
|
private roleChaining?: boolean;
|
||||||
|
|
||||||
constructor(props: CredentialsClientProps) {
|
constructor(props: CredentialsClientProps) {
|
||||||
|
|
@ -41,6 +43,9 @@ export class CredentialsClient {
|
||||||
httpAgent: handler,
|
httpAgent: handler,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (props.stsEndpoint) {
|
||||||
|
this.stsEndpoint = props.stsEndpoint;
|
||||||
|
}
|
||||||
this.roleChaining = props.roleChaining;
|
this.roleChaining = props.roleChaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,9 +54,11 @@ export class CredentialsClient {
|
||||||
const config = { customUserAgent: USER_AGENT } as {
|
const config = { customUserAgent: USER_AGENT } as {
|
||||||
customUserAgent: string;
|
customUserAgent: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
|
endpoint?: string;
|
||||||
requestHandler?: NodeHttpHandler;
|
requestHandler?: NodeHttpHandler;
|
||||||
};
|
};
|
||||||
if (this.region !== undefined) config.region = this.region;
|
if (this.region !== undefined) config.region = this.region;
|
||||||
|
if (this.stsEndpoint !== undefined) config.endpoint = this.stsEndpoint;
|
||||||
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
|
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
|
||||||
this._stsClient = new STSClient(config);
|
this._stsClient = new STSClient(config);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/index.ts
10
src/index.ts
|
|
@ -64,6 +64,7 @@ export async function run() {
|
||||||
.map((s) => s.trim());
|
.map((s) => s.trim());
|
||||||
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
|
const forceSkipOidc = getBooleanInput('force-skip-oidc', { required: false });
|
||||||
const noProxy = core.getInput('no-proxy', { required: false });
|
const noProxy = core.getInput('no-proxy', { required: false });
|
||||||
|
const stsEndpoint = core.getInput('sts-endpoint', { required: false });
|
||||||
const globalTimeout = Number.parseInt(core.getInput('action-timeout-s', { required: false })) || 0;
|
const globalTimeout = Number.parseInt(core.getInput('action-timeout-s', { required: false })) || 0;
|
||||||
|
|
||||||
let timeoutId: NodeJS.Timeout | undefined;
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
|
|
@ -128,12 +129,19 @@ export async function run() {
|
||||||
exportRegion(region, outputEnvCredentials);
|
exportRegion(region, outputEnvCredentials);
|
||||||
|
|
||||||
// Instantiate credentials client
|
// Instantiate credentials client
|
||||||
const clientProps: { region: string; proxyServer?: string; noProxy?: string; roleChaining: boolean } = {
|
const clientProps: {
|
||||||
|
region: string;
|
||||||
|
proxyServer?: string;
|
||||||
|
noProxy?: string;
|
||||||
|
stsEndpoint?: string;
|
||||||
|
roleChaining: boolean;
|
||||||
|
} = {
|
||||||
region,
|
region,
|
||||||
roleChaining,
|
roleChaining,
|
||||||
};
|
};
|
||||||
if (proxyServer) clientProps.proxyServer = proxyServer;
|
if (proxyServer) clientProps.proxyServer = proxyServer;
|
||||||
if (noProxy) clientProps.noProxy = noProxy;
|
if (noProxy) clientProps.noProxy = noProxy;
|
||||||
|
if (stsEndpoint) clientProps.stsEndpoint = stsEndpoint;
|
||||||
const credentialsClient = new CredentialsClient(clientProps);
|
const credentialsClient = new CredentialsClient(clientProps);
|
||||||
let sourceAccountId: string;
|
let sourceAccountId: string;
|
||||||
let webIdentityToken: string;
|
let webIdentityToken: string;
|
||||||
|
|
|
||||||
|
|
@ -311,9 +311,7 @@ describe('Configure AWS Credentials', {}, () => {
|
||||||
});
|
});
|
||||||
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||||
await run();
|
await run();
|
||||||
expect(core.warning).toHaveBeenCalledWith(
|
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining("'custom-tags' is set but will be ignored"));
|
||||||
expect.stringContaining("'custom-tags' is set but will be ignored"),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -801,6 +799,54 @@ describe('Configure AWS Credentials', {}, () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Custom STS Endpoint', {}, () => {
|
||||||
|
it('passes sts-endpoint to the STS client', async () => {
|
||||||
|
const client = new CredentialsClient({
|
||||||
|
region: 'us-east-1',
|
||||||
|
stsEndpoint: 'https://sts.custom.example.com',
|
||||||
|
roleChaining: false,
|
||||||
|
});
|
||||||
|
const endpoint = await client.stsClient.config.endpoint();
|
||||||
|
expect(endpoint).toMatchObject({ hostname: 'sts.custom.example.com', protocol: 'https:' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not override endpoint when sts-endpoint is not provided', () => {
|
||||||
|
const client = new CredentialsClient({
|
||||||
|
region: 'us-east-1',
|
||||||
|
roleChaining: false,
|
||||||
|
});
|
||||||
|
expect(client.stsClient.config.endpoint).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with http endpoints for local services', async () => {
|
||||||
|
const client = new CredentialsClient({
|
||||||
|
region: 'us-east-1',
|
||||||
|
stsEndpoint: 'http://localhost:9000',
|
||||||
|
roleChaining: false,
|
||||||
|
});
|
||||||
|
const endpoint = await client.stsClient.config.endpoint();
|
||||||
|
expect(endpoint).toMatchObject({ hostname: 'localhost', protocol: 'http:', port: 9000 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('succeeds in a full action run with sts-endpoint input', async () => {
|
||||||
|
vi.mocked(core.getInput).mockImplementation(
|
||||||
|
mocks.getInput({
|
||||||
|
...mocks.GH_OIDC_INPUTS,
|
||||||
|
'sts-endpoint': 'https://sts.custom.example.com',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
vi.mocked(core.getIDToken).mockResolvedValue('testoidctoken');
|
||||||
|
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
|
||||||
|
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
|
||||||
|
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
|
expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('HTTP Proxy Configuration', {}, () => {
|
describe('HTTP Proxy Configuration', {}, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,10 @@ describe('Profile Manager', {}, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('round-trips through parseIni', {}, () => {
|
it('round-trips through parseIni', {}, () => {
|
||||||
const data = { dev: { aws_access_key_id: 'AKIA', aws_secret_access_key: 'secret' }, 'profile prod': { region: 'us-west-2' } };
|
const data = {
|
||||||
|
dev: { aws_access_key_id: 'AKIA', aws_secret_access_key: 'secret' },
|
||||||
|
'profile prod': { region: 'us-west-2' },
|
||||||
|
};
|
||||||
const roundTripped = parseIni(stringifyIni(data));
|
const roundTripped = parseIni(stringifyIni(data));
|
||||||
expect(roundTripped).toEqual(data);
|
expect(roundTripped).toEqual(data);
|
||||||
});
|
});
|
||||||
|
|
@ -196,10 +199,15 @@ describe('Profile Manager', {}, () => {
|
||||||
const filePath = '/home/runner/.aws/credentials';
|
const filePath = '/home/runner/.aws/credentials';
|
||||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||||
|
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
filePath,
|
||||||
aws_secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
'dev',
|
||||||
}, false);
|
{
|
||||||
|
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
||||||
|
aws_secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -214,16 +222,26 @@ describe('Profile Manager', {}, () => {
|
||||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||||
|
|
||||||
// Create initial profile
|
// Create initial profile
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
filePath,
|
||||||
aws_secret_access_key: 'devSecretKey',
|
'dev',
|
||||||
}, false);
|
{
|
||||||
|
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
|
||||||
|
aws_secret_access_key: 'devSecretKey',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// Add second profile
|
// Add second profile
|
||||||
mergeProfileSection(filePath, 'prod', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'AKIAPRODEXAMPLE',
|
filePath,
|
||||||
aws_secret_access_key: 'prodSecretKey',
|
'prod',
|
||||||
}, false);
|
{
|
||||||
|
aws_access_key_id: 'AKIAPRODEXAMPLE',
|
||||||
|
aws_secret_access_key: 'prodSecretKey',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -239,18 +257,28 @@ describe('Profile Manager', {}, () => {
|
||||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||||
|
|
||||||
// Create initial profile
|
// Create initial profile
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'OLD_KEY',
|
filePath,
|
||||||
aws_secret_access_key: 'oldSecretKey',
|
'dev',
|
||||||
aws_session_token: 'oldSessionToken'
|
{
|
||||||
}, false);
|
aws_access_key_id: 'OLD_KEY',
|
||||||
|
aws_secret_access_key: 'oldSecretKey',
|
||||||
|
aws_session_token: 'oldSessionToken',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// Overwrite with new credentials
|
// Overwrite with new credentials
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'NEW_KEY',
|
filePath,
|
||||||
aws_secret_access_key: 'newSecretKey',
|
'dev',
|
||||||
aws_session_token: 'newSessionToken',
|
{
|
||||||
}, true);
|
aws_access_key_id: 'NEW_KEY',
|
||||||
|
aws_secret_access_key: 'newSecretKey',
|
||||||
|
aws_session_token: 'newSessionToken',
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -265,17 +293,27 @@ describe('Profile Manager', {}, () => {
|
||||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||||
|
|
||||||
// Create profile with session token
|
// Create profile with session token
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'AKIA',
|
filePath,
|
||||||
aws_secret_access_key: 'secret',
|
'dev',
|
||||||
aws_session_token: 'old-token',
|
{
|
||||||
}, false);
|
aws_access_key_id: 'AKIA',
|
||||||
|
aws_secret_access_key: 'secret',
|
||||||
|
aws_session_token: 'old-token',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// Overwrite without session token
|
// Overwrite without session token
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'AKIA2',
|
filePath,
|
||||||
aws_secret_access_key: 'secret2',
|
'dev',
|
||||||
}, true);
|
{
|
||||||
|
aws_access_key_id: 'AKIA2',
|
||||||
|
aws_secret_access_key: 'secret2',
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -290,10 +328,15 @@ describe('Profile Manager', {}, () => {
|
||||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||||
fs.writeFileSync(filePath, '', { mode: 0o600 });
|
fs.writeFileSync(filePath, '', { mode: 0o600 });
|
||||||
|
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'AKIA',
|
filePath,
|
||||||
aws_secret_access_key: 'secret',
|
'dev',
|
||||||
}, false);
|
{
|
||||||
|
aws_access_key_id: 'AKIA',
|
||||||
|
aws_secret_access_key: 'secret',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -307,17 +350,31 @@ describe('Profile Manager', {}, () => {
|
||||||
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
fs.mkdirSync('/home/runner/.aws', { recursive: true });
|
||||||
|
|
||||||
// Create initial profile
|
// Create initial profile
|
||||||
mergeProfileSection(filePath, 'dev', {
|
mergeProfileSection(
|
||||||
aws_access_key_id: 'OLD_KEY',
|
filePath,
|
||||||
aws_secret_access_key: 'oldSecretKey',
|
'dev',
|
||||||
}, false);
|
{
|
||||||
|
aws_access_key_id: 'OLD_KEY',
|
||||||
|
aws_secret_access_key: 'oldSecretKey',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// Overwrite with new credentials
|
// Overwrite with new credentials
|
||||||
expect(() => mergeProfileSection(filePath, 'dev', {
|
expect(() =>
|
||||||
aws_access_key_id: 'NEW_KEY',
|
mergeProfileSection(
|
||||||
aws_secret_access_key: 'newSecretKey',
|
filePath,
|
||||||
aws_session_token: 'sessionToken',
|
'dev',
|
||||||
}, false)).toThrow(`Profile with name "dev" already exists. Please use the overwrite-aws-profile input if you want to overwrite existing profiles.`);
|
{
|
||||||
|
aws_access_key_id: 'NEW_KEY',
|
||||||
|
aws_secret_access_key: 'newSecretKey',
|
||||||
|
aws_session_token: 'sessionToken',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
).toThrow(
|
||||||
|
`Profile with name "dev" already exists. Please use the overwrite-aws-profile input if you want to overwrite existing profiles.`,
|
||||||
|
);
|
||||||
|
|
||||||
const content = fs.readFileSync(filePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -468,7 +525,7 @@ describe('Profile Manager', {}, () => {
|
||||||
SecretAccessKey: 'secret',
|
SecretAccessKey: 'secret',
|
||||||
},
|
},
|
||||||
'us-east-1',
|
'us-east-1',
|
||||||
false
|
false,
|
||||||
),
|
),
|
||||||
).toThrow('whitespace');
|
).toThrow('whitespace');
|
||||||
});
|
});
|
||||||
|
|
@ -486,7 +543,7 @@ describe('Profile Manager', {}, () => {
|
||||||
SecretAccessKey: 'secret',
|
SecretAccessKey: 'secret',
|
||||||
},
|
},
|
||||||
'us-east-1',
|
'us-east-1',
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(fs.existsSync('/custom/credentials')).toBe(true);
|
expect(fs.existsSync('/custom/credentials')).toBe(true);
|
||||||
|
|
@ -501,7 +558,7 @@ describe('Profile Manager', {}, () => {
|
||||||
SecretAccessKey: 'secret',
|
SecretAccessKey: 'secret',
|
||||||
},
|
},
|
||||||
'us-east-1',
|
'us-east-1',
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(core.info).toHaveBeenCalledWith('Writing credentials to profile: dev');
|
expect(core.info).toHaveBeenCalledWith('Writing credentials to profile: dev');
|
||||||
|
|
@ -517,12 +574,7 @@ describe('Profile Manager', {}, () => {
|
||||||
'[personal]\naws_access_key_id=AKIAPERSONAL\naws_secret_access_key=personalSecret\naws_session_token=personalToken\n',
|
'[personal]\naws_access_key_id=AKIAPERSONAL\naws_secret_access_key=personalSecret\naws_session_token=personalToken\n',
|
||||||
);
|
);
|
||||||
|
|
||||||
writeProfileFiles(
|
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-east-1', false);
|
||||||
'dev',
|
|
||||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
|
||||||
'us-east-1',
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = fs.readFileSync(credsPath, 'utf-8');
|
const content = fs.readFileSync(credsPath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -540,17 +592,9 @@ describe('Profile Manager', {}, () => {
|
||||||
it('preserves pre-existing config with extra keys', {}, () => {
|
it('preserves pre-existing config with extra keys', {}, () => {
|
||||||
const configPath = getProfileFilePaths().config;
|
const configPath = getProfileFilePaths().config;
|
||||||
fs.mkdirSync(require('node:path').dirname(configPath), { recursive: true });
|
fs.mkdirSync(require('node:path').dirname(configPath), { recursive: true });
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(configPath, '[profile personal]\nregion=eu-west-1\noutput=json\ncli_pager=\n');
|
||||||
configPath,
|
|
||||||
'[profile personal]\nregion=eu-west-1\noutput=json\ncli_pager=\n',
|
|
||||||
);
|
|
||||||
|
|
||||||
writeProfileFiles(
|
writeProfileFiles('dev', { AccessKeyId: 'AKIA', SecretAccessKey: 'secret' }, 'us-east-1', false);
|
||||||
'dev',
|
|
||||||
{ AccessKeyId: 'AKIA', SecretAccessKey: 'secret' },
|
|
||||||
'us-east-1',
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = fs.readFileSync(configPath, 'utf-8');
|
const content = fs.readFileSync(configPath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -566,17 +610,9 @@ describe('Profile Manager', {}, () => {
|
||||||
it('preserves pre-existing default profile when writing a named profile', {}, () => {
|
it('preserves pre-existing default profile when writing a named profile', {}, () => {
|
||||||
const credsPath = getProfileFilePaths().credentials;
|
const credsPath = getProfileFilePaths().credentials;
|
||||||
fs.mkdirSync(require('node:path').dirname(credsPath), { recursive: true });
|
fs.mkdirSync(require('node:path').dirname(credsPath), { recursive: true });
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(credsPath, '[default]\naws_access_key_id=AKIADEFAULT\naws_secret_access_key=defaultSecret\n');
|
||||||
credsPath,
|
|
||||||
'[default]\naws_access_key_id=AKIADEFAULT\naws_secret_access_key=defaultSecret\n',
|
|
||||||
);
|
|
||||||
|
|
||||||
writeProfileFiles(
|
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-west-2', false);
|
||||||
'dev',
|
|
||||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
|
||||||
'us-west-2',
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = fs.readFileSync(credsPath, 'utf-8');
|
const content = fs.readFileSync(credsPath, 'utf-8');
|
||||||
const parsed = parseIni(content);
|
const parsed = parseIni(content);
|
||||||
|
|
@ -596,12 +632,7 @@ describe('Profile Manager', {}, () => {
|
||||||
'# My important comment\n[personal]\naws_access_key_id=AKIA\naws_secret_access_key=secret\n',
|
'# My important comment\n[personal]\naws_access_key_id=AKIA\naws_secret_access_key=secret\n',
|
||||||
);
|
);
|
||||||
|
|
||||||
writeProfileFiles(
|
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-east-1', false);
|
||||||
'dev',
|
|
||||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
|
||||||
'us-east-1',
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const content = fs.readFileSync(credsPath, 'utf-8') as string;
|
const content = fs.readFileSync(credsPath, 'utf-8') as string;
|
||||||
|
|
||||||
|
|
@ -635,12 +666,7 @@ describe('Profile Manager', {}, () => {
|
||||||
|
|
||||||
fs.mkdirSync('/custom-creds', { recursive: true });
|
fs.mkdirSync('/custom-creds', { recursive: true });
|
||||||
|
|
||||||
writeProfileFiles(
|
writeProfileFiles('dev', { AccessKeyId: 'AKIA', SecretAccessKey: 'secret' }, 'us-east-1', false);
|
||||||
'dev',
|
|
||||||
{ AccessKeyId: 'AKIA', SecretAccessKey: 'secret' },
|
|
||||||
'us-east-1',
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(fs.existsSync('/custom-creds/credentials')).toBe(true);
|
expect(fs.existsSync('/custom-creds/credentials')).toBe(true);
|
||||||
// Config file should be at the default path (under homedir)
|
// Config file should be at the default path (under homedir)
|
||||||
|
|
@ -657,7 +683,7 @@ describe('Profile Manager', {}, () => {
|
||||||
SessionToken: 'FwoGZXIvYXdzEBYaDEXAMPLE',
|
SessionToken: 'FwoGZXIvYXdzEBYaDEXAMPLE',
|
||||||
},
|
},
|
||||||
'us-east-1',
|
'us-east-1',
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
const credsPath = getProfileFilePaths().credentials;
|
const credsPath = getProfileFilePaths().credentials;
|
||||||
|
|
@ -672,28 +698,20 @@ describe('Profile Manager', {}, () => {
|
||||||
// - LF line endings, trailing newline
|
// - LF line endings, trailing newline
|
||||||
expect(credContent).toBe(
|
expect(credContent).toBe(
|
||||||
'[dev]\n' +
|
'[dev]\n' +
|
||||||
'aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n' +
|
'aws_access_key_id = AKIAIOSFODNN7EXAMPLE\n' +
|
||||||
'aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n' +
|
'aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\n' +
|
||||||
'aws_session_token = FwoGZXIvYXdzEBYaDEXAMPLE\n',
|
'aws_session_token = FwoGZXIvYXdzEBYaDEXAMPLE\n',
|
||||||
);
|
|
||||||
expect(configContent).toBe(
|
|
||||||
'[profile dev]\n' +
|
|
||||||
'region = us-east-1\n',
|
|
||||||
);
|
);
|
||||||
|
expect(configContent).toBe('[profile dev]\n' + 'region = us-east-1\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('golden file for multi-profile output', {}, () => {
|
it('golden file for multi-profile output', {}, () => {
|
||||||
writeProfileFiles(
|
writeProfileFiles('dev', { AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' }, 'us-east-1', false);
|
||||||
'dev',
|
|
||||||
{ AccessKeyId: 'AKIADEV', SecretAccessKey: 'devSecret' },
|
|
||||||
'us-east-1',
|
|
||||||
false
|
|
||||||
);
|
|
||||||
writeProfileFiles(
|
writeProfileFiles(
|
||||||
'prod',
|
'prod',
|
||||||
{ AccessKeyId: 'AKIAPROD', SecretAccessKey: 'prodSecret', SessionToken: 'prodToken' },
|
{ AccessKeyId: 'AKIAPROD', SecretAccessKey: 'prodSecret', SessionToken: 'prodToken' },
|
||||||
'us-west-2',
|
'us-west-2',
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
const credsPath = getProfileFilePaths().credentials;
|
const credsPath = getProfileFilePaths().credentials;
|
||||||
|
|
@ -704,20 +722,16 @@ describe('Profile Manager', {}, () => {
|
||||||
|
|
||||||
expect(credContent).toBe(
|
expect(credContent).toBe(
|
||||||
'[dev]\n' +
|
'[dev]\n' +
|
||||||
'aws_access_key_id = AKIADEV\n' +
|
'aws_access_key_id = AKIADEV\n' +
|
||||||
'aws_secret_access_key = devSecret\n' +
|
'aws_secret_access_key = devSecret\n' +
|
||||||
'\n' +
|
'\n' +
|
||||||
'[prod]\n' +
|
'[prod]\n' +
|
||||||
'aws_access_key_id = AKIAPROD\n' +
|
'aws_access_key_id = AKIAPROD\n' +
|
||||||
'aws_secret_access_key = prodSecret\n' +
|
'aws_secret_access_key = prodSecret\n' +
|
||||||
'aws_session_token = prodToken\n',
|
'aws_session_token = prodToken\n',
|
||||||
);
|
);
|
||||||
expect(configContent).toBe(
|
expect(configContent).toBe(
|
||||||
'[profile dev]\n' +
|
'[profile dev]\n' + 'region = us-east-1\n' + '\n' + '[profile prod]\n' + 'region = us-west-2\n',
|
||||||
'region = us-east-1\n' +
|
|
||||||
'\n' +
|
|
||||||
'[profile prod]\n' +
|
|
||||||
'region = us-west-2\n',
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue