Skip to content

Commit

Permalink
fix(core): Always derive instanceId from the encryption key (no-cha…
Browse files Browse the repository at this point in the history
…nglog) (n8n-io#7501)

This was the expected behavior, until I changed it in
n8n-io#7471
  • Loading branch information
netroy authored Oct 24, 2023
1 parent baecb93 commit a9fdd01
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 15 deletions.
33 changes: 18 additions & 15 deletions packages/core/src/InstanceSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { jsonParse } from 'n8n-workflow';

interface ReadOnlySettings {
encryptionKey: string;
instanceId: string;
}

interface WritableSettings {
Expand All @@ -32,14 +31,12 @@ export class InstanceSettings {

private settings = this.loadOrCreate();

readonly instanceId = this.generateInstanceId();

get encryptionKey() {
return this.settings.encryptionKey;
}

get instanceId() {
return this.settings.instanceId;
}

get tunnelSubdomain() {
return this.settings.tunnelSubdomain;
}
Expand All @@ -58,25 +55,31 @@ export class InstanceSettings {
}

private loadOrCreate(): Settings {
let settings: Settings;
const { settingsFile } = this;
if (existsSync(settingsFile)) {
const content = readFileSync(settingsFile, 'utf8');
return jsonParse(content, {
settings = jsonParse(content, {
errorMessage: `Error parsing n8n-config file "${settingsFile}". It does not seem to be valid JSON.`,
});
} else {
// If file doesn't exist, create new settings
const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64');
settings = { encryptionKey };
mkdirSync(path.dirname(settingsFile));
this.save(settings);
// console.info(`UserSettings were generated and saved to: ${settingsFile}`);
}

// If file doesn't exist, create new settings
const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64');
const instanceId = createHash('sha256')
const { encryptionKey, tunnelSubdomain } = settings;
return { encryptionKey, tunnelSubdomain };
}

private generateInstanceId() {
const { encryptionKey } = this;
return createHash('sha256')
.update(encryptionKey.slice(Math.round(encryptionKey.length / 2)))
.digest('hex');

const settings = { encryptionKey, instanceId };
mkdirSync(path.dirname(settingsFile));
this.save(settings);
console.log(`UserSettings were generated and saved to: ${settingsFile}`);
return settings;
}

private save(settings: Settings) {
Expand Down
61 changes: 61 additions & 0 deletions packages/core/test/InstanceSettings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import fs from 'fs';
import { InstanceSettings } from '@/InstanceSettings';

describe('InstanceSettings', () => {
process.env.N8N_USER_FOLDER = '/test';

describe('If the settings file exists', () => {
beforeEach(() => {
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
});

it('should load settings from the file', () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(JSON.stringify({ encryptionKey: 'test_key' }));
const settings = new InstanceSettings();
expect(settings.encryptionKey).toEqual('test_key');
expect(settings.instanceId).toEqual(
'6ce26c63596f0cc4323563c529acfca0cccb0e57f6533d79a60a42c9ff862ae7',
);
});

it('should throw error if settings file is not valid JSON', () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue('{"encryptionKey":"test_key"');
expect(() => new InstanceSettings()).toThrowError();
});
});

describe('If the settings file does not exist', () => {
it('should create a new settings file', () => {
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
const mkdirSpy = jest.spyOn(fs, 'mkdirSync').mockReturnValue('');
const writeFileSpy = jest.spyOn(fs, 'writeFileSync').mockReturnValue();
const settings = new InstanceSettings();
expect(settings.encryptionKey).not.toEqual('test_key');
expect(mkdirSpy).toHaveBeenCalledWith('/test/.n8n');
expect(writeFileSpy).toHaveBeenCalledWith(
'/test/.n8n/config',
expect.stringContaining('"encryptionKey":'),
'utf-8',
);
});

it('should pick up the encryption key from env var N8N_ENCRYPTION_KEY', () => {
process.env.N8N_ENCRYPTION_KEY = 'env_key';
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
const mkdirSpy = jest.spyOn(fs, 'mkdirSync').mockReturnValue('');
const writeFileSpy = jest.spyOn(fs, 'writeFileSync').mockReturnValue();
const settings = new InstanceSettings();
expect(settings.encryptionKey).toEqual('env_key');
expect(settings.instanceId).toEqual(
'2c70e12b7a0646f92279f427c7b38e7334d8e5389cff167a1dc30e73f826b683',
);
expect(settings.encryptionKey).not.toEqual('test_key');
expect(mkdirSpy).toHaveBeenCalledWith('/test/.n8n');
expect(writeFileSpy).toHaveBeenCalledWith(
'/test/.n8n/config',
expect.stringContaining('"encryptionKey":'),
'utf-8',
);
});
});
});

0 comments on commit a9fdd01

Please sign in to comment.