mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor(core): Port user management config (#18205)
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
import { Container } from '@n8n/di';
|
||||||
|
|
||||||
|
import { UserManagementConfig } from '../user-management.config';
|
||||||
|
|
||||||
|
describe('UserManagementConfig', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
Container.reset();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const originalEnv = process.env;
|
||||||
|
afterEach(() => {
|
||||||
|
process.env = originalEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with refresh timout > session, sets refresh timout to `0`', () => {
|
||||||
|
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||||
|
|
||||||
|
process.env = {
|
||||||
|
N8N_USER_MANAGEMENT_JWT_DURATION_HOURS: '1',
|
||||||
|
N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS: '2',
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = Container.get(UserManagementConfig);
|
||||||
|
|
||||||
|
expect(config.jwtRefreshTimeoutHours).toBe(0);
|
||||||
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||||
|
'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS needs to be smaller than N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. Setting N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS to 0.',
|
||||||
|
);
|
||||||
|
|
||||||
|
consoleWarnSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with refresh timout == session, sets refresh timout to `0`', () => {
|
||||||
|
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||||
|
|
||||||
|
process.env = {
|
||||||
|
N8N_USER_MANAGEMENT_JWT_DURATION_HOURS: '1',
|
||||||
|
N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS: '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = Container.get(UserManagementConfig);
|
||||||
|
|
||||||
|
expect(config.jwtRefreshTimeoutHours).toBe(0);
|
||||||
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||||
|
'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS needs to be smaller than N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. Setting N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS to 0.',
|
||||||
|
);
|
||||||
|
|
||||||
|
consoleWarnSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with refresh timout < session, keeps refresh timout intact', () => {
|
||||||
|
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||||
|
|
||||||
|
process.env = {
|
||||||
|
N8N_USER_MANAGEMENT_JWT_DURATION_HOURS: '10',
|
||||||
|
N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS: '5',
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = Container.get(UserManagementConfig);
|
||||||
|
|
||||||
|
expect(config.jwtRefreshTimeoutHours).toBe(5);
|
||||||
|
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
consoleWarnSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -86,8 +86,34 @@ class EmailConfig {
|
|||||||
template: TemplateConfig;
|
template: TemplateConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INVALID_JWT_REFRESH_TIMEOUT_WARNING =
|
||||||
|
'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS needs to be smaller than N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. Setting N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS to 0.';
|
||||||
|
|
||||||
@Config
|
@Config
|
||||||
export class UserManagementConfig {
|
export class UserManagementConfig {
|
||||||
@Nested
|
@Nested
|
||||||
emails: EmailConfig;
|
emails: EmailConfig;
|
||||||
|
|
||||||
|
/** JWT secret to use. If unset, n8n will generate its own. */
|
||||||
|
@Env('N8N_USER_MANAGEMENT_JWT_SECRET')
|
||||||
|
jwtSecret: string = '';
|
||||||
|
|
||||||
|
/** How long (in hours) before the JWT expires. */
|
||||||
|
@Env('N8N_USER_MANAGEMENT_JWT_DURATION_HOURS')
|
||||||
|
jwtSessionDurationHours: number = 168;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long (in hours) before expiration to automatically refresh it.
|
||||||
|
* - `0` means 25% of `N8N_USER_MANAGEMENT_JWT_DURATION_HOURS`.
|
||||||
|
* - `-1` means it will never refresh. This forces users to log back in after expiration.
|
||||||
|
*/
|
||||||
|
@Env('N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS')
|
||||||
|
jwtRefreshTimeoutHours: number = 0;
|
||||||
|
|
||||||
|
sanitize() {
|
||||||
|
if (this.jwtRefreshTimeoutHours >= this.jwtSessionDurationHours) {
|
||||||
|
console.warn(INVALID_JWT_REFRESH_TIMEOUT_WARNING);
|
||||||
|
this.jwtRefreshTimeoutHours = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ export const Config: ClassDecorator = (ConfigClass: Class) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof config.sanitize === 'function') config.sanitize();
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Container } from '@n8n/di';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
|
|
||||||
|
import type { UserManagementConfig } from '../src/configs/user-management.config';
|
||||||
import { GlobalConfig } from '../src/index';
|
import { GlobalConfig } from '../src/index';
|
||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
@@ -101,6 +102,9 @@ describe('GlobalConfig', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
userManagement: {
|
userManagement: {
|
||||||
|
jwtSecret: '',
|
||||||
|
jwtSessionDurationHours: 168,
|
||||||
|
jwtRefreshTimeoutHours: 0,
|
||||||
emails: {
|
emails: {
|
||||||
mode: 'smtp',
|
mode: 'smtp',
|
||||||
smtp: {
|
smtp: {
|
||||||
@@ -124,7 +128,7 @@ describe('GlobalConfig', () => {
|
|||||||
'project-shared': '',
|
'project-shared': '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
} as UserManagementConfig,
|
||||||
eventBus: {
|
eventBus: {
|
||||||
checkUnsentInterval: 0,
|
checkUnsentInterval: 0,
|
||||||
crashRecoveryMode: 'extensive',
|
crashRecoveryMode: 'extensive',
|
||||||
|
|||||||
@@ -11,15 +11,12 @@ import { mock } from 'jest-mock-extended';
|
|||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
import { AuthService } from '@/auth/auth.service';
|
import { AuthService } from '@/auth/auth.service';
|
||||||
import config from '@/config';
|
|
||||||
import { AUTH_COOKIE_NAME } from '@/constants';
|
import { AUTH_COOKIE_NAME } from '@/constants';
|
||||||
import type { MfaService } from '@/mfa/mfa.service';
|
import type { MfaService } from '@/mfa/mfa.service';
|
||||||
import { JwtService } from '@/services/jwt.service';
|
import { JwtService } from '@/services/jwt.service';
|
||||||
import type { UrlService } from '@/services/url.service';
|
import type { UrlService } from '@/services/url.service';
|
||||||
|
|
||||||
describe('AuthService', () => {
|
describe('AuthService', () => {
|
||||||
config.set('userManagement.jwtSecret', 'random-secret');
|
|
||||||
|
|
||||||
const browserId = 'test-browser-id';
|
const browserId = 'test-browser-id';
|
||||||
const userData = {
|
const userData = {
|
||||||
id: '123',
|
id: '123',
|
||||||
@@ -29,8 +26,11 @@ describe('AuthService', () => {
|
|||||||
mfaEnabled: false,
|
mfaEnabled: false,
|
||||||
};
|
};
|
||||||
const user = mock<User>(userData);
|
const user = mock<User>(userData);
|
||||||
const globalConfig = mock<GlobalConfig>({ auth: { cookie: { secure: true, samesite: 'lax' } } });
|
const globalConfig = mock<GlobalConfig>({
|
||||||
const jwtService = new JwtService(mock());
|
auth: { cookie: { secure: true, samesite: 'lax' } },
|
||||||
|
userManagement: { jwtSecret: 'random-secret' },
|
||||||
|
});
|
||||||
|
const jwtService = new JwtService(mock(), globalConfig);
|
||||||
const urlService = mock<UrlService>();
|
const urlService = mock<UrlService>();
|
||||||
const userRepository = mock<UserRepository>();
|
const userRepository = mock<UserRepository>();
|
||||||
const invalidAuthTokenRepository = mock<InvalidAuthTokenRepository>();
|
const invalidAuthTokenRepository = mock<InvalidAuthTokenRepository>();
|
||||||
@@ -58,8 +58,8 @@ describe('AuthService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
jest.setSystemTime(now);
|
jest.setSystemTime(now);
|
||||||
config.set('userManagement.jwtSessionDurationHours', 168);
|
globalConfig.userManagement.jwtSessionDurationHours = 168;
|
||||||
config.set('userManagement.jwtRefreshTimeoutHours', 0);
|
globalConfig.userManagement.jwtRefreshTimeoutHours = 0;
|
||||||
globalConfig.auth.cookie = { secure: true, samesite: 'lax' };
|
globalConfig.auth.cookie = { secure: true, samesite: 'lax' };
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ describe('AuthService', () => {
|
|||||||
const testDurationSeconds = testDurationHours * Time.hours.toSeconds;
|
const testDurationSeconds = testDurationHours * Time.hours.toSeconds;
|
||||||
|
|
||||||
it('should apply it to tokens', () => {
|
it('should apply it to tokens', () => {
|
||||||
config.set('userManagement.jwtSessionDurationHours', testDurationHours);
|
globalConfig.userManagement.jwtSessionDurationHours = testDurationHours;
|
||||||
const token = authService.issueJWT(user, false, browserId);
|
const token = authService.issueJWT(user, false, browserId);
|
||||||
|
|
||||||
const decodedToken = jwtService.verify(token);
|
const decodedToken = jwtService.verify(token);
|
||||||
@@ -376,7 +376,7 @@ describe('AuthService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not refresh the cookie if jwtRefreshTimeoutHours is set to -1', async () => {
|
it('should not refresh the cookie if jwtRefreshTimeoutHours is set to -1', async () => {
|
||||||
config.set('userManagement.jwtRefreshTimeoutHours', -1);
|
globalConfig.userManagement.jwtRefreshTimeoutHours = -1;
|
||||||
|
|
||||||
userRepository.findOne.mockResolvedValue(user);
|
userRepository.findOne.mockResolvedValue(user);
|
||||||
expect(await authService.resolveJwt(validToken, req, res)).toEqual([
|
expect(await authService.resolveJwt(validToken, req, res)).toEqual([
|
||||||
|
|||||||
@@ -268,9 +268,9 @@ export class AuthService {
|
|||||||
return createHash('sha256').update(input).digest('base64');
|
return createHash('sha256').update(input).digest('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** How many **milliseconds** before expiration should a JWT be renewed */
|
/** How many **milliseconds** before expiration should a JWT be renewed. */
|
||||||
get jwtRefreshTimeout() {
|
get jwtRefreshTimeout() {
|
||||||
const { jwtRefreshTimeoutHours, jwtSessionDurationHours } = config.get('userManagement');
|
const { jwtRefreshTimeoutHours, jwtSessionDurationHours } = this.globalConfig.userManagement;
|
||||||
if (jwtRefreshTimeoutHours === 0) {
|
if (jwtRefreshTimeoutHours === 0) {
|
||||||
return Math.floor(jwtSessionDurationHours * 0.25 * Time.hours.toMilliseconds);
|
return Math.floor(jwtSessionDurationHours * 0.25 * Time.hours.toMilliseconds);
|
||||||
} else {
|
} else {
|
||||||
@@ -278,8 +278,8 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** How many **seconds** is an issued JWT valid for */
|
/** How many **seconds** is an issued JWT valid for. */
|
||||||
get jwtExpiration() {
|
get jwtExpiration() {
|
||||||
return config.get('userManagement.jwtSessionDurationHours') * Time.hours.toSeconds;
|
return this.globalConfig.userManagement.jwtSessionDurationHours * Time.hours.toSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
describe('userManagement.jwtRefreshTimeoutHours', () => {
|
|
||||||
it("resets jwtRefreshTimeoutHours to 0 if it's greater than or equal to jwtSessionDurationHours", async () => {
|
|
||||||
process.env.N8N_USER_MANAGEMENT_JWT_DURATION_HOURS = '1';
|
|
||||||
process.env.N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS = '1';
|
|
||||||
const { default: config } = await import('@/config');
|
|
||||||
|
|
||||||
expect(config.getEnv('userManagement.jwtRefreshTimeoutHours')).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -93,20 +93,6 @@ if (!inE2ETests && !inTest) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate Configuration
|
|
||||||
config.validate({
|
|
||||||
allowed: 'strict',
|
|
||||||
});
|
|
||||||
const userManagement = config.get('userManagement');
|
|
||||||
if (userManagement.jwtRefreshTimeoutHours >= userManagement.jwtSessionDurationHours) {
|
|
||||||
if (!inTest)
|
|
||||||
logger.warn(
|
|
||||||
'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS needs to smaller than N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. Setting N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS to 0 for now.',
|
|
||||||
);
|
|
||||||
|
|
||||||
config.set('userManagement.jwtRefreshTimeoutHours', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
setGlobalState({
|
setGlobalState({
|
||||||
defaultTimezone: globalConfig.generic.timezone,
|
defaultTimezone: globalConfig.generic.timezone,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -103,25 +103,6 @@ export const schema = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
userManagement: {
|
userManagement: {
|
||||||
jwtSecret: {
|
|
||||||
doc: 'Set a specific JWT secret (optional - n8n can generate one)', // Generated @ start.ts
|
|
||||||
format: String,
|
|
||||||
default: '',
|
|
||||||
env: 'N8N_USER_MANAGEMENT_JWT_SECRET',
|
|
||||||
},
|
|
||||||
jwtSessionDurationHours: {
|
|
||||||
doc: 'Set a specific expiration date for the JWTs in hours.',
|
|
||||||
format: Number,
|
|
||||||
default: 168,
|
|
||||||
env: 'N8N_USER_MANAGEMENT_JWT_DURATION_HOURS',
|
|
||||||
},
|
|
||||||
jwtRefreshTimeoutHours: {
|
|
||||||
doc: 'How long before the JWT expires to automatically refresh it. 0 means 25% of N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. -1 means it will never refresh, which forces users to login again after the defined period in N8N_USER_MANAGEMENT_JWT_DURATION_HOURS.',
|
|
||||||
format: Number,
|
|
||||||
default: 0,
|
|
||||||
env: 'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS',
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @important Do not remove until after cloud hooks are updated to stop using convict config.
|
* @important Do not remove until after cloud hooks are updated to stop using convict config.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import type { GlobalConfig } from '@n8n/config';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import type { InstanceSettings } from 'n8n-core';
|
import type { InstanceSettings } from 'n8n-core';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import { JwtService } from '@/services/jwt.service';
|
import { JwtService } from '@/services/jwt.service';
|
||||||
|
|
||||||
describe('JwtService', () => {
|
describe('JwtService', () => {
|
||||||
@@ -13,21 +13,29 @@ describe('JwtService', () => {
|
|||||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImlhdCI6MTY5OTk4NDMxM30.xNZOAmcidW5ovEF_mwIOzCWkJ70FEO6MFNLK2QRDOeQ';
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsImlhdCI6MTY5OTk4NDMxM30.xNZOAmcidW5ovEF_mwIOzCWkJ70FEO6MFNLK2QRDOeQ';
|
||||||
|
|
||||||
const instanceSettings = mock<InstanceSettings>({ encryptionKey: 'test-key' });
|
const instanceSettings = mock<InstanceSettings>({ encryptionKey: 'test-key' });
|
||||||
|
let globalConfig: GlobalConfig;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
globalConfig = mock<GlobalConfig>({
|
||||||
|
userManagement: {
|
||||||
|
jwtSecret: '',
|
||||||
|
jwtSessionDurationHours: 168,
|
||||||
|
jwtRefreshTimeoutHours: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('secret initialization', () => {
|
describe('secret initialization', () => {
|
||||||
it('should read the secret from config, when set', () => {
|
it('should read the secret from config, when set', () => {
|
||||||
config.set('userManagement.jwtSecret', jwtSecret);
|
globalConfig.userManagement.jwtSecret = jwtSecret;
|
||||||
const jwtService = new JwtService(instanceSettings);
|
const jwtService = new JwtService(instanceSettings, globalConfig);
|
||||||
expect(jwtService.jwtSecret).toEqual(jwtSecret);
|
expect(jwtService.jwtSecret).toEqual(jwtSecret);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should derive the secret from encryption key when not set in config', () => {
|
it('should derive the secret from encryption key when not set in config', () => {
|
||||||
config.set('userManagement.jwtSecret', '');
|
globalConfig.userManagement.jwtSecret = '';
|
||||||
const jwtService = new JwtService(instanceSettings);
|
const jwtService = new JwtService(instanceSettings, globalConfig);
|
||||||
expect(jwtService.jwtSecret).toEqual(
|
expect(jwtService.jwtSecret).toEqual(
|
||||||
'e9e2975005eddefbd31b2c04a0b0f2d9c37d9d718cf3676cddf76d65dec555cb',
|
'e9e2975005eddefbd31b2c04a0b0f2d9c37d9d718cf3676cddf76d65dec555cb',
|
||||||
);
|
);
|
||||||
@@ -35,8 +43,7 @@ describe('JwtService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('with a secret set', () => {
|
describe('with a secret set', () => {
|
||||||
config.set('userManagement.jwtSecret', jwtSecret);
|
let jwtService: JwtService;
|
||||||
const jwtService = new JwtService(instanceSettings);
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
jest.useFakeTimers().setSystemTime(new Date(iat * 1000));
|
jest.useFakeTimers().setSystemTime(new Date(iat * 1000));
|
||||||
@@ -44,6 +51,11 @@ describe('JwtService', () => {
|
|||||||
|
|
||||||
afterAll(() => jest.useRealTimers());
|
afterAll(() => jest.useRealTimers());
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
globalConfig.userManagement.jwtSecret = jwtSecret;
|
||||||
|
jwtService = new JwtService(instanceSettings, globalConfig);
|
||||||
|
});
|
||||||
|
|
||||||
it('should sign', () => {
|
it('should sign', () => {
|
||||||
const token = jwtService.sign(payload);
|
const token = jwtService.sign(payload);
|
||||||
expect(token).toEqual(signedToken);
|
expect(token).toEqual(signedToken);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const securitySchema = mock<OpenAPIV3.ApiKeySecurityScheme>({
|
|||||||
name: 'X-N8N-API-KEY',
|
name: 'X-N8N-API-KEY',
|
||||||
});
|
});
|
||||||
|
|
||||||
const jwtService = new JwtService(instanceSettings);
|
const jwtService = new JwtService(instanceSettings, mock());
|
||||||
|
|
||||||
let userRepository: UserRepository;
|
let userRepository: UserRepository;
|
||||||
let apiKeyRepository: ApiKeyRepository;
|
let apiKeyRepository: ApiKeyRepository;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { Service } from '@n8n/di';
|
import { GlobalConfig } from '@n8n/config';
|
||||||
|
import { Container, Service } from '@n8n/di';
|
||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { InstanceSettings } from 'n8n-core';
|
import { InstanceSettings } from 'n8n-core';
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class JwtService {
|
export class JwtService {
|
||||||
readonly jwtSecret = config.getEnv('userManagement.jwtSecret');
|
jwtSecret: string = '';
|
||||||
|
|
||||||
constructor({ encryptionKey }: InstanceSettings) {
|
constructor({ encryptionKey }: InstanceSettings, globalConfig: GlobalConfig) {
|
||||||
this.jwtSecret = config.getEnv('userManagement.jwtSecret');
|
this.jwtSecret = globalConfig.userManagement.jwtSecret;
|
||||||
if (!this.jwtSecret) {
|
if (!this.jwtSecret) {
|
||||||
// If we don't have a JWT secret set, generate one based on encryption key.
|
// If we don't have a JWT secret set, generate one based on encryption key.
|
||||||
// For a key off every other letter from encryption key
|
// For a key off every other letter from encryption key
|
||||||
@@ -20,7 +19,7 @@ export class JwtService {
|
|||||||
baseKey += encryptionKey[i];
|
baseKey += encryptionKey[i];
|
||||||
}
|
}
|
||||||
this.jwtSecret = createHash('sha256').update(baseKey).digest('hex');
|
this.jwtSecret = createHash('sha256').update(baseKey).digest('hex');
|
||||||
config.set('userManagement.jwtSecret', this.jwtSecret);
|
Container.get(GlobalConfig).userManagement.jwtSecret = this.jwtSecret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { LicenseMocker } from '@test-integration/license';
|
|||||||
|
|
||||||
import { PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
|
import { PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
|
||||||
import type { SetupProps, TestServer } from '../types';
|
import type { SetupProps, TestServer } from '../types';
|
||||||
|
import { GlobalConfig } from '@n8n/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin to prefix a path segment into a request URL pathname.
|
* Plugin to prefix a path segment into a request URL pathname.
|
||||||
@@ -127,7 +128,7 @@ export const setupTestServer = ({
|
|||||||
if (modules) await testModules.loadModules(modules);
|
if (modules) await testModules.loadModules(modules);
|
||||||
await testDb.init();
|
await testDb.init();
|
||||||
|
|
||||||
config.set('userManagement.jwtSecret', 'My JWT secret');
|
Container.get(GlobalConfig).userManagement.jwtSecret = 'My JWT secret';
|
||||||
config.set('userManagement.isInstanceOwnerSetUp', true);
|
config.set('userManagement.isInstanceOwnerSetUp', true);
|
||||||
|
|
||||||
testServer.license.mock(Container.get(License));
|
testServer.license.mock(Container.get(License));
|
||||||
|
|||||||
Reference in New Issue
Block a user