mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-07 02:47:32 -08:00
163 lines
4.7 KiB
TypeScript
163 lines
4.7 KiB
TypeScript
import { mock } from 'jest-mock-extended';
|
|
|
|
import config from '@/config';
|
|
import { AUTH_COOKIE_NAME, Time } from '@/constants';
|
|
import { License } from '@/License';
|
|
import { issueJWT } from '@/auth/jwt';
|
|
import { refreshExpiringCookie } from '@/middlewares';
|
|
|
|
import { mockInstance } from '../../shared/mocking';
|
|
|
|
import type { AuthenticatedRequest } from '@/requests';
|
|
import type { NextFunction, Response } from 'express';
|
|
import type { User } from '@/databases/entities/User';
|
|
|
|
mockInstance(License);
|
|
|
|
jest.useFakeTimers();
|
|
|
|
describe('refreshExpiringCookie', () => {
|
|
const oldDuration = config.getEnv('userManagement.jwtSessionDurationHours');
|
|
const oldTimeout = config.getEnv('userManagement.jwtRefreshTimeoutHours');
|
|
let mockUser: User;
|
|
|
|
beforeEach(() => {
|
|
mockUser = mock<User>({ password: 'passwordHash' });
|
|
});
|
|
|
|
afterEach(() => {
|
|
config.set('userManagement.jwtSessionDuration', oldDuration);
|
|
config.set('userManagement.jwtRefreshTimeoutHours', oldTimeout);
|
|
});
|
|
|
|
it('does not do anything if the user is not authorized', async () => {
|
|
const req = mock<AuthenticatedRequest>();
|
|
const res = mock<Response>({ cookie: jest.fn() });
|
|
const next = jest.fn();
|
|
|
|
await refreshExpiringCookie(req, res, next);
|
|
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).not.toHaveBeenCalled();
|
|
});
|
|
|
|
describe('with N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS=-1', () => {
|
|
it('does not refresh the cookie, ever', async () => {
|
|
config.set('userManagement.jwtSessionDurationHours', 1);
|
|
config.set('userManagement.jwtRefreshTimeoutHours', -1);
|
|
const { token } = issueJWT(mockUser);
|
|
|
|
jest.advanceTimersByTime(1000 * 60 * 55); /* 55 minutes */
|
|
|
|
const req = mock<AuthenticatedRequest>({
|
|
cookies: { [AUTH_COOKIE_NAME]: token },
|
|
user: mockUser,
|
|
});
|
|
const res = mock<Response>({ cookie: jest.fn() });
|
|
const next = jest.fn();
|
|
await refreshExpiringCookie(req, res, next);
|
|
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('with N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS=0', () => {
|
|
let token: string;
|
|
let req: AuthenticatedRequest;
|
|
let res: Response;
|
|
let next: NextFunction;
|
|
|
|
beforeEach(() => {
|
|
// ARRANGE
|
|
config.set('userManagement.jwtSessionDurationHours', 1);
|
|
config.set('userManagement.jwtRefreshTimeoutHours', 0);
|
|
token = issueJWT(mockUser).token;
|
|
|
|
req = mock<AuthenticatedRequest>({
|
|
cookies: { [AUTH_COOKIE_NAME]: token },
|
|
user: mockUser,
|
|
});
|
|
res = mock<Response>({ cookie: jest.fn() });
|
|
next = jest.fn();
|
|
});
|
|
|
|
it('does not refresh the cookie when more than 1/4th of time is left', async () => {
|
|
// ARRANGE
|
|
jest.advanceTimersByTime(44 * Time.minutes.toMilliseconds); /* 44 minutes */
|
|
|
|
// ACT
|
|
await refreshExpiringCookie(req, res, next);
|
|
|
|
// ASSERT
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('refreshes the cookie when 1/4th of time is left', async () => {
|
|
// ARRANGE
|
|
jest.advanceTimersByTime(46 * Time.minutes.toMilliseconds); /* 46 minutes */
|
|
|
|
// ACT
|
|
await refreshExpiringCookie(req, res, next);
|
|
|
|
// ASSERT
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('with N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS=50', () => {
|
|
const jwtSessionDurationHours = 51;
|
|
let token: string;
|
|
let req: AuthenticatedRequest;
|
|
let res: Response;
|
|
let next: NextFunction;
|
|
|
|
// ARRANGE
|
|
beforeEach(() => {
|
|
config.set('userManagement.jwtSessionDurationHours', jwtSessionDurationHours);
|
|
config.set('userManagement.jwtRefreshTimeoutHours', 50);
|
|
|
|
token = issueJWT(mockUser).token;
|
|
req = mock<AuthenticatedRequest>({
|
|
cookies: { [AUTH_COOKIE_NAME]: token },
|
|
user: mockUser,
|
|
});
|
|
res = mock<Response>({ cookie: jest.fn() });
|
|
next = jest.fn();
|
|
});
|
|
|
|
it('does not do anything if the cookie is still valid', async () => {
|
|
// ARRANGE
|
|
// cookie has 50.5 hours to live: 51 - 0.5
|
|
jest.advanceTimersByTime(30 * Time.minutes.toMilliseconds);
|
|
|
|
// ACT
|
|
await refreshExpiringCookie(req, res, next);
|
|
|
|
// ASSERT
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('refreshes the cookie if it has less than 50 hours to live', async () => {
|
|
// ARRANGE
|
|
// cookie has 49.5 hours to live: 51 - 1.5
|
|
jest.advanceTimersByTime(1.5 * Time.hours.toMilliseconds);
|
|
|
|
// ACT
|
|
await refreshExpiringCookie(req, res, next);
|
|
|
|
// ASSERT
|
|
expect(next).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).toHaveBeenCalledTimes(1);
|
|
expect(res.cookie).toHaveBeenCalledWith(AUTH_COOKIE_NAME, expect.any(String), {
|
|
httpOnly: true,
|
|
maxAge: jwtSessionDurationHours * Time.hours.toMilliseconds,
|
|
sameSite: 'lax',
|
|
});
|
|
});
|
|
});
|
|
});
|