mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
152 lines
4.0 KiB
TypeScript
152 lines
4.0 KiB
TypeScript
import { mock } from 'jest-mock-extended';
|
|
import { CREDENTIAL_ERRORS, CredentialDataError, Credentials, type ErrorReporter } from 'n8n-core';
|
|
import { CREDENTIAL_EMPTY_VALUE, type ICredentialType } from 'n8n-workflow';
|
|
|
|
import { CREDENTIAL_BLANKING_VALUE } from '@/constants';
|
|
import type { CredentialTypes } from '@/credential-types';
|
|
import { CredentialsService } from '@/credentials/credentials.service';
|
|
import type { CredentialsEntity } from '@/databases/entities/credentials-entity';
|
|
import type { CredentialsRepository } from '@/databases/repositories/credentials.repository';
|
|
|
|
describe('CredentialsService', () => {
|
|
const credType = mock<ICredentialType>({
|
|
extends: [],
|
|
properties: [
|
|
{
|
|
name: 'clientSecret',
|
|
type: 'string',
|
|
typeOptions: { password: true },
|
|
doNotInherit: false,
|
|
},
|
|
{
|
|
name: 'accessToken',
|
|
type: 'string',
|
|
typeOptions: { password: true },
|
|
doNotInherit: false,
|
|
},
|
|
],
|
|
});
|
|
|
|
const errorReporter = mock<ErrorReporter>();
|
|
const credentialTypes = mock<CredentialTypes>();
|
|
const credentialsRepository = mock<CredentialsRepository>();
|
|
const service = new CredentialsService(
|
|
credentialsRepository,
|
|
mock(),
|
|
mock(),
|
|
mock(),
|
|
errorReporter,
|
|
mock(),
|
|
mock(),
|
|
credentialTypes,
|
|
mock(),
|
|
mock(),
|
|
mock(),
|
|
mock(),
|
|
mock(),
|
|
);
|
|
|
|
beforeEach(() => jest.resetAllMocks());
|
|
|
|
describe('redact', () => {
|
|
it('should redact sensitive values', () => {
|
|
const credential = mock<CredentialsEntity>({
|
|
id: '123',
|
|
name: 'Test Credential',
|
|
type: 'oauth2',
|
|
});
|
|
|
|
const decryptedData = {
|
|
clientId: 'abc123',
|
|
clientSecret: 'sensitiveSecret',
|
|
accessToken: '',
|
|
oauthTokenData: 'super-secret',
|
|
csrfSecret: 'super-secret',
|
|
};
|
|
|
|
credentialTypes.getByName.calledWith(credential.type).mockReturnValueOnce(credType);
|
|
|
|
const redactedData = service.redact(decryptedData, credential);
|
|
|
|
expect(redactedData).toEqual({
|
|
clientId: 'abc123',
|
|
clientSecret: CREDENTIAL_BLANKING_VALUE,
|
|
accessToken: CREDENTIAL_EMPTY_VALUE,
|
|
oauthTokenData: CREDENTIAL_BLANKING_VALUE,
|
|
csrfSecret: CREDENTIAL_BLANKING_VALUE,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('decrypt', () => {
|
|
const data = {
|
|
clientId: 'abc123',
|
|
clientSecret: 'sensitiveSecret',
|
|
accessToken: '',
|
|
oauthTokenData: 'super-secret',
|
|
csrfSecret: 'super-secret',
|
|
};
|
|
const credentialEntity = mock<CredentialsEntity>({
|
|
id: '123',
|
|
name: 'Test Credential',
|
|
type: 'oauth2',
|
|
});
|
|
const credentials = mock<Credentials>({ id: credentialEntity.id });
|
|
|
|
beforeEach(() => {
|
|
credentialTypes.getByName.calledWith(credentialEntity.type).mockReturnValueOnce(credType);
|
|
});
|
|
|
|
it('should redact sensitive values by default', () => {
|
|
// ARRANGE
|
|
jest.spyOn(Credentials.prototype, 'getData').mockReturnValueOnce(data);
|
|
|
|
// ACT
|
|
const redactedData = service.decrypt(credentialEntity);
|
|
|
|
// ASSERT
|
|
expect(redactedData).toEqual({
|
|
...data,
|
|
clientSecret: CREDENTIAL_BLANKING_VALUE,
|
|
accessToken: CREDENTIAL_EMPTY_VALUE,
|
|
oauthTokenData: CREDENTIAL_BLANKING_VALUE,
|
|
csrfSecret: CREDENTIAL_BLANKING_VALUE,
|
|
});
|
|
});
|
|
|
|
it('should return sensitive values if `includeRawData` is true', () => {
|
|
// ARRANGE
|
|
jest.spyOn(Credentials.prototype, 'getData').mockReturnValueOnce(data);
|
|
|
|
// ACT
|
|
const redactedData = service.decrypt(credentialEntity, true);
|
|
|
|
// ASSERT
|
|
expect(redactedData).toEqual(data);
|
|
});
|
|
|
|
it('should return return an empty object if decryption fails', () => {
|
|
// ARRANGE
|
|
const decryptionError = new CredentialDataError(
|
|
credentials,
|
|
CREDENTIAL_ERRORS.DECRYPTION_FAILED,
|
|
);
|
|
jest.spyOn(Credentials.prototype, 'getData').mockImplementation(() => {
|
|
throw decryptionError;
|
|
});
|
|
|
|
// ACT
|
|
const redactedData = service.decrypt(credentialEntity, true);
|
|
|
|
// ASSERT
|
|
expect(redactedData).toEqual({});
|
|
expect(credentialTypes.getByName).not.toHaveBeenCalled();
|
|
expect(errorReporter.error).toHaveBeenCalledWith(decryptionError, {
|
|
extra: { credentialId: credentialEntity.id },
|
|
level: 'error',
|
|
tags: { credentialType: credentialEntity.type },
|
|
});
|
|
});
|
|
});
|
|
});
|