n8n/packages/cli/test/unit/CredentialsHelper.test.ts
2023-12-22 15:41:29 +01:00

297 lines
6.7 KiB
TypeScript

import Container from 'typedi';
import type {
IAuthenticateGeneric,
ICredentialDataDecryptedObject,
ICredentialType,
IHttpRequestOptions,
INode,
INodeProperties,
} from 'n8n-workflow';
import { deepCopy } from 'n8n-workflow';
import { Workflow } from 'n8n-workflow';
import { CredentialsHelper } from '@/CredentialsHelper';
import { NodeTypes } from '@/NodeTypes';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { mockInstance } from '../shared/mocking';
describe('CredentialsHelper', () => {
mockInstance(CredentialsRepository);
mockInstance(SharedCredentialsRepository);
const mockNodesAndCredentials = mockInstance(LoadNodesAndCredentials, {
loadedNodes: {
'test.set': {
sourcePath: '',
type: {
description: {
displayName: 'Set',
name: 'set',
group: ['input'],
version: 1,
description: 'Sets a value',
defaults: {
name: 'Set',
color: '#0000FF',
},
inputs: ['main'],
outputs: ['main'],
properties: [
{
displayName: 'Value1',
name: 'value1',
type: 'string',
default: 'default-value1',
},
{
displayName: 'Value2',
name: 'value2',
type: 'string',
default: 'default-value2',
},
],
},
},
},
},
});
const nodeTypes = mockInstance(NodeTypes);
describe('authenticate', () => {
const tests: Array<{
description: string;
input: {
credentials: ICredentialDataDecryptedObject;
credentialType: ICredentialType;
};
output: IHttpRequestOptions;
}> = [
{
description: 'basicAuth, default property names',
input: {
credentials: {
user: 'user1',
password: 'password1',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'User',
name: 'user',
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
auth: {
username: '={{$credentials.user}}',
password: '={{$credentials.password}}',
},
},
};
})(),
},
output: {
url: '',
headers: {},
auth: { username: 'user1', password: 'password1' },
qs: {},
},
},
{
description: 'headerAuth',
input: {
credentials: {
accessToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.accessToken}}',
},
},
};
})(),
},
output: { url: '', headers: { Authorization: 'Bearer test' }, qs: {} },
},
{
description: 'headerAuth, key and value expressions',
input: {
credentials: {
accessToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
default: '',
},
];
authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
'={{$credentials.accessToken}}': '=Bearer {{$credentials.accessToken}}',
},
},
};
})(),
},
output: { url: '', headers: { test: 'Bearer test' }, qs: {} },
},
{
description: 'queryAuth',
input: {
credentials: {
accessToken: 'test',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string',
default: '',
},
];
authenticate = {
type: 'generic',
properties: {
qs: {
accessToken: '={{$credentials.accessToken}}',
},
},
} as IAuthenticateGeneric;
})(),
},
output: { url: '', headers: {}, qs: { accessToken: 'test' } },
},
{
description: 'custom authentication',
input: {
credentials: {
accessToken: 'test',
user: 'testUser',
},
credentialType: new (class TestApi implements ICredentialType {
name = 'testApi';
displayName = 'Test API';
properties: INodeProperties[] = [
{
displayName: 'My Token',
name: 'myToken',
type: 'string',
default: '',
},
];
async authenticate(
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
requestOptions.headers!.Authorization = `Bearer ${credentials.accessToken}`;
requestOptions.qs!.user = credentials.user;
return requestOptions;
}
})(),
},
output: {
url: '',
headers: { Authorization: 'Bearer test' },
qs: { user: 'testUser' },
},
},
];
const node: INode = {
id: 'uuid-1',
parameters: {},
name: 'test',
type: 'test.set',
typeVersion: 1,
position: [0, 0],
};
const incomingRequestOptions = {
url: '',
headers: {},
qs: {},
};
const workflow = new Workflow({
nodes: [node],
connections: {},
active: false,
nodeTypes,
});
for (const testData of tests) {
test(testData.description, async () => {
//@ts-expect-error `loadedCredentials` is a getter and we are replacing it here with a property
mockNodesAndCredentials.loadedCredentials = {
[testData.input.credentialType.name]: {
type: testData.input.credentialType,
sourcePath: '',
},
};
const credentialsHelper = Container.get(CredentialsHelper);
const result = await credentialsHelper.authenticate(
testData.input.credentials,
testData.input.credentialType.name,
deepCopy(incomingRequestOptions),
workflow,
node,
);
expect(result).toEqual(testData.output);
});
}
});
});