mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-01 09:17:08 +02:00
753 lines
22 KiB
TypeScript
753 lines
22 KiB
TypeScript
import { ApplicationError } from 'n8n-workflow';
|
|
import type { AwsAssumeRoleCredentialsType, AWSRegion } from './types';
|
|
|
|
global.fetch = jest.fn();
|
|
|
|
jest.mock('aws4', () => ({
|
|
sign: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('xml2js', () => ({
|
|
parseString: jest.fn(),
|
|
}));
|
|
|
|
import { sign } from 'aws4';
|
|
import { parseString } from 'xml2js';
|
|
import { assumeRole } from './utils';
|
|
import * as systemCredentialsUtils from './system-credentials-utils';
|
|
|
|
describe('assumeRole', () => {
|
|
let mockFetch: jest.MockedFunction<typeof fetch>;
|
|
let mockSign: jest.MockedFunction<typeof sign>;
|
|
let mockParseString: jest.MockedFunction<typeof parseString>;
|
|
let consoleErrorSpy: jest.SpyInstance;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
mockFetch = global.fetch as jest.MockedFunction<typeof fetch>;
|
|
mockSign = sign as jest.MockedFunction<typeof sign>;
|
|
mockParseString = parseString as jest.MockedFunction<typeof parseString>;
|
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
|
|
mockSign.mockImplementation((request: any) => request as any);
|
|
});
|
|
|
|
afterEach(() => {
|
|
consoleErrorSpy.mockRestore();
|
|
});
|
|
|
|
describe('with system credentials', () => {
|
|
it('should successfully assume role using system credentials by environment', async () => {
|
|
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: 'environment' 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 successfully assume role using system credentials by instanceMetadata', async () => {
|
|
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',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: true,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
};
|
|
|
|
jest.spyOn(systemCredentialsUtils, 'getSystemCredentials').mockResolvedValue(null);
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'System AWS credentials are required for role assumption',
|
|
);
|
|
});
|
|
|
|
it('should include external ID when provided', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: true,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
roleSessionName: 'test-session',
|
|
externalId: 'external-123',
|
|
};
|
|
|
|
const mockSystemCredentials = {
|
|
accessKeyId: 'system-access-key',
|
|
secretAccessKey: 'system-secret-key',
|
|
source: 'environment' as const,
|
|
};
|
|
|
|
jest
|
|
.spyOn(systemCredentialsUtils, 'getSystemCredentials')
|
|
.mockResolvedValue(mockSystemCredentials);
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'us-east-1');
|
|
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
'https://sts.us-east-1.amazonaws.com',
|
|
expect.objectContaining({
|
|
body: expect.stringContaining('ExternalId=external-123'),
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('with manual STS credentials', () => {
|
|
it('should successfully assume role using manual STS credentials', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
roleSessionName: 'test-session',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
stsSessionToken: 'sts-session-token',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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(mockSign).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
path: '/',
|
|
region: 'us-east-1',
|
|
}),
|
|
{
|
|
accessKeyId: 'sts-access-key',
|
|
secretAccessKey: 'sts-secret-key',
|
|
sessionToken: 'sts-session-token',
|
|
},
|
|
);
|
|
});
|
|
|
|
it('should work without STS session token', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'us-east-1');
|
|
|
|
expect(mockSign).toHaveBeenCalledWith(expect.anything(), {
|
|
accessKeyId: 'sts-access-key',
|
|
secretAccessKey: 'sts-secret-key',
|
|
sessionToken: undefined,
|
|
});
|
|
});
|
|
|
|
it('should throw error when STS access key ID is missing', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'STS Access Key ID is required when not using system credentials',
|
|
);
|
|
});
|
|
|
|
it('should throw error when STS access key ID is empty', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: ' ',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'STS Access Key ID is required when not using system credentials',
|
|
);
|
|
});
|
|
|
|
it('should throw error when STS secret access key is missing', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
};
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'STS Secret Access Key is required when not using system credentials',
|
|
);
|
|
});
|
|
|
|
it('should throw error when STS secret access key is empty', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: ' ',
|
|
};
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'STS Secret Access Key is required when not using system credentials',
|
|
);
|
|
});
|
|
|
|
it('should trim whitespace from STS credentials', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: ' sts-access-key ',
|
|
stsSecretAccessKey: ' sts-secret-key ',
|
|
stsSessionToken: ' sts-session-token ',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'us-east-1');
|
|
|
|
expect(mockSign).toHaveBeenCalledWith(expect.anything(), {
|
|
accessKeyId: 'sts-access-key',
|
|
secretAccessKey: 'sts-secret-key',
|
|
sessionToken: 'sts-session-token',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('region handling', () => {
|
|
it('should use correct endpoint for China regions', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'cn-north-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws-cn:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'cn-north-1' as AWSRegion);
|
|
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
'https://sts.cn-north-1.amazonaws.com.cn',
|
|
expect.any(Object),
|
|
);
|
|
});
|
|
|
|
it('should use correct endpoint for standard regions', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'eu-west-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'eu-west-1');
|
|
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
'https://sts.eu-west-1.amazonaws.com',
|
|
expect.any(Object),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('error handling', () => {
|
|
it('should throw error when signing fails', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
mockSign.mockImplementation(() => {
|
|
throw new Error('Signing failed');
|
|
});
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'Failed to sign STS request',
|
|
);
|
|
});
|
|
|
|
it('should throw error when STS request fails', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: false,
|
|
status: 403,
|
|
statusText: 'Forbidden',
|
|
text: jest.fn().mockResolvedValue('Access denied'),
|
|
};
|
|
|
|
mockFetch.mockResolvedValue(mockResponse as any);
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'STS AssumeRole failed: 403 Forbidden - Access denied',
|
|
);
|
|
});
|
|
|
|
it('should throw error when XML parsing fails', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('invalid xml'),
|
|
};
|
|
|
|
mockFetch.mockResolvedValue(mockResponse as any);
|
|
|
|
mockParseString.mockImplementation((_xml, _options, callback) => {
|
|
callback(new Error('XML parsing failed'), null);
|
|
});
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow('XML parsing failed');
|
|
});
|
|
|
|
it('should throw error when response has no credentials', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
mockFetch.mockResolvedValue(mockResponse as any);
|
|
|
|
mockParseString.mockImplementation((_xml, _options, callback) => {
|
|
callback(null, {
|
|
AssumeRoleResponse: {
|
|
AssumeRoleResult: {},
|
|
},
|
|
});
|
|
});
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'Invalid response from STS AssumeRole',
|
|
);
|
|
});
|
|
|
|
it('should throw error when response structure is invalid', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
mockFetch.mockResolvedValue(mockResponse as any);
|
|
|
|
mockParseString.mockImplementation((_xml, _options, callback) => {
|
|
callback(null, {
|
|
InvalidResponse: {},
|
|
});
|
|
});
|
|
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(ApplicationError);
|
|
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
|
'Invalid response from STS AssumeRole',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('default values', () => {
|
|
it('should use default role session name when not provided', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
useSystemCredentialsForRole: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'us-east-1');
|
|
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
'https://sts.us-east-1.amazonaws.com',
|
|
expect.objectContaining({
|
|
body: expect.stringContaining('RoleSessionName=n8n-session'),
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('should default useSystemCredentialsForRole to false when not provided', async () => {
|
|
const credentials: AwsAssumeRoleCredentialsType = {
|
|
region: 'us-east-1',
|
|
customEndpoints: false,
|
|
roleArn: 'arn:aws:iam::123456789012:role/TestRole',
|
|
stsAccessKeyId: 'sts-access-key',
|
|
stsSecretAccessKey: 'sts-secret-key',
|
|
};
|
|
|
|
const mockResponse = {
|
|
ok: true,
|
|
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
|
};
|
|
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
await assumeRole(credentials, 'us-east-1');
|
|
|
|
expect(mockSign).toHaveBeenCalledWith(expect.anything(), {
|
|
accessKeyId: 'sts-access-key',
|
|
secretAccessKey: 'sts-secret-key',
|
|
sessionToken: undefined,
|
|
});
|
|
});
|
|
});
|
|
});
|