Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { Container } from '@n8n/di';
import { ApplicationError } from 'n8n-workflow';

type Resolvers = 'environment' | 'podIdentity' | 'containerMetadata' | 'instanceMetadata';
type RetrunData = {
type ReturnData = {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While stepping through understanding, noticed this misspelling. This type is not exported and only used below.

accessKeyId: string;
secretAccessKey: string;
sessionToken?: string;
};

export const envGetter = (key: string): string | undefined => process.env[key];

export const credentialsResolver: Record<Resolvers, () => Promise<RetrunData | null>> = {
export const credentialsResolver: Record<Resolvers, () => Promise<ReturnData | null>> = {
environment: getEnvironmentCredentials,
instanceMetadata: getInstanceMetadataCredentials,
containerMetadata: getContainerMetadataCredentials,
Expand Down
78 changes: 77 additions & 1 deletion packages/nodes-base/credentials/common/aws/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('assumeRole', () => {
});

describe('with system credentials', () => {
it('should successfully assume role using system credentials', async () => {
it('should successfully assume role using system credentials by environment', async () => {
const credentials: AwsAssumeRoleCredentialsType = {
region: 'us-east-1',
customEndpoints: false,
Expand Down Expand Up @@ -113,6 +113,82 @@ describe('assumeRole', () => {
);
});

it('should successfully assume role using system credentials by instanceMetadata', async () => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately, this could (should?) iterate through each source type and ensure it behaves as intended.

const credentials: AwsAssumeRoleCredentialsType = {
region: 'us-east-1',
customEndpoints: false,
useSystemCredentialsForRole: true,
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
roleSessionName: 'test-session',
};

const mockSystemCredentials = {
accessKeyId: 'system-access-key',
secretAccessKey: 'system-secret-key',
sessionToken: 'system-session-token',
source: 'instanceMetadata' as const,
};

jest
.spyOn(systemCredentialsUtils, 'getSystemCredentials')
.mockResolvedValue(mockSystemCredentials);

const mockResponse = {
ok: true,
text: jest.fn().mockResolvedValue(`<?xml version="1.0" encoding="UTF-8"?>
<AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<AssumeRoleResult>
<Credentials>
<AccessKeyId>assumed-access-key</AccessKeyId>
<SecretAccessKey>assumed-secret-key</SecretAccessKey>
<SessionToken>assumed-session-token</SessionToken>
</Credentials>
</AssumeRoleResult>
</AssumeRoleResponse>`),
};

mockFetch.mockResolvedValue(mockResponse as any);

mockParseString.mockImplementation((_xml, _options, callback) => {
callback(null, {
AssumeRoleResponse: {
AssumeRoleResult: {
Credentials: {
AccessKeyId: 'assumed-access-key',
SecretAccessKey: 'assumed-secret-key',
SessionToken: 'assumed-session-token',
},
},
},
});
});

const result = await assumeRole(credentials, 'us-east-1');

expect(result).toEqual({
accessKeyId: 'assumed-access-key',
secretAccessKey: 'assumed-secret-key',
sessionToken: 'assumed-session-token',
});

expect(systemCredentialsUtils.getSystemCredentials).toHaveBeenCalled();
expect(mockSign).toHaveBeenCalledWith(
expect.objectContaining({
method: 'POST',
path: '/',
region: 'us-east-1',
}),
mockSystemCredentials,
);
expect(mockFetch).toHaveBeenCalledWith(
'https://sts.us-east-1.amazonaws.com',
expect.objectContaining({
method: 'POST',
body: expect.stringContaining('Action=AssumeRole'),
}),
);
});

it('should throw error when system credentials are not available', async () => {
const credentials: AwsAssumeRoleCredentialsType = {
region: 'us-east-1',
Expand Down
8 changes: 1 addition & 7 deletions packages/nodes-base/credentials/common/aws/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,7 @@ export async function assumeRole(
'System AWS credentials are required for role assumption. Please ensure AWS credentials are available via environment variables, instance metadata, or container role.',
);
}
if (systemCredentials.source !== 'environment') {
return {
accessKeyId: systemCredentials.accessKeyId,
secretAccessKey: systemCredentials.secretAccessKey,
sessionToken: systemCredentials.sessionToken as string,
};
}

stsCallCredentials = systemCredentials;
} else {
if (!credentials.stsAccessKeyId || credentials.stsAccessKeyId.trim() === '') {
Expand Down