Skip to content

Commit a9fdd01

Browse files
authored
fix(core): Always derive instanceId from the encryption key (no-changlog) (n8n-io#7501)
This was the expected behavior, until I changed it in n8n-io#7471
1 parent baecb93 commit a9fdd01

File tree

2 files changed

+79
-15
lines changed

2 files changed

+79
-15
lines changed

packages/core/src/InstanceSettings.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { jsonParse } from 'n8n-workflow';
66

77
interface ReadOnlySettings {
88
encryptionKey: string;
9-
instanceId: string;
109
}
1110

1211
interface WritableSettings {
@@ -32,14 +31,12 @@ export class InstanceSettings {
3231

3332
private settings = this.loadOrCreate();
3433

34+
readonly instanceId = this.generateInstanceId();
35+
3536
get encryptionKey() {
3637
return this.settings.encryptionKey;
3738
}
3839

39-
get instanceId() {
40-
return this.settings.instanceId;
41-
}
42-
4340
get tunnelSubdomain() {
4441
return this.settings.tunnelSubdomain;
4542
}
@@ -58,25 +55,31 @@ export class InstanceSettings {
5855
}
5956

6057
private loadOrCreate(): Settings {
58+
let settings: Settings;
6159
const { settingsFile } = this;
6260
if (existsSync(settingsFile)) {
6361
const content = readFileSync(settingsFile, 'utf8');
64-
return jsonParse(content, {
62+
settings = jsonParse(content, {
6563
errorMessage: `Error parsing n8n-config file "${settingsFile}". It does not seem to be valid JSON.`,
6664
});
65+
} else {
66+
// If file doesn't exist, create new settings
67+
const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64');
68+
settings = { encryptionKey };
69+
mkdirSync(path.dirname(settingsFile));
70+
this.save(settings);
71+
// console.info(`UserSettings were generated and saved to: ${settingsFile}`);
6772
}
6873

69-
// If file doesn't exist, create new settings
70-
const encryptionKey = process.env.N8N_ENCRYPTION_KEY ?? randomBytes(24).toString('base64');
71-
const instanceId = createHash('sha256')
74+
const { encryptionKey, tunnelSubdomain } = settings;
75+
return { encryptionKey, tunnelSubdomain };
76+
}
77+
78+
private generateInstanceId() {
79+
const { encryptionKey } = this;
80+
return createHash('sha256')
7281
.update(encryptionKey.slice(Math.round(encryptionKey.length / 2)))
7382
.digest('hex');
74-
75-
const settings = { encryptionKey, instanceId };
76-
mkdirSync(path.dirname(settingsFile));
77-
this.save(settings);
78-
console.log(`UserSettings were generated and saved to: ${settingsFile}`);
79-
return settings;
8083
}
8184

8285
private save(settings: Settings) {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import fs from 'fs';
2+
import { InstanceSettings } from '@/InstanceSettings';
3+
4+
describe('InstanceSettings', () => {
5+
process.env.N8N_USER_FOLDER = '/test';
6+
7+
describe('If the settings file exists', () => {
8+
beforeEach(() => {
9+
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
10+
});
11+
12+
it('should load settings from the file', () => {
13+
jest.spyOn(fs, 'readFileSync').mockReturnValue(JSON.stringify({ encryptionKey: 'test_key' }));
14+
const settings = new InstanceSettings();
15+
expect(settings.encryptionKey).toEqual('test_key');
16+
expect(settings.instanceId).toEqual(
17+
'6ce26c63596f0cc4323563c529acfca0cccb0e57f6533d79a60a42c9ff862ae7',
18+
);
19+
});
20+
21+
it('should throw error if settings file is not valid JSON', () => {
22+
jest.spyOn(fs, 'readFileSync').mockReturnValue('{"encryptionKey":"test_key"');
23+
expect(() => new InstanceSettings()).toThrowError();
24+
});
25+
});
26+
27+
describe('If the settings file does not exist', () => {
28+
it('should create a new settings file', () => {
29+
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
30+
const mkdirSpy = jest.spyOn(fs, 'mkdirSync').mockReturnValue('');
31+
const writeFileSpy = jest.spyOn(fs, 'writeFileSync').mockReturnValue();
32+
const settings = new InstanceSettings();
33+
expect(settings.encryptionKey).not.toEqual('test_key');
34+
expect(mkdirSpy).toHaveBeenCalledWith('/test/.n8n');
35+
expect(writeFileSpy).toHaveBeenCalledWith(
36+
'/test/.n8n/config',
37+
expect.stringContaining('"encryptionKey":'),
38+
'utf-8',
39+
);
40+
});
41+
42+
it('should pick up the encryption key from env var N8N_ENCRYPTION_KEY', () => {
43+
process.env.N8N_ENCRYPTION_KEY = 'env_key';
44+
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
45+
const mkdirSpy = jest.spyOn(fs, 'mkdirSync').mockReturnValue('');
46+
const writeFileSpy = jest.spyOn(fs, 'writeFileSync').mockReturnValue();
47+
const settings = new InstanceSettings();
48+
expect(settings.encryptionKey).toEqual('env_key');
49+
expect(settings.instanceId).toEqual(
50+
'2c70e12b7a0646f92279f427c7b38e7334d8e5389cff167a1dc30e73f826b683',
51+
);
52+
expect(settings.encryptionKey).not.toEqual('test_key');
53+
expect(mkdirSpy).toHaveBeenCalledWith('/test/.n8n');
54+
expect(writeFileSpy).toHaveBeenCalledWith(
55+
'/test/.n8n/config',
56+
expect.stringContaining('"encryptionKey":'),
57+
'utf-8',
58+
);
59+
});
60+
});
61+
});

0 commit comments

Comments
 (0)