first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,45 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Create Group', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'CreateGroup',
Version: CURRENT_VERSION,
GroupName: 'NewGroupTest',
})
.reply(200, {
CreateGroupResponse: {
CreateGroupResult: {
Group: {
GroupName: 'NewGroupTest',
Arn: 'arn:aws:iam::130450532146:group/NewGroupTest',
GroupId: 'AGPAR4X3VE4ZI7H42C2XW',
Path: '/',
CreateDate: 1743409792,
},
},
ResponseMetadata: {
RequestId: '50bf4fdb-34b9-4c99-8da8-358d50783c8d',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['create.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,78 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -120],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "group",
"operation": "create",
"groupName": "NewGroupTest",
"additionalFields": {},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [160, -120],
"id": "23fd4c79-516d-49dc-81c8-e68052a294d1",
"name": "createGroup",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"createGroup": [
{
"json": {
"CreateGroupResponse": {
"CreateGroupResult": {
"Group": {
"Arn": "arn:aws:iam::130450532146:group/NewGroupTest",
"CreateDate": 1743409792,
"GroupId": "AGPAR4X3VE4ZI7H42C2XW",
"GroupName": "NewGroupTest",
"Path": "/"
}
},
"ResponseMetadata": {
"RequestId": "50bf4fdb-34b9-4c99-8da8-358d50783c8d"
}
}
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "createGroup",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "da88fb89-6234-4eb3-b6fb-31a7a299c7ec",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,61 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Delete Group', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'GetGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupForTest1',
})
.reply(200, {
GetGroupResponse: {
GetGroupResult: {
Users: [{ UserName: 'User1' }],
},
},
});
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'RemoveUserFromGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupForTest1',
UserName: 'User1',
})
.reply(200, {});
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'DeleteGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupForTest1',
})
.reply(200, {
DeleteGroupResponse: {
ResponseMetadata: {
RequestId: 'b9cc2642-db2c-4935-aaaf-eacf10e4f00a',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['delete.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,62 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [200, 120],
"id": "b4205abf-7102-4e53-8aed-7bd047acfaf4",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "group",
"operation": "delete",
"group": {
"__rl": true,
"value": "GroupForTest1",
"mode": "list",
"cachedResultName": "GroupForTest1"
},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [380, 120],
"id": "bba99f6d-ed9c-4603-95f0-4ecce6e6f976",
"name": "AWS IAM13",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "AWS IAM13",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"AWS IAM13": [
{
"json": {
"DeleteGroupResponse": {
"ResponseMetadata": {
"RequestId": "b9cc2642-db2c-4935-aaaf-eacf10e4f00a"
}
}
}
}
]
}
}
@@ -0,0 +1,54 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Get Group', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'GetGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupNameUpdated2',
})
.reply(200, {
GetGroupResponse: {
GetGroupResult: {
Group: {
Arn: 'arn:aws:iam::130450532146:group/New/Path/GroupNameUpdated2',
CreateDate: 1739193696,
GroupId: 'AGPAR4X3VE4ZKHNKBQHBZ',
GroupName: 'GroupNameUpdated2',
Path: '/New/Path/',
},
Users: [
{
Arn: 'arn:aws:iam::130450532146:user/rhis/path/Jonas',
CreateDate: 1739198295,
PasswordLastUsed: null,
PermissionsBoundary: null,
Tags: null,
Path: '/rhis/path/',
UserId: 'AIDAR4X3VE4ZDJJFKI6OU',
UserName: 'Jonas',
},
],
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['get.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,86 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-80, -100],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "group",
"operation": "get",
"group": {
"__rl": true,
"value": "GroupNameUpdated2",
"mode": "list",
"cachedResultName": "GroupNameUpdated2"
},
"includeUsers": true,
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [160, -100],
"id": "7990c8f3-738e-4150-855e-a9c0a965b75d",
"name": "getGroup",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"getGroup": [
{
"json": {
"Arn": "arn:aws:iam::130450532146:group/New/Path/GroupNameUpdated2",
"CreateDate": 1739193696,
"GroupId": "AGPAR4X3VE4ZKHNKBQHBZ",
"GroupName": "GroupNameUpdated2",
"Path": "/New/Path/",
"Users": [
{
"Arn": "arn:aws:iam::130450532146:user/rhis/path/Jonas",
"CreateDate": 1739198295,
"PasswordLastUsed": null,
"Path": "/rhis/path/",
"PermissionsBoundary": null,
"Tags": null,
"UserId": "AIDAR4X3VE4ZDJJFKI6OU",
"UserName": "Jonas"
}
]
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "getGroup",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "47e9f15a-50b7-4f12-ac5b-c7b24bb9a400",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,58 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Get All Groups', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'ListGroups',
Version: CURRENT_VERSION,
MaxItems: 100,
})
.reply(200, {
ListGroupsResponse: {
ListGroupsResult: {
Groups: [
{
Arn: 'arn:aws:iam::130450532146:group/test/Admin7',
CreateDate: 1733436631,
GroupId: 'AGPAR4X3VE4ZAFFY5EDUJ',
GroupName: 'Admin7',
Path: '/test/',
},
{
Arn: 'arn:aws:iam::130450532146:group/cognito',
CreateDate: 1730804196,
GroupId: 'AGPAR4X3VE4ZMVEFLBSRB',
GroupName: 'cognito',
Path: '/',
},
{
Arn: 'arn:aws:iam::130450532146:group/GroupCreatedAfter',
CreateDate: 1741589366,
GroupId: 'AGPAR4X3VE4ZF5VE6UF2U',
GroupName: 'GroupCreatedAfter',
Path: '/',
},
],
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['getAll.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,85 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -220],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "group",
"includeUsers": false,
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [160, -220],
"id": "23fd4c79-516d-49dc-81c8-e68052a294d1",
"name": "getAllGroups",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"getAllGroups": [
{
"json": {
"Arn": "arn:aws:iam::130450532146:group/test/Admin7",
"CreateDate": 1733436631,
"GroupId": "AGPAR4X3VE4ZAFFY5EDUJ",
"GroupName": "Admin7",
"Path": "/test/"
}
},
{
"json": {
"Arn": "arn:aws:iam::130450532146:group/cognito",
"CreateDate": 1730804196,
"GroupId": "AGPAR4X3VE4ZMVEFLBSRB",
"GroupName": "cognito",
"Path": "/"
}
},
{
"json": {
"Arn": "arn:aws:iam::130450532146:group/GroupCreatedAfter",
"CreateDate": 1741589366,
"GroupId": "AGPAR4X3VE4ZF5VE6UF2U",
"GroupName": "GroupCreatedAfter",
"Path": "/"
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "getAllGroups",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "51beb4c0-4163-4c57-a715-6eb45f1ffb9b",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,36 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Update Group', () => {
beforeEach(() => {
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'UpdateGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupNameUpdated',
NewGroupName: 'GroupNameUpdated2',
})
.reply(200, {
UpdateGroupResponse: {
ResponseMetadata: {
RequestId: '16ada465-a981-44ab-841f-3ca3247f7405',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['update.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,74 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -120],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"resource": "group",
"operation": "update",
"group": {
"__rl": true,
"value": "GroupNameUpdated",
"mode": "list",
"cachedResultName": "GroupNameUpdated"
},
"groupName": "GroupNameUpdated2",
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [140, -120],
"id": "6299cf80-7aea-4cf6-8604-368a42290da7",
"name": "updateGroup",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"updateGroup": [
{
"json": {
"UpdateGroupResponse": {
"ResponseMetadata": {
"RequestId": "16ada465-a981-44ab-841f-3ca3247f7405"
}
}
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "updateGroup",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "bce6ece0-c45a-4389-9e39-6edea3d2e0da",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,130 @@
import { NodeApiError } from 'n8n-workflow';
import type { INodeExecutionData, IN8nHttpFullResponse, JsonObject } from 'n8n-workflow';
import { ERROR_DESCRIPTIONS } from '../../helpers/constants';
import { handleError } from '../../helpers/errorHandler';
const mockExecuteSingleFunctions = {
getNode: jest.fn(() => ({ name: 'MockNode' })),
getNodeParameter: jest.fn(),
} as any;
describe('handleError', () => {
let response: IN8nHttpFullResponse;
let data: INodeExecutionData[];
beforeEach(() => {
data = [{}] as INodeExecutionData[];
response = { statusCode: 200, body: {} } as IN8nHttpFullResponse;
});
test('should return data when no error occurs', async () => {
const result = await handleError.call(mockExecuteSingleFunctions, data, response);
expect(result).toBe(data);
});
test('should throw NodeApiError for EntityAlreadyExists with user conflict', async () => {
mockExecuteSingleFunctions.getNodeParameter = jest
.fn()
.mockReturnValueOnce('user')
.mockReturnValueOnce('existingUserName');
response.statusCode = 400;
response.body = {
Error: { Code: 'EntityAlreadyExists', Message: 'User "existingUserName" already exists' },
} as JsonObject;
await expect(handleError.call(mockExecuteSingleFunctions, data, response)).rejects.toThrow(
new NodeApiError(mockExecuteSingleFunctions.getNode(), response.body as JsonObject, {
message: 'User "existingUserName" already exists',
description: ERROR_DESCRIPTIONS.EntityAlreadyExists.User,
}),
);
});
test('should throw NodeApiError for NoSuchEntity with user not found', async () => {
mockExecuteSingleFunctions.getNodeParameter
.mockReturnValueOnce('user')
.mockReturnValueOnce('nonExistentUser');
response.statusCode = 404;
response.body = {
Error: { Code: 'NoSuchEntity', Message: 'User "nonExistentUser" does not exist' },
} as JsonObject;
await expect(handleError.call(mockExecuteSingleFunctions, data, response)).rejects.toThrowError(
new NodeApiError(mockExecuteSingleFunctions.getNode(), response.body as JsonObject, {
message: 'User "nonExistentUser" does not exist',
description: ERROR_DESCRIPTIONS.NoSuchEntity.User,
}),
);
});
test('should throw generic error if no specific mapping exists', async () => {
mockExecuteSingleFunctions.getNodeParameter.mockReturnValue('container');
response.statusCode = 400;
response.body = { Error: { Code: 'BadRequest', Message: 'Invalid request' } } as JsonObject;
await expect(handleError.call(mockExecuteSingleFunctions, data, response)).rejects.toThrow(
new NodeApiError(mockExecuteSingleFunctions.getNode(), response.body as JsonObject, {
message: 'BadRequest',
description: 'Invalid request',
}),
);
});
test('should throw NodeApiError for EntityAlreadyExists with group conflict', async () => {
mockExecuteSingleFunctions.getNodeParameter
.mockReturnValueOnce('group')
.mockReturnValue('existingGroupName');
response.statusCode = 400;
response.body = {
Error: { Code: 'EntityAlreadyExists', Message: 'Group "existingGroupName" already exists' },
} as JsonObject;
await expect(handleError.call(mockExecuteSingleFunctions, data, response)).rejects.toThrow(
new NodeApiError(mockExecuteSingleFunctions.getNode(), response.body as JsonObject, {
message: 'Group "existingGroupName" already exists',
description: ERROR_DESCRIPTIONS.EntityAlreadyExists.Group,
}),
);
});
test('should throw NodeApiError for NoSuchEntity with group not found', async () => {
mockExecuteSingleFunctions.getNodeParameter
.mockReturnValueOnce('group')
.mockReturnValue('nonExistentGroup');
response.statusCode = 404;
response.body = {
Error: { Code: 'NoSuchEntity', Message: 'Group "nonExistentGroup" does not exist' },
} as JsonObject;
await expect(handleError.call(mockExecuteSingleFunctions, data, response)).rejects.toThrow(
new NodeApiError(mockExecuteSingleFunctions.getNode(), response.body as JsonObject, {
message: 'Group "nonExistentGroup" does not exist',
description: ERROR_DESCRIPTIONS.NoSuchEntity.Group,
}),
);
});
test('should throw NodeApiError for DeleteConflict', async () => {
mockExecuteSingleFunctions.getNodeParameter
.mockReturnValueOnce('user')
.mockReturnValue('userInGroup');
response.statusCode = 400;
response.body = {
Error: { Code: 'DeleteConflict', Message: 'User "userIngroup" is in a group' },
} as JsonObject;
await expect(handleError.call(mockExecuteSingleFunctions, data, response)).rejects.toThrow(
new NodeApiError(mockExecuteSingleFunctions.getNode(), response.body as JsonObject, {
message: 'User "userIngroup" is in a group',
description: 'This entity is still in use. Remove users from the group before deleting.',
}),
);
});
});
@@ -0,0 +1,421 @@
import type { IHttpRequestOptions } from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import {
preprocessTags,
deleteGroupMembers,
validatePath,
validateUserPath,
removeUserFromGroups,
findUsersForGroup,
simplifyGetGroupsResponse,
simplifyGetAllGroupsResponse,
simplifyGetAllUsersResponse,
validateName,
validatePermissionsBoundary,
encodeBodyAsFormUrlEncoded,
} from '../../helpers/utils';
import { awsApiRequest } from '../../transport';
jest.mock('../../transport', () => ({
awsApiRequest: jest.fn(),
}));
describe('AWS IAM - Helper Functions', () => {
let mockNode: any;
beforeEach(() => {
mockNode = {
getNodeParameter: jest.fn(),
getNode: jest.fn(),
helpers: {
returnJsonArray: jest.fn((input: unknown[]) => input.map((i) => ({ json: i }))),
},
};
});
describe('encodeBodyAsFormUrlEncoded', () => {
it('should encode the body as application/x-www-form-urlencoded', async () => {
const requestOptions: IHttpRequestOptions = {
body: {
client_id: 'myClient',
client_secret: 'mySecret',
grant_type: 'client_credentials',
},
url: '',
headers: {},
};
const result = await encodeBodyAsFormUrlEncoded.call(mockNode, requestOptions);
expect(result.body).toBe(
'client_id=myClient&client_secret=mySecret&grant_type=client_credentials',
);
});
it('should return unchanged options if no body is present', async () => {
const requestOptions: IHttpRequestOptions = { url: '', headers: {} };
const result = await encodeBodyAsFormUrlEncoded.call(mockNode, requestOptions);
expect(result).toEqual({ url: '', headers: {} });
});
});
describe('findUsersForGroup', () => {
it('should return users for a valid groupName', async () => {
mockNode.getNodeParameter.mockReturnValue('groupName');
const mockResponse = {
GetGroupResponse: { GetGroupResult: { Users: [{ UserName: 'user1' }] } },
};
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
const result = await findUsersForGroup.call(mockNode, 'groupName');
expect(result).toEqual([{ UserName: 'user1' }]);
});
});
describe('preprocessTags', () => {
it('should preprocess tags correctly into request body', async () => {
mockNode.getNodeParameter.mockReturnValue({
tags: [
{ key: 'Department', value: 'Engineering' },
{ key: 'Role', value: 'Developer' },
],
});
const requestOptions = { body: '', headers: {}, url: '' };
const result = await preprocessTags.call(mockNode, requestOptions);
expect(result.body).toBe(
'Tags.member.1.Key=Department&Tags.member.1.Value=Engineering&Tags.member.2.Key=Role&Tags.member.2.Value=Developer',
);
});
it('should throw error if a tag is missing a key', async () => {
mockNode.getNodeParameter.mockReturnValue({
tags: [{ key: '', value: 'Engineering' }],
});
const requestOptions = { body: '', headers: {}, url: '' };
await expect(preprocessTags.call(mockNode, requestOptions)).rejects.toThrow(
NodeOperationError,
);
await expect(preprocessTags.call(mockNode, requestOptions)).rejects.toThrow(
"Tag at position 1 is missing 'Key'. Both 'Key' and 'Value' are required.",
);
});
it('should throw error if a tag is missing a value', async () => {
mockNode.getNodeParameter.mockReturnValue({
tags: [{ key: 'Department', value: '' }],
});
const requestOptions = { body: '', headers: {}, url: '' };
await expect(preprocessTags.call(mockNode, requestOptions)).rejects.toThrow(
NodeOperationError,
);
await expect(preprocessTags.call(mockNode, requestOptions)).rejects.toThrow(
"Tag at position 1 is missing 'Value'. Both 'Key' and 'Value' are required.",
);
});
});
describe('deleteGroupMembers', () => {
it('should attempt to remove users from a group', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'group') {
return 'groupName';
}
return null;
});
const mockUsers = [{ UserName: 'user1' }];
(awsApiRequest as jest.Mock).mockResolvedValue(mockUsers);
const requestOptions = { headers: {}, url: '' };
const result = await deleteGroupMembers.call(mockNode, requestOptions);
expect(result).toEqual(requestOptions);
expect(awsApiRequest).toHaveBeenCalledWith(
expect.objectContaining({
method: 'POST',
body: expect.stringContaining('GroupName=groupName'),
}),
);
});
});
describe('validatePath', () => {
it('should throw an error for invalid path length', async () => {
mockNode.getNodeParameter.mockReturnValue('');
await expect(validatePath.call(mockNode, { headers: {}, url: '' })).rejects.toThrowError(
NodeOperationError,
);
});
it('should throw an error for invalid path format', async () => {
mockNode.getNodeParameter.mockReturnValue('/invalidPath');
await expect(validatePath.call(mockNode, { url: '' })).rejects.toThrowError(
NodeOperationError,
);
});
it('should pass for a valid path', async () => {
mockNode.getNodeParameter.mockReturnValue('/valid/path/');
const result = await validatePath.call(mockNode, { headers: {}, url: '' });
expect(result).toEqual({ headers: {}, url: '' });
});
});
describe('validateUserPath', () => {
it('should throw an error for invalid path prefix', async () => {
mockNode.getNodeParameter.mockReturnValue('/invalidPrefix');
const mockResponse = {
ListUsersResponse: {
ListUsersResult: {
Users: [{ UserName: 'user1', Path: '/validPrefix/user1' }],
},
},
};
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
await expect(validateUserPath.call(mockNode, { headers: {}, url: '' })).rejects.toThrowError(
NodeOperationError,
);
});
it('should modify the request body with a valid path', async () => {
mockNode.getNodeParameter.mockReturnValue('/validPrefix/');
const requestOptions = { body: {}, headers: {}, url: '' };
const mockResponse = {
ListUsersResponse: {
ListUsersResult: {
Users: [{ UserName: 'user1', Path: '/validPrefix/user1' }],
},
},
};
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
const result = await validateUserPath.call(mockNode, requestOptions);
expect(result.body).toHaveProperty('PathPrefix', '/validPrefix/');
});
});
describe('validateName', () => {
const requestOptions: IHttpRequestOptions = { body: {}, url: '' };
it('should throw an error if userName contains spaces', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'resource') return 'user';
if (param === 'userName') return 'John Doe';
return '';
});
await expect(validateName.call(mockNode, requestOptions)).rejects.toThrowError(
new NodeOperationError(mockNode.getNode(), 'User name should not contain spaces.'),
);
});
it('should throw an error if userName contains invalid characters', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'resource') return 'user';
if (param === 'userName') return 'John@Doe';
return '';
});
await expect(validateName.call(mockNode, requestOptions)).rejects.toThrowError(
new NodeOperationError(
mockNode.getNode(),
'User name can have up to 64 characters. Valid characters: letters, numbers, hyphens (-), and underscores (_).',
),
);
});
it('should pass validation for valid userName', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'resource') return 'user';
if (param === 'userName') return 'John_Doe123';
return '';
});
await expect(validateName.call(mockNode, requestOptions)).resolves.toEqual(requestOptions);
});
it('should throw an error if groupName contains spaces', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'resource') return 'group';
if (param === 'groupName') return 'Group Name';
return '';
});
await expect(validateName.call(mockNode, requestOptions)).rejects.toThrowError(
new NodeOperationError(mockNode.getNode(), 'Group name should not contain spaces.'),
);
});
it('should throw an error if groupName contains invalid characters', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'resource') return 'group';
if (param === 'groupName') return 'Group@Name';
return '';
});
await expect(validateName.call(mockNode, requestOptions)).rejects.toThrowError(
new NodeOperationError(
mockNode.getNode(),
'Group name can have up to 128 characters. Valid characters: letters, numbers, hyphens (-), and underscores (_).',
),
);
});
it('should pass validation for valid groupName', async () => {
mockNode.getNodeParameter.mockImplementation((param: string) => {
if (param === 'resource') return 'group';
if (param === 'groupName') return 'Group_Name-123';
return '';
});
await expect(validateName.call(mockNode, requestOptions)).resolves.toEqual(requestOptions);
});
});
describe('validatePermissionsBoundary', () => {
const requestOptions: IHttpRequestOptions = { body: {}, url: '' };
it('should return the request options unchanged if no permissions boundary is set', async () => {
mockNode.getNodeParameter.mockReturnValue(undefined);
const result = await validatePermissionsBoundary.call(mockNode, requestOptions);
expect(result).toEqual(requestOptions);
});
it('should add a valid permissions boundary to the request body', async () => {
const validArn = 'arn:aws:iam::123456789012:policy/ExamplePolicy';
mockNode.getNodeParameter.mockReturnValue(validArn);
const result = await validatePermissionsBoundary.call(mockNode, requestOptions);
expect(result.body).toEqual({ PermissionsBoundary: validArn });
});
it('should throw an error for invalid permissions boundary format', async () => {
const invalidArn = 'invalid:arn:format';
mockNode.getNodeParameter.mockReturnValue(invalidArn);
await expect(
validatePermissionsBoundary.call(mockNode, { body: {}, url: '', headers: {} }),
).rejects.toThrow(NodeOperationError);
});
});
describe('simplifyGetGroupsResponse', () => {
it('should return group data', async () => {
mockNode.getNodeParameter.mockReturnValue(false);
const mockResponse = {
body: { GetGroupResponse: { GetGroupResult: { Group: { GroupName: 'TestGroup' } } } },
headers: {},
url: '',
statusCode: 200,
};
const result = await simplifyGetGroupsResponse.call(mockNode, [], mockResponse);
expect(result).toEqual([{ json: { GroupName: 'TestGroup' } }]);
});
it('should include users if "includeUsers" is true', async () => {
mockNode.getNodeParameter.mockReturnValue(true);
const mockResponse = {
body: {
GetGroupResponse: {
GetGroupResult: { Group: { GroupName: 'TestGroup' }, Users: [{ UserName: 'user1' }] },
},
},
headers: {},
url: '',
statusCode: 200,
};
const result = await simplifyGetGroupsResponse.call(mockNode, [], mockResponse);
expect(result).toEqual([
{ json: { GroupName: 'TestGroup', Users: [{ UserName: 'user1' }] } },
]);
});
});
describe('simplifyGetAllGroupsResponse', () => {
it('should return groups without users if "includeUsers" is false', async () => {
mockNode.getNodeParameter.mockReturnValue(false);
const mockResponse = {
body: {
ListGroupsResponse: { ListGroupsResult: { Groups: [{ GroupName: 'TestGroup' }] } },
},
headers: {},
url: '',
statusCode: 200,
};
const result = await simplifyGetAllGroupsResponse.call(mockNode, [], mockResponse);
expect(result).toEqual([{ json: { GroupName: 'TestGroup' } }]);
});
it('should return groups with users if "includeUsers" is true', async () => {
mockNode.getNodeParameter.mockReturnValue(true);
const mockResponse = {
body: {
ListGroupsResponse: { ListGroupsResult: { Groups: [{ GroupName: 'TestGroup' }] } },
},
headers: {},
url: '',
statusCode: 200,
};
const mockUsers = [{ UserName: 'user1' }];
(awsApiRequest as jest.Mock).mockResolvedValueOnce({
GetGroupResponse: { GetGroupResult: { Users: mockUsers } },
});
const result = await simplifyGetAllGroupsResponse.call(mockNode, [], mockResponse);
expect(result).toEqual([{ json: { GroupName: 'TestGroup', Users: mockUsers } }]);
});
});
describe('simplifyGetAllUsersResponse', () => {
it('should return all users', async () => {
const mockResponse = {
body: { ListUsersResponse: { ListUsersResult: { Users: [{ UserName: 'user1' }] } } },
headers: {},
url: '',
statusCode: 200,
};
const result = await simplifyGetAllUsersResponse.call(mockNode, [], mockResponse);
expect(result).toEqual([{ json: { UserName: 'user1' } }]);
});
});
describe('removeUserFromGroups', () => {
it('should remove a user from all groups', async () => {
mockNode.getNodeParameter.mockReturnValue('user1');
const mockUserGroups = { results: [{ value: 'group1' }, { value: 'group2' }] };
(awsApiRequest as jest.Mock).mockResolvedValue(mockUserGroups);
const requestOptions = { headers: {}, url: '' };
const result = await removeUserFromGroups.call(mockNode, requestOptions);
expect(result).toEqual(requestOptions);
});
});
});
@@ -0,0 +1,119 @@
import type { ILoadOptionsFunctions } from 'n8n-workflow';
import { searchUsers, searchGroups, searchGroupsForUser } from '../../methods/listSearch';
import { awsApiRequest } from '../../transport';
jest.mock('../../transport', () => ({
awsApiRequest: jest.fn(),
}));
describe('AWS IAM - List search', () => {
afterEach(() => {
jest.clearAllMocks();
});
const mockContext = {
helpers: {
requestWithAuthentication: jest.fn(),
},
getNodeParameter: jest.fn(),
getCredentials: jest.fn(),
} as unknown as ILoadOptionsFunctions;
describe('searchUsers', () => {
it('should return an empty result if no users are found', async () => {
const responseData = { ListUsersResponse: { ListUsersResult: { Users: [] } } };
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchUsers.call(mockContext);
expect(result.results).toEqual([]);
});
it('should return formatted user results when users are found', async () => {
const responseData = {
ListUsersResponse: {
ListUsersResult: {
Users: [{ UserName: 'User1' }, { UserName: 'User2' }],
},
},
};
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchUsers.call(mockContext);
expect(result.results).toEqual([
{ name: 'User1', value: 'User1' },
{ name: 'User2', value: 'User2' },
]);
});
it('should apply filter to the user results', async () => {
const responseData = {
ListUsersResponse: {
ListUsersResult: {
Users: [{ UserName: 'User1' }, { UserName: 'User2' }],
},
},
};
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchUsers.call(mockContext, 'User1');
expect(result.results).toEqual([{ name: 'User1', value: 'User1' }]);
});
});
describe('searchGroups', () => {
it('should return an empty result if no groups are found', async () => {
const responseData = { ListGroupsResponse: { ListGroupsResult: { Groups: [] } } };
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchGroups.call(mockContext);
expect(result.results).toEqual([]);
});
it('should return formatted group results when groups are found', async () => {
const responseData = {
ListGroupsResponse: {
ListGroupsResult: {
Groups: [{ GroupName: 'Group1' }, { GroupName: 'Group2' }],
},
},
};
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchGroups.call(mockContext);
expect(result.results).toEqual([
{ name: 'Group1', value: 'Group1' },
{ name: 'Group2', value: 'Group2' },
]);
});
it('should apply filter to the group results', async () => {
const responseData = {
ListGroupsResponse: {
ListGroupsResult: {
Groups: [{ GroupName: 'Group1' }, { GroupName: 'Group2' }],
},
},
};
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchGroups.call(mockContext, 'Group1');
expect(result.results).toEqual([{ name: 'Group1', value: 'Group1' }]);
});
});
describe('searchGroupsForUser', () => {
it('should return empty if no user groups are found', async () => {
mockContext.getNodeParameter = jest.fn().mockReturnValue('user1');
const responseData = {
ListGroupsResponse: { ListGroupsResult: { Groups: [] } },
};
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
const result = await searchGroupsForUser.call(mockContext);
expect(result.results).toEqual([]);
});
});
});
@@ -0,0 +1,38 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Add User to Group', () => {
beforeEach(() => {
const baseUrl = 'https://iam.amazonaws.com/';
nock.cleanAll();
nock(baseUrl)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'AddUserToGroup',
Version: CURRENT_VERSION,
UserName: 'Jonas',
GroupName: 'GroupNameUpdated2',
})
.reply(200, {
AddUserToGroupResponse: {
ResponseMetadata: {
RequestId: '8192250c-9225-4903-af62-a521ce939968',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['addToGroup.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,78 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -120],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"operation": "addToGroup",
"user": {
"__rl": true,
"value": "Jonas",
"mode": "list",
"cachedResultName": "Jonas"
},
"group": {
"__rl": true,
"value": "GroupNameUpdated2",
"mode": "list",
"cachedResultName": "GroupNameUpdated2"
},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [160, -120],
"id": "982d8c6e-e94a-414c-90ba-35743676eef8",
"name": "addToGroup",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"addToGroup": [
{
"json": {
"AddUserToGroupResponse": {
"ResponseMetadata": {
"RequestId": "8192250c-9225-4903-af62-a521ce939968"
}
}
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "addToGroup",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "2e9e5c7a-705f-49a7-83db-397f1bb48798",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,47 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Create user', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'CreateUser',
Version: CURRENT_VERSION,
UserName: 'UserTest1',
})
.reply(200, {
CreateUserResponse: {
CreateUserResult: {
User: {
Arn: 'arn:aws:iam::130450532146:user/UserTest1',
CreateDate: 1744115235,
PasswordLastUsed: null,
Path: '/',
PermissionsBoundary: null,
UserId: 'AIDAR4X3VE4ZHHMNF7NBB',
UserName: 'UserTest1',
},
},
ResponseMetadata: {
RequestId: 'ce14481c-5629-4ae4-9eae-3722f48bb3e0',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['create.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,70 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [200, 120],
"id": "b4205abf-7102-4e53-8aed-7bd047acfaf4",
"name": "When clicking Test workflow"
},
{
"parameters": {
"operation": "create",
"userName": "UserTest1",
"additionalFields": {
"path": "/new/path/"
},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [400, 120],
"id": "64bbfce7-cc7d-4e69-82d3-d4ecac5ad389",
"name": "AWS IAM12",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "AWS IAM12",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"AWS IAM12": [
{
"json": {
"CreateUserResponse": {
"CreateUserResult": {
"User": {
"Arn": "arn:aws:iam::130450532146:user/UserTest1",
"CreateDate": 1744115235,
"PasswordLastUsed": null,
"Path": "/",
"PermissionsBoundary": null,
"UserId": "AIDAR4X3VE4ZHHMNF7NBB",
"UserName": "UserTest1"
}
},
"ResponseMetadata": {
"RequestId": "ce14481c-5629-4ae4-9eae-3722f48bb3e0"
}
}
}
}
]
}
}
@@ -0,0 +1,96 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Delete user', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'GetUser',
Version: CURRENT_VERSION,
UserName: 'JohnThis10',
})
.reply(200, {
GetUserResponse: {
GetUserResult: {
User: {
UserName: 'JohnThis10',
},
},
},
});
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'ListGroups',
Version: CURRENT_VERSION,
})
.reply(200, {
ListGroupsResponse: {
ListGroupsResult: {
Groups: [{ GroupName: 'GroupA' }],
},
},
});
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'GetGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupA',
})
.reply(200, {
GetGroupResponse: {
GetGroupResult: {
Users: [{ UserName: 'JohnThis10' }],
},
},
});
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'RemoveUserFromGroup',
Version: CURRENT_VERSION,
GroupName: 'GroupA',
UserName: 'JohnThis10',
})
.reply(200, {
ResponseMetadata: {
RequestId: 'remove-groupA-id',
},
});
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'DeleteUser',
Version: CURRENT_VERSION,
UserName: 'JohnThis10',
})
.reply(200, {
DeleteUserResponse: {
ResponseMetadata: {
RequestId: '44c7c6c0-260b-4dfd-beee-2cce8f05bed3',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['delete.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,72 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-80, -100],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"operation": "delete",
"user": {
"__rl": true,
"value": "JohnThis10",
"mode": "list",
"cachedResultName": "JohnThis10"
},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [180, -100],
"id": "f8bf0d9d-78aa-48e6-80bd-3018c0bac0ea",
"name": "deleteUser",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"deleteUser": [
{
"json": {
"DeleteUserResponse": {
"ResponseMetadata": {
"RequestId": "44c7c6c0-260b-4dfd-beee-2cce8f05bed3"
}
}
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "deleteUser",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "731b81df-c6a0-4074-a3f8-e0ad0e9c884a",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,49 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Get User', () => {
beforeEach(() => {
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'GetUser',
Version: CURRENT_VERSION,
UserName: 'accounts@this.de',
})
.reply(200, {
GetUserResponse: {
GetUserResult: {
User: {
UserName: 'accounts@this.de',
UserId: 'AIDAR4X3VE4ZANWXRN2L2',
Arn: 'arn:aws:iam::130450532146:user/accounts@this.de',
CreateDate: 1733911052,
Path: '/',
Tags: [
{
Key: 'AKIAR4X3VE4ZALQYFEMT',
Value: 'API dev',
},
],
PasswordLastUsed: null,
PermissionsBoundary: null,
},
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['get.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,79 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -120],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"operation": "get",
"user": {
"__rl": true,
"mode": "list",
"value": "accounts@this.de"
},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [140, -100],
"id": "982d8c6e-e94a-414c-90ba-35743676eef8",
"name": "getUser",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"getUser": [
{
"json": {
"Arn": "arn:aws:iam::130450532146:user/accounts@this.de",
"CreateDate": 1733911052,
"PasswordLastUsed": null,
"Path": "/",
"PermissionsBoundary": null,
"Tags": [
{
"Key": "AKIAR4X3VE4ZALQYFEMT",
"Value": "API dev"
}
],
"UserId": "AIDAR4X3VE4ZANWXRN2L2",
"UserName": "accounts@this.de"
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "getUser",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "5ce54c9d-de6e-499a-b042-0bb89c41208c",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,57 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Get All Users', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'ListUsers',
Version: CURRENT_VERSION,
MaxItems: 100,
})
.reply(200, {
ListUsersResponse: {
ListUsersResult: {
Users: [
{
Arn: 'arn:aws:iam::130450532146:user/Johnnn',
UserName: 'Johnnn',
UserId: 'AIDAR4X3VE4ZAJGXLCVOP',
Path: '/',
CreateDate: 1739198010,
PasswordLastUsed: null,
PermissionsBoundary: null,
Tags: null,
},
{
Arn: 'arn:aws:iam::130450532146:user/rhis/path/Jonas',
UserName: 'Jonas',
UserId: 'AIDAR4X3VE4ZDJJFKI6OU',
Path: '/rhis/path/',
CreateDate: 1739198295,
PasswordLastUsed: null,
PermissionsBoundary: null,
Tags: null,
},
],
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['getAll.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,81 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -220],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"additionalFields": {},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [160, -220],
"id": "23fd4c79-516d-49dc-81c8-e68052a294d1",
"name": "getAllUsers",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"getAllUsers": [
{
"json": {
"Arn": "arn:aws:iam::130450532146:user/Johnnn",
"CreateDate": 1739198010,
"PasswordLastUsed": null,
"Path": "/",
"PermissionsBoundary": null,
"Tags": null,
"UserId": "AIDAR4X3VE4ZAJGXLCVOP",
"UserName": "Johnnn"
}
},
{
"json": {
"Arn": "arn:aws:iam::130450532146:user/rhis/path/Jonas",
"CreateDate": 1739198295,
"PasswordLastUsed": null,
"Path": "/rhis/path/",
"PermissionsBoundary": null,
"Tags": null,
"UserId": "AIDAR4X3VE4ZDJJFKI6OU",
"UserName": "Jonas"
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "getAllUsers",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "c23e7348-6add-4711-b567-53922f951cbe",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,37 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Remove User From Group', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'RemoveUserFromGroup',
Version: CURRENT_VERSION,
UserName: 'UserTest1',
GroupName: 'GroupCreatedAfter',
})
.reply(200, {
RemoveUserFromGroupResponse: {
ResponseMetadata: {
RequestId: '48508b51-1506-496c-8455-7135269209f0',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['removeFromGroup.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,78 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-40, -120],
"id": "7da2ce49-9a9d-4240-b082-ff1b12d101b1",
"name": "When clicking Test workflow"
},
{
"parameters": {
"operation": "removeFromGroup",
"user": {
"__rl": true,
"value": "UserTest1",
"mode": "list",
"cachedResultName": "UserTest1"
},
"group": {
"__rl": true,
"value": "GroupCreatedAfter",
"mode": "list",
"cachedResultName": "GroupCreatedAfter"
},
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [140, -120],
"id": "5f05f1ad-aaae-49c1-b03d-c77c1442a0c9",
"name": "removeFromGroup",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"pinData": {
"removeFromGroup": [
{
"json": {
"RemoveUserFromGroupResponse": {
"ResponseMetadata": {
"RequestId": "48508b51-1506-496c-8455-7135269209f0"
}
}
}
}
]
},
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "removeFromGroup",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "9cb635bc-9e0f-4903-8705-c0ec4495c39f",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "26f38eb23ad84214537831cfef4032299ffac994ff18d5cb72e82d31ac4ceac4"
},
"id": "0qqycx08fTBpfbC9",
"tags": []
}
@@ -0,0 +1,37 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
import nock from 'nock';
import { BASE_URL, CURRENT_VERSION } from '../../helpers/constants';
describe('AWS IAM - Update User', () => {
beforeEach(() => {
nock.cleanAll();
nock(BASE_URL)
.persist()
.defaultReplyHeaders({ 'Content-Type': 'application/x-amz-json-1.1' })
.post('/', {
Action: 'UpdateUser',
Version: CURRENT_VERSION,
UserName: 'NewUser',
NewUserName: 'UserTest',
})
.reply(200, {
UpdateUserResponse: {
ResponseMetadata: {
RequestId: 'bdb4a8b5-627a-41a7-aba9-5733b7869c16',
},
},
});
});
new NodeTestHarness().setupTests({
workflowFiles: ['update.workflow.json'],
credentials: {
aws: {
region: 'eu-central-1',
accessKeyId: 'test',
secretAccessKey: 'test',
},
},
});
});
@@ -0,0 +1,62 @@
{
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [200, 120],
"id": "b4205abf-7102-4e53-8aed-7bd047acfaf4",
"name": "When clicking Test workflow"
},
{
"parameters": {
"operation": "update",
"user": {
"__rl": true,
"value": "NewUser",
"mode": "list",
"cachedResultName": "NewUser"
},
"userName": "UserTest",
"requestOptions": {}
},
"type": "n8n-nodes-base.awsIam",
"typeVersion": 1,
"position": [400, 120],
"id": "64bbfce7-cc7d-4e69-82d3-d4ecac5ad389",
"name": "AWS IAM12",
"credentials": {
"aws": {
"id": "exampleId",
"name": "AWS US EAST"
}
}
}
],
"connections": {
"When clicking Test workflow": {
"main": [
[
{
"node": "AWS IAM12",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {
"AWS IAM12": [
{
"json": {
"UpdateUserResponse": {
"ResponseMetadata": {
"RequestId": "bdb4a8b5-627a-41a7-aba9-5733b7869c16"
}
}
}
}
]
}
}