From b4a384a4a9e570618a58924582fb3998006b8722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lud=C4=9Bk=20Nov=C3=BD?= <13610612+ludeknovy@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:36:12 +0200 Subject: [PATCH 1/4] Add role migration check in user initialization The commit includes adding a new query for role migration and incorporating it into the user initialization process to prevent user creation before migration finishes. Also, a custom error, MigrationNotFinishedException, has been added for better error handling regarding the migration completion status. This will help in ensuring accurate role assignment to new users. --- .../controllers/auth/init-user-controller.ts | 32 ++++++++++++------- .../migration-not-finished-exception.ts | 9 ++++++ src/server/queries/auth.ts | 7 ++++ 3 files changed, 36 insertions(+), 12 deletions(-) create mode 100644 src/server/errors/migration-not-finished-exception.ts diff --git a/src/server/controllers/auth/init-user-controller.ts b/src/server/controllers/auth/init-user-controller.ts index 67bd30ee..4410adf1 100644 --- a/src/server/controllers/auth/init-user-controller.ts +++ b/src/server/controllers/auth/init-user-controller.ts @@ -2,21 +2,29 @@ import boom = require("boom") import { Request, Response, NextFunction } from "express" import { db } from "../../../db/db" import { AllowedRoles } from "../../middleware/authorization-middleware" -import { getUsers } from "../../queries/auth" +import { getUsers, getRoleMigration } from "../../queries/auth" import { createNewUserController } from "../users/create-new-user-controller" +import { MigrationNotFinishedException } from "../../errors/migration-not-finished-exception" export const initUserController = async (req: Request, res: Response, next: NextFunction) => { - try { - const users = await db.manyOrNone(getUsers()) - if (users && users.length > 0) { - return next(boom.forbidden("User was already initialized")) - } - req.body.role = AllowedRoles.Admin - await createNewUserController(req, res, next) - + try { + const roleMigration = await db.oneOrNone(getRoleMigration()) + console.log(roleMigration) + if (!roleMigration) { + throw MigrationNotFinishedException + } + const users = await db.manyOrNone(getUsers()) + if (users && users.length > 0) { + return next(boom.forbidden("User was already initialized")) + } + req.body.role = AllowedRoles.Admin + await createNewUserController(req, res, next) - } catch(error) { - return next(error) - } + } catch(error) { + if (error.code === "42P01" || error instanceof MigrationNotFinishedException) { + return next(boom.preconditionRequired("Migrations were not finished, please try it again later.")) + } + return next(error) + } } diff --git a/src/server/errors/migration-not-finished-exception.ts b/src/server/errors/migration-not-finished-exception.ts new file mode 100644 index 00000000..9f324ffe --- /dev/null +++ b/src/server/errors/migration-not-finished-exception.ts @@ -0,0 +1,9 @@ +export class MigrationNotFinishedException extends Error { + constructor (message) { + super(message) + + this.name = this.constructor.name + + Error.captureStackTrace(this, this.constructor) + } +} diff --git a/src/server/queries/auth.ts b/src/server/queries/auth.ts index 12dc45e7..45cab7a5 100644 --- a/src/server/queries/auth.ts +++ b/src/server/queries/auth.ts @@ -32,3 +32,10 @@ export const updatePassword = (id, password) => { values: [id, password], } } + +export const getRoleMigration = () => { + return { + text: "SELECT * FROM pgmigrations WHERE name = $1", + values: ["1643273224321_role"], + } +} From 742f0d6457ee08e36bc17c63fd4af9c5fd4cd73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lud=C4=9Bk=20Nov=C3=BD?= <13610612+ludeknovy@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:47:32 +0200 Subject: [PATCH 2/4] Update exception handling in initUserController Refactored the initUserController by improving exception handling for migration not finished scenarios. Updated the test cases to include checks for unfinished migrations and removed a redundant console.log statement. This will enhance the error handling capability of the initialization process, making it more robust and informative to the end-user. --- .../auth/init-user-controller.spec.ts | 73 +++++++++++-------- .../controllers/auth/init-user-controller.ts | 3 +- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/server/controllers/auth/init-user-controller.spec.ts b/src/server/controllers/auth/init-user-controller.spec.ts index fa9b293e..3f992a5d 100644 --- a/src/server/controllers/auth/init-user-controller.spec.ts +++ b/src/server/controllers/auth/init-user-controller.spec.ts @@ -6,37 +6,50 @@ import { AllowedRoles } from "../../middleware/authorization-middleware" jest.mock("../../../db/db") const mockResponse = () => { - const res: Partial = {} - res.send = jest.fn().mockReturnValue(res) - res.status = jest.fn().mockReturnValue(res) - return res + const res: Partial = {} + res.send = jest.fn().mockReturnValue(res) + res.status = jest.fn().mockReturnValue(res) + return res } describe("initUserController", () => { - it("should call createNewUserController if no user exists", async function () { - const querySpy = jest.spyOn(require("../users/create-new-user-controller"), "createNewUserController"); - (db.manyOrNone as any).mockResolvedValue([]) - const nextFunction: NextFunction = jest.fn() - const response = mockResponse() - const request = { body: { username: "test", password: "123" } } - await initUserController( - request as unknown as IGetUserAuthInfoRequest, - response as unknown as Response, nextFunction) - expect(querySpy).toHaveBeenCalledTimes(1) - expect(request.body).toEqual({ username: "test", password: "123", role: AllowedRoles.Admin }) - }) - it("should return an error if user already exists", async function () { - const querySpy = jest.spyOn(require("boom"), "forbidden"); - - (db.manyOrNone as any).mockResolvedValue(["test"]) - const nextFunction: NextFunction = jest.fn() - const response = mockResponse() - const request = {} - await initUserController( - request as unknown as IGetUserAuthInfoRequest, - response as unknown as Response, nextFunction) - expect(nextFunction).toHaveBeenCalledTimes(1) - expect(querySpy).toHaveBeenCalledTimes(1) - expect(querySpy).toHaveBeenCalledWith("User was already initialized") - }) + it("should call createNewUserController if no user exists", async function () { + const querySpy = jest.spyOn(require("../users/create-new-user-controller"), "createNewUserController"); + (db.oneOrNone as any).mockResolvedValueOnce({}); // migration has finished + (db.manyOrNone as any).mockResolvedValue([]) + const nextFunction: NextFunction = jest.fn() + const response = mockResponse() + const request = { body: { username: "test", password: "123" } } + await initUserController( + request as unknown as IGetUserAuthInfoRequest, + response as unknown as Response, nextFunction) + expect(querySpy).toHaveBeenCalledTimes(1) + expect(request.body).toEqual({ username: "test", password: "123", role: AllowedRoles.Admin }) + }) + it("should return an error if user already exists", async function () { + const querySpy = jest.spyOn(require("boom"), "forbidden"); + (db.oneOrNone as any).mockResolvedValueOnce({}); // migration has finished + (db.manyOrNone as any).mockResolvedValue(["test"]) + const nextFunction: NextFunction = jest.fn() + const response = mockResponse() + const request = {} + await initUserController( + request as unknown as IGetUserAuthInfoRequest, + response as unknown as Response, nextFunction) + expect(nextFunction).toHaveBeenCalledTimes(1) + expect(querySpy).toHaveBeenCalledTimes(1) + expect(querySpy).toHaveBeenCalledWith("User was already initialized") + }) + it("should throw exception when migrations have not finished", async () => { + const spy = jest.spyOn(require("boom"), "preconditionRequired") + const nextFunction: NextFunction = jest.fn() + const response = mockResponse() + const request = {} + await initUserController( + request as unknown as IGetUserAuthInfoRequest, + response as unknown as Response, nextFunction) + expect(nextFunction).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith("Migrations were not finished, please try it again later.") + }) }) diff --git a/src/server/controllers/auth/init-user-controller.ts b/src/server/controllers/auth/init-user-controller.ts index 4410adf1..7c44ceb1 100644 --- a/src/server/controllers/auth/init-user-controller.ts +++ b/src/server/controllers/auth/init-user-controller.ts @@ -9,9 +9,8 @@ import { MigrationNotFinishedException } from "../../errors/migration-not-finish export const initUserController = async (req: Request, res: Response, next: NextFunction) => { try { const roleMigration = await db.oneOrNone(getRoleMigration()) - console.log(roleMigration) if (!roleMigration) { - throw MigrationNotFinishedException + throw new MigrationNotFinishedException("role migration has not finished") } const users = await db.manyOrNone(getUsers()) if (users && users.length > 0) { From 73076fdb807dc9631b46789328df41082d2f8f27 Mon Sep 17 00:00:00 2001 From: Ludek Novy <13610612+ludeknovy@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:39:05 +0200 Subject: [PATCH 3/4] fix: upgrade pg from 8.11.3 to 8.11.4 (#322) Snyk has created this PR to upgrade pg from 8.11.3 to 8.11.4. See this package in npm: https://www.npmjs.com/package/pg See this project in Snyk: https://app.snyk.io/org/ludeknovy/project/6001874a-311f-46e3-8e8c-e69318c103b2?utm_source=github&utm_medium=referral&page=upgrade-pr Co-authored-by: snyk-bot --- package-lock.json | 34 ++++++++++++++++------------------ package.json | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0a7e7fc..af15521a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "moment": "^2.29.4", "multer": "^1.4.5-lts.1", "node-pg-migrate": "^6.2.2", - "pg": "^8.11.3", + "pg": "^8.11.4", "pg-promise": "^10.15.4", "uuid": "^9.0.1", "winston": "^3.12.0", @@ -9454,15 +9454,13 @@ } }, "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "version": "8.11.4", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.4.tgz", + "integrity": "sha512-pWb7JKPxGk1UFbtq7jQ0m3IfPpb7LLACCEyN8/u9DYEom+Q/BSKy+4TRl4+Hh003AOYhppB/z+QK87/hx/bk0w==", "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "pg-connection-string": "^2.6.3", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -9488,9 +9486,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -9509,9 +9507,9 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", "peerDependencies": { "pg": ">=8.0" } @@ -9556,9 +9554,9 @@ } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" }, "node_modules/pg-types": { "version": "2.2.0", diff --git a/package.json b/package.json index 8c707e9b..e1de1fe5 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "moment": "^2.29.4", "multer": "^1.4.5-lts.1", "node-pg-migrate": "^6.2.2", - "pg": "^8.11.3", + "pg": "^8.11.4", "pg-promise": "^10.15.4", "uuid": "^9.0.1", "winston": "^3.12.0", From 62321c6191e601fa46b6542a03343e4cdca4820b Mon Sep 17 00:00:00 2001 From: Ludek Novy <13610612+ludeknovy@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:15:42 +0200 Subject: [PATCH 4/4] fix: upgrade pg from 8.11.4 to 8.11.5 (#323) Snyk has created this PR to upgrade pg from 8.11.4 to 8.11.5. See this package in npm: https://www.npmjs.com/package/pg See this project in Snyk: https://app.snyk.io/org/ludeknovy/project/6001874a-311f-46e3-8e8c-e69318c103b2?utm_source=github&utm_medium=referral&page=upgrade-pr Co-authored-by: snyk-bot --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index af15521a..f915400d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "moment": "^2.29.4", "multer": "^1.4.5-lts.1", "node-pg-migrate": "^6.2.2", - "pg": "^8.11.4", + "pg": "^8.11.5", "pg-promise": "^10.15.4", "uuid": "^9.0.1", "winston": "^3.12.0", @@ -9454,11 +9454,11 @@ } }, "node_modules/pg": { - "version": "8.11.4", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.4.tgz", - "integrity": "sha512-pWb7JKPxGk1UFbtq7jQ0m3IfPpb7LLACCEyN8/u9DYEom+Q/BSKy+4TRl4+Hh003AOYhppB/z+QK87/hx/bk0w==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "dependencies": { - "pg-connection-string": "^2.6.3", + "pg-connection-string": "^2.6.4", "pg-pool": "^3.6.2", "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", diff --git a/package.json b/package.json index e1de1fe5..9bedbf33 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "moment": "^2.29.4", "multer": "^1.4.5-lts.1", "node-pg-migrate": "^6.2.2", - "pg": "^8.11.4", + "pg": "^8.11.5", "pg-promise": "^10.15.4", "uuid": "^9.0.1", "winston": "^3.12.0",