From b7bb09a770cbc644079a1794f8b5aa090ad7df51 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Fri, 5 May 2023 21:52:30 +0200 Subject: [PATCH] test: session.middleware --- package.json | 2 + pnpm-lock.yaml | 96 ++++++++++++++++++- .../utils/__tests__/page-helpers.test.ts | 28 ++++++ .../middlewares/session.middleware.test.ts | 22 +++++ src/server/services/auth/auth.service.ts | 15 +-- 5 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 src/client/utils/__tests__/page-helpers.test.ts create mode 100644 src/server/middlewares/session.middleware.test.ts diff --git a/package.json b/package.json index ea028bc2..16ac9a72 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "@types/react": "18.0.28", "@types/react-dom": "18.0.11", "@types/semver": "^7.3.12", + "@types/supertest": "^2.0.12", "@types/testing-library__jest-dom": "^5.14.5", "@types/uuid": "^9.0.1", "@types/validator": "^13.7.14", @@ -130,6 +131,7 @@ "next-router-mock": "^0.9.2", "nodemon": "^2.0.21", "prettier": "^2.8.4", + "supertest": "^6.3.3", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "5.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9335fa0..cdf8a6e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -219,6 +219,9 @@ devDependencies: '@types/semver': specifier: ^7.3.12 version: 7.3.13 + '@types/supertest': + specifier: ^2.0.12 + version: 2.0.12 '@types/testing-library__jest-dom': specifier: ^5.14.5 version: 5.14.5 @@ -306,6 +309,9 @@ devDependencies: prettier: specifier: ^2.8.4 version: 2.8.4 + supertest: + specifier: ^6.3.3 + version: 6.3.3 ts-jest: specifier: ^29.0.3 version: 29.0.5(@babel/core@7.21.3)(esbuild@0.16.17)(jest@29.5.0)(typescript@5.0.2) @@ -2384,6 +2390,10 @@ packages: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} dev: true + /@types/cookiejar@2.1.2: + resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==} + dev: true + /@types/debug@4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: @@ -2579,6 +2589,19 @@ packages: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true + /@types/superagent@4.1.17: + resolution: {integrity: sha512-FFK/rRjNy24U6J1BvQkaNWu2ohOIF/kxRQXRsbT141YQODcOcZjzlcc4DGdI2SkTa0rhmF+X14zu6ICjCGIg+w==} + dependencies: + '@types/cookiejar': 2.1.2 + '@types/node': 18.15.3 + dev: true + + /@types/supertest@2.0.12: + resolution: {integrity: sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==} + dependencies: + '@types/superagent': 4.1.17 + dev: true + /@types/testing-library__jest-dom@5.14.5: resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==} dependencies: @@ -3082,6 +3105,10 @@ packages: get-intrinsic: 1.2.0 dev: true + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: true + /asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} dependencies: @@ -3575,6 +3602,10 @@ packages: engines: {node: '>= 12.0.0'} dev: true + /component-emitter@1.3.0: + resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} + dev: true + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -3627,6 +3658,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + dev: true + /copy-anything@3.0.3: resolution: {integrity: sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==} engines: {node: '>=12.13'} @@ -3848,6 +3883,13 @@ packages: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false + /dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dev: true + /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5068,6 +5110,10 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + dev: true + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -5185,6 +5231,15 @@ packages: fetch-blob: 3.2.0 dev: false + /formidable@2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + dependencies: + dezalgo: 1.0.4 + hexoid: 1.0.0 + once: 1.4.0 + qs: 6.11.0 + dev: true + /forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -5493,6 +5548,11 @@ packages: resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} dev: true + /hexoid@1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + dev: true + /hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} dependencies: @@ -6916,7 +6976,6 @@ packages: /methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - dev: false /micromark-core-commonmark@1.0.6: resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==} @@ -7191,6 +7250,12 @@ packages: hasBin: true dev: false + /mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: true + /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -7965,7 +8030,6 @@ packages: engines: {node: '>=0.6'} dependencies: side-channel: 1.0.4 - dev: false /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -8753,6 +8817,24 @@ packages: resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==} dev: false + /superagent@8.0.9: + resolution: {integrity: sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==} + engines: {node: '>=6.4.0 <13 || >=14'} + dependencies: + component-emitter: 1.3.0 + cookiejar: 2.1.4 + debug: 4.3.4 + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 2.1.2 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.11.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + dev: true + /superjson@1.12.2: resolution: {integrity: sha512-ugvUo9/WmvWOjstornQhsN/sR9mnGtWGYeTxFuqLb4AiT4QdUavjGFRALCPKWWnAiUJ4HTpytj5e0t5HoMRkXg==} engines: {node: '>=10'} @@ -8760,6 +8842,16 @@ packages: copy-anything: 3.0.3 dev: false + /supertest@6.3.3: + resolution: {integrity: sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==} + engines: {node: '>=6.4.0'} + dependencies: + methods: 1.1.2 + superagent: 8.0.9 + transitivePeerDependencies: + - supports-color + dev: true + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} diff --git a/src/client/utils/__tests__/page-helpers.test.ts b/src/client/utils/__tests__/page-helpers.test.ts new file mode 100644 index 00000000..9aeae77d --- /dev/null +++ b/src/client/utils/__tests__/page-helpers.test.ts @@ -0,0 +1,28 @@ +import { getAuthedPageProps } from '../page-helpers'; + +describe('getAuthedPageProps', () => { + it('should redirect to /login if there is no user id in session', async () => { + // arrange + const ctx = { req: { session: {} } }; + + // act + // @ts-expect-error - we're passing in a partial context + const { redirect } = await getAuthedPageProps(ctx); + + // assert + expect(redirect.destination).toBe('/login'); + expect(redirect.permanent).toBe(false); + }); + + it('should return props if there is a user id in session', async () => { + // arrange + const ctx = { req: { session: { userId: '123' } } }; + + // act + // @ts-expect-error - we're passing in a partial context + const { props } = await getAuthedPageProps(ctx); + + // assert + expect(props).toEqual({}); + }); +}); diff --git a/src/server/middlewares/session.middleware.test.ts b/src/server/middlewares/session.middleware.test.ts new file mode 100644 index 00000000..d0238932 --- /dev/null +++ b/src/server/middlewares/session.middleware.test.ts @@ -0,0 +1,22 @@ +import request from 'supertest'; +import express from 'express'; +import { sessionMiddleware } from './session.middleware'; + +describe('Session Middleware', () => { + it('should redirect to /login if there is no user id in session', async () => { + // arrange + let session; + const app = express(); + app.use(sessionMiddleware); + app.use('/test', (req, res) => { + session = req.session; + res.send('ok'); + }); + + // act + await request(app).get('/test'); + + // assert + expect(session).toHaveProperty('cookie'); + }); +}); diff --git a/src/server/services/auth/auth.service.ts b/src/server/services/auth/auth.service.ts index ff3c97f4..6391b148 100644 --- a/src/server/services/auth/auth.service.ts +++ b/src/server/services/auth/auth.service.ts @@ -7,7 +7,6 @@ import { AuthQueries } from '@/server/queries/auth/auth.queries'; import { Context } from '@/server/context'; import { getConfig } from '../../core/TipiConfig'; import TipiCache from '../../core/TipiCache'; -import { Logger } from '../../core/Logger'; import { fileExists, unlinkFile } from '../../common/fs.helpers'; import { decrypt, encrypt } from '../../utils/encryption'; @@ -346,12 +345,14 @@ export class AuthServiceClass { * @param {number} userId - The user ID */ private destroyAllSessionsByUserId = async (userId: number) => { - await TipiCache.getByPrefix(`session:${userId}:`).then((sessions) => { - sessions.forEach((session) => { - TipiCache.del(session.key).then(() => Logger.info('Session deleted')); - TipiCache.del(`tipi:${session.val}`).then(() => Logger.info('Session key deleted')); - }); - }); + const sessions = await TipiCache.getByPrefix(`session:${userId}:`); + + await Promise.all( + sessions.map(async (session) => { + await TipiCache.del(session.key); + await TipiCache.del(`tipi:${session.val}`); + }), + ); }; public changePassword = async (params: { currentPassword: string; newPassword: string; userId: number }) => {