From 1362a1c09d40389ad51242d0ae833b98a96fa5fc Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:50:40 +0530 Subject: [PATCH 1/9] added one put api for user intro --- controllers/users.js | 4 ++ routes/users.js | 2 + test/integration/users.test.js | 99 ++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/controllers/users.js b/controllers/users.js index 6c8cb3a0d..2a9989a0f 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -803,6 +803,10 @@ const rejectProfileDiff = async (req, res) => { }; const addUserIntro = async (req, res) => { + if (req.params.userId !== req.userData.id) { + return res.status(403).json({ message: "Unauthorized access" }); + } + try { const rawData = req.body; const joinData = await userQuery.getJoinData(req.userData.id); diff --git a/routes/users.js b/routes/users.js index 94d301cda..999c4d2c8 100644 --- a/routes/users.js +++ b/routes/users.js @@ -12,6 +12,7 @@ const { authorizeAndAuthenticate } = require("../middlewares/authorizeUsersAndSe const ROLES = require("../constants/roles"); const { Services } = require("../constants/bot"); const authenticateProfile = require("../middlewares/authenticateProfile"); +const { devFlagMiddleware } = require("../middlewares/devFlag"); router.post("/", authorizeAndAuthenticate([ROLES.SUPERUSER], [Services.CRON_JOB_HANDLER]), users.markUnverified); router.post("/update-in-discord", authenticate, authorizeRoles([SUPERUSER]), users.setInDiscordScript); @@ -36,6 +37,7 @@ router.patch( router.get("/:username", users.getUser); router.get("/:userId/intro", authenticate, authorizeRoles([SUPERUSER]), users.getUserIntro); router.put("/self/intro", authenticate, userValidator.validateJoinData, users.addUserIntro); +router.put("/:userId/intro", devFlagMiddleware, authenticate, userValidator.validateJoinData, users.addUserIntro); router.get("/:id/skills", users.getUserSkills); router.get("/:id/badges", getUserBadges); router.patch( diff --git a/test/integration/users.test.js b/test/integration/users.test.js index 4c7143b8d..9043dc14c 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -1579,6 +1579,105 @@ describe("Users", function () { }); }); + describe("PUT /users/:userId/intro", function () { + let userStatusData; + + beforeEach(async function () { + await userStatusModel.updateUserStatus(userId, userStatusDataAfterSignup); + const updateStatus = await userStatusModel.updateUserStatus(userId, userStatusDataAfterFillingJoinSection); + userStatusData = (await firestore.collection("usersStatus").doc(updateStatus.id).get()).data(); + }); + + it("should return 409 if the data already present", function (done) { + addJoinData(joinData(userId)[3]); + chai + .request(app) + .put(`/users/${userId}/intro?dev=true`) + .set("Cookie", `${cookieName}=${jwt}`) + .send(joinData(userId)[3]) + .end((err, res) => { + if (err) { + return done(err); + } + expect(res).to.have.status(409); + expect(res.body).to.be.a("object"); + expect(res.body.message).to.equal("User data is already present!"); + return done(); + }); + }); + + it("Should store the info in db", function (done) { + chai + .request(app) + .put(`/users/${userId}/intro?dev=true`) + .set("Cookie", `${cookieName}=${jwt}`) + .send(joinData()[2]) + .end((err, res) => { + if (err) { + return done(err); + } + expect(res).to.have.status(201); + expect(res.body).to.be.a("object"); + expect(res.body.message).to.equal("User join data and newstatus data added and updated successfully"); + expect(userStatusData).to.have.own.property("currentStatus"); + expect(userStatusData).to.have.own.property("monthlyHours"); + expect(userStatusData.currentStatus.state).to.equal("ONBOARDING"); + expect(userStatusData.monthlyHours.committed).to.equal(40); + return done(); + }); + }); + + it("Should return 401 for unauthorized request", function (done) { + chai + .request(app) + .put(`/users/${userId}/intro?dev=true`) + .set("Cookie", `${cookieName}=""`) + .send(joinData()[2]) + .end((err, res) => { + if (err) { + return done(err); + } + expect(res).to.have.status(401); + expect(res.body).to.be.a("object"); + return done(); + }); + }); + + it("Should return 400 for invalid Data", function (done) { + chai + .request(app) + .put(`/users/${userId}/intro?dev=true`) + .set("Cookie", `${cookieName}=${jwt}`) + .send(joinData()[1]) + .end((err, res) => { + if (err) { + return done(err); + } + expect(res).to.have.status(400); + expect(res.body).to.be.a("object"); + expect(res.body.message).to.be.equal('"firstName" is required'); + return done(); + }); + }); + + it("Should return 403 for Unauthorized access", function (done) { + chai + .request(app) + .put(`/users/${userId}/intro?dev=true`) + .set("Cookie", `${cookieName}=${jwt}`) + .send(joinData()[1]) + .end((err, res) => { + if (err) { + return done(err); + } + expect(res).to.have.status(400); + expect(res.body).to.be.a("object"); + expect(res.body.message).to.be.equal('"firstName" is required'); + return done(); + }); + }); + }); + describe("PATCH /users/rejectDiff", function () { let profileDiffsId; From d480bbb43c598130888e7e4fe7c8b5c41e377497 Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:07:55 +0530 Subject: [PATCH 2/9] route checking fix --- controllers/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/users.js b/controllers/users.js index 2a9989a0f..6060210b2 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -803,7 +803,7 @@ const rejectProfileDiff = async (req, res) => { }; const addUserIntro = async (req, res) => { - if (req.params.userId !== req.userData.id) { + if (req.path !== "/self/intro" && req.params.userId !== req.userData.id) { return res.status(403).json({ message: "Unauthorized access" }); } From 472713971892eec717973ca2b93ff9a441d6d9c2 Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Wed, 11 Dec 2024 08:44:41 +0530 Subject: [PATCH 3/9] deprecate message comment --- routes/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/users.js b/routes/users.js index 999c4d2c8..a3e0046e9 100644 --- a/routes/users.js +++ b/routes/users.js @@ -36,7 +36,7 @@ router.patch( ); router.get("/:username", users.getUser); router.get("/:userId/intro", authenticate, authorizeRoles([SUPERUSER]), users.getUserIntro); -router.put("/self/intro", authenticate, userValidator.validateJoinData, users.addUserIntro); +router.put("/self/intro", authenticate, userValidator.validateJoinData, users.addUserIntro); // This route is being deprecated soon, please use alternate available route `/users/:userId/intro`. router.put("/:userId/intro", devFlagMiddleware, authenticate, userValidator.validateJoinData, users.addUserIntro); router.get("/:id/skills", users.getUserSkills); router.get("/:id/badges", getUserBadges); From f46422e03ada7507d88a93843e6ca629454239a7 Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:46:23 +0530 Subject: [PATCH 4/9] fixed test cases --- controllers/users.js | 2 +- test/integration/users.test.js | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/controllers/users.js b/controllers/users.js index 6060210b2..2e6df3d51 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -804,7 +804,7 @@ const rejectProfileDiff = async (req, res) => { const addUserIntro = async (req, res) => { if (req.path !== "/self/intro" && req.params.userId !== req.userData.id) { - return res.status(403).json({ message: "Unauthorized access" }); + return res.boom.forbidden("Forbidden access"); } try { diff --git a/test/integration/users.test.js b/test/integration/users.test.js index 9043dc14c..3572af9c6 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -1627,7 +1627,7 @@ describe("Users", function () { }); }); - it("Should return 401 for unauthorized request", function (done) { + it("Should return 401 for Unauthenticated User Request", function (done) { chai .request(app) .put(`/users/${userId}/intro?dev=true`) @@ -1639,6 +1639,7 @@ describe("Users", function () { } expect(res).to.have.status(401); expect(res.body).to.be.a("object"); + expect(res.body.message).to.equal("Unauthenticated User"); return done(); }); }); @@ -1660,19 +1661,21 @@ describe("Users", function () { }); }); - it("Should return 403 for Unauthorized access", function (done) { + it("Should return 403 for Forbidden access", function (done) { + const userId = "anotherUser123"; + addJoinData(joinData(userId)[3]); chai .request(app) .put(`/users/${userId}/intro?dev=true`) - .set("Cookie", `${cookieName}=${jwt}`) - .send(joinData()[1]) + .set("cookie", `${cookieName}=${jwt}`) + .send(joinData(userId)[3]) .end((err, res) => { - if (err) { - return done(err); - } - expect(res).to.have.status(400); - expect(res.body).to.be.a("object"); - expect(res.body.message).to.be.equal('"firstName" is required'); + if (err) return done(err); + + expect(res).to.have.status(403); + expect(res.body).to.be.an("object"); + expect(res.body.message).to.equal("Forbidden access"); + return done(); }); }); From 7dcd8851a2ea941cf9a247bbfc6bcf549e496b7d Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:24:32 +0530 Subject: [PATCH 5/9] added one userauthorization middleware and test cases fixes --- controllers/stocks.js | 25 +++++++ middlewares/userAuthorization.ts | 9 +++ routes/stocks.js | 5 +- .../middlewares/userAuthorization.test.ts | 70 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 middlewares/userAuthorization.ts create mode 100644 test/unit/middlewares/userAuthorization.test.ts diff --git a/controllers/stocks.js b/controllers/stocks.js index 1f914098b..8f25e1049 100644 --- a/controllers/stocks.js +++ b/controllers/stocks.js @@ -48,6 +48,31 @@ const getSelfStocks = async (req, res) => { try { const { id: userId } = req.userData; const userStocks = await stocks.fetchUserStocks(userId); + + res.set( + "X-Deprecation-Warning", + "WARNING: This endpoint is being deprecated and will be removed in the future. Please use `/stocks/:userId` route to get the user stocks details." + ); + return res.json({ + message: userStocks.length > 0 ? "User stocks returned successfully!" : "No stocks found", + userStocks, + }); + } catch (err) { + logger.error(`Error while getting user stocks ${err}`); + return res.boom.badImplementation(INTERNAL_SERVER_ERROR); + } +}; + +/** + * Fetches all the stocks of the authenticated user + * + * @param req {Object} - Express request object + * @param res {Object} - Express response object + */ +const getUserStocks = async (req, res) => { + try { + const userStocks = await stocks.fetchUserStocks(req.params.userId); + return res.json({ message: userStocks.length > 0 ? "User stocks returned successfully!" : "No stocks found", userStocks, diff --git a/middlewares/userAuthorization.ts b/middlewares/userAuthorization.ts new file mode 100644 index 000000000..376f2dda2 --- /dev/null +++ b/middlewares/userAuthorization.ts @@ -0,0 +1,9 @@ +import { NextFunction } from "express"; +import { CustomRequest, CustomResponse } from "../types/global"; + +export const userAuthorization = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { + if (req.params.userId === req.userData.id) { + return next(); + } + res.boom.forbidden("Unauthorized access"); +}; diff --git a/routes/stocks.js b/routes/stocks.js index f9818f357..d0aab0c8c 100644 --- a/routes/stocks.js +++ b/routes/stocks.js @@ -5,9 +5,12 @@ const authorizeRoles = require("../middlewares/authorizeRoles"); const { addNewStock, fetchStocks, getSelfStocks } = require("../controllers/stocks"); const { createStock } = require("../middlewares/validators/stocks"); const { SUPERUSER } = require("../constants/roles"); +const { devFlagMiddleware } = require("../middlewares/devFlag"); +const { userAuthorization } = require("../middlewares/userAuthorization"); router.get("/", fetchStocks); router.post("/", authenticate, authorizeRoles([SUPERUSER]), createStock, addNewStock); -router.get("/user/self", authenticate, getSelfStocks); +router.get("/user/self", authenticate, getSelfStocks); // this route will soon be deprecated, please use `/stocks/:userId` route. +router.get("/:userId", devFlagMiddleware, authenticate, userAuthorization, getUserStocks); module.exports = router; diff --git a/test/unit/middlewares/userAuthorization.test.ts b/test/unit/middlewares/userAuthorization.test.ts new file mode 100644 index 000000000..0815cbc32 --- /dev/null +++ b/test/unit/middlewares/userAuthorization.test.ts @@ -0,0 +1,70 @@ +import * as sinon from "sinon"; +import chai from "chai"; +const { expect } = chai; +const { userAuthorization } = require("../../../middlewares/userAuthorization"); + +describe("userAuthorization Middleware", function () { + let req; + let res; + let next; + + beforeEach(function () { + req = { + params: {}, + userData: {}, + }; + res = { + boom: { + forbidden: sinon.spy((message) => { + res.status = 403; + res.message = message; + }), + }, + }; + next = sinon.spy(); + }); + + it("should call next() if userId matches userData.id", function () { + req.params.userId = "123"; + req.userData.id = "123"; + + userAuthorization(req, res, next); + + expect(next.calledOnce).to.be.true; + expect(res.boom.forbidden.notCalled).to.be.true; + }); + + it("should call res.boom.forbidden() if userId does not match userData.id", function () { + req.params.userId = "123"; + req.userData.id = "456"; + + userAuthorization(req, res, next); + + expect(res.boom.forbidden.calledOnce).to.be.true; + expect(res.status).to.equal(403); + expect(res.message).to.equal("Unauthorized access"); + expect(next.notCalled).to.be.true; + }); + + it("should call res.boom.forbidden() if userData.id is missing", function () { + req.params.userId = "123"; + + userAuthorization(req, res, next); + + expect(res.boom.forbidden.calledOnce).to.be.true; + expect(res.status).to.equal(403); + expect(res.message).to.equal("Unauthorized access"); + expect(next.notCalled).to.be.true; + }); + + it("should call res.boom.forbidden() if userId is missing", function () { + req.userData.id = "123"; + + userAuthorization(req, res, next); + + expect(res.boom.forbidden.calledOnce).to.be.true; + expect(res.status).to.equal(403); + expect(res.message).to.equal("Unauthorized access"); + expect(next.notCalled).to.be.true; + }); +}); From 51f0587f5e5bdd2c4fd904b624745ae3ea37f5fb Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:21:35 +0530 Subject: [PATCH 6/9] added userAuthorization middleware --- controllers/users.js | 4 ---- routes/users.js | 10 +++++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/controllers/users.js b/controllers/users.js index 2e6df3d51..6c8cb3a0d 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -803,10 +803,6 @@ const rejectProfileDiff = async (req, res) => { }; const addUserIntro = async (req, res) => { - if (req.path !== "/self/intro" && req.params.userId !== req.userData.id) { - return res.boom.forbidden("Forbidden access"); - } - try { const rawData = req.body; const joinData = await userQuery.getJoinData(req.userData.id); diff --git a/routes/users.js b/routes/users.js index a3e0046e9..e38c2ca31 100644 --- a/routes/users.js +++ b/routes/users.js @@ -13,6 +13,7 @@ const ROLES = require("../constants/roles"); const { Services } = require("../constants/bot"); const authenticateProfile = require("../middlewares/authenticateProfile"); const { devFlagMiddleware } = require("../middlewares/devFlag"); +const { userAuthorization } = require("../middlewares/userAuthorization"); router.post("/", authorizeAndAuthenticate([ROLES.SUPERUSER], [Services.CRON_JOB_HANDLER]), users.markUnverified); router.post("/update-in-discord", authenticate, authorizeRoles([SUPERUSER]), users.setInDiscordScript); @@ -37,7 +38,14 @@ router.patch( router.get("/:username", users.getUser); router.get("/:userId/intro", authenticate, authorizeRoles([SUPERUSER]), users.getUserIntro); router.put("/self/intro", authenticate, userValidator.validateJoinData, users.addUserIntro); // This route is being deprecated soon, please use alternate available route `/users/:userId/intro`. -router.put("/:userId/intro", devFlagMiddleware, authenticate, userValidator.validateJoinData, users.addUserIntro); +router.put( + "/:userId/intro", + devFlagMiddleware, + authenticate, + userValidator.validateJoinData, + userAuthorization, + users.addUserIntro +); router.get("/:id/skills", users.getUserSkills); router.get("/:id/badges", getUserBadges); router.patch( From 238cd1755b865562be75c182d3ce0fb4c6bb98f3 Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:50:41 +0530 Subject: [PATCH 7/9] Revert "added one userauthorization middleware and test cases fixes" This reverts commit 7dcd8851a2ea941cf9a247bbfc6bcf549e496b7d. --- controllers/stocks.js | 25 ------- middlewares/userAuthorization.ts | 9 --- routes/stocks.js | 5 +- .../middlewares/userAuthorization.test.ts | 70 ------------------- 4 files changed, 1 insertion(+), 108 deletions(-) delete mode 100644 middlewares/userAuthorization.ts delete mode 100644 test/unit/middlewares/userAuthorization.test.ts diff --git a/controllers/stocks.js b/controllers/stocks.js index 8f25e1049..1f914098b 100644 --- a/controllers/stocks.js +++ b/controllers/stocks.js @@ -48,31 +48,6 @@ const getSelfStocks = async (req, res) => { try { const { id: userId } = req.userData; const userStocks = await stocks.fetchUserStocks(userId); - - res.set( - "X-Deprecation-Warning", - "WARNING: This endpoint is being deprecated and will be removed in the future. Please use `/stocks/:userId` route to get the user stocks details." - ); - return res.json({ - message: userStocks.length > 0 ? "User stocks returned successfully!" : "No stocks found", - userStocks, - }); - } catch (err) { - logger.error(`Error while getting user stocks ${err}`); - return res.boom.badImplementation(INTERNAL_SERVER_ERROR); - } -}; - -/** - * Fetches all the stocks of the authenticated user - * - * @param req {Object} - Express request object - * @param res {Object} - Express response object - */ -const getUserStocks = async (req, res) => { - try { - const userStocks = await stocks.fetchUserStocks(req.params.userId); - return res.json({ message: userStocks.length > 0 ? "User stocks returned successfully!" : "No stocks found", userStocks, diff --git a/middlewares/userAuthorization.ts b/middlewares/userAuthorization.ts deleted file mode 100644 index 376f2dda2..000000000 --- a/middlewares/userAuthorization.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NextFunction } from "express"; -import { CustomRequest, CustomResponse } from "../types/global"; - -export const userAuthorization = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { - if (req.params.userId === req.userData.id) { - return next(); - } - res.boom.forbidden("Unauthorized access"); -}; diff --git a/routes/stocks.js b/routes/stocks.js index d0aab0c8c..f9818f357 100644 --- a/routes/stocks.js +++ b/routes/stocks.js @@ -5,12 +5,9 @@ const authorizeRoles = require("../middlewares/authorizeRoles"); const { addNewStock, fetchStocks, getSelfStocks } = require("../controllers/stocks"); const { createStock } = require("../middlewares/validators/stocks"); const { SUPERUSER } = require("../constants/roles"); -const { devFlagMiddleware } = require("../middlewares/devFlag"); -const { userAuthorization } = require("../middlewares/userAuthorization"); router.get("/", fetchStocks); router.post("/", authenticate, authorizeRoles([SUPERUSER]), createStock, addNewStock); -router.get("/user/self", authenticate, getSelfStocks); // this route will soon be deprecated, please use `/stocks/:userId` route. -router.get("/:userId", devFlagMiddleware, authenticate, userAuthorization, getUserStocks); +router.get("/user/self", authenticate, getSelfStocks); module.exports = router; diff --git a/test/unit/middlewares/userAuthorization.test.ts b/test/unit/middlewares/userAuthorization.test.ts deleted file mode 100644 index 0815cbc32..000000000 --- a/test/unit/middlewares/userAuthorization.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as sinon from "sinon"; -import chai from "chai"; -const { expect } = chai; -const { userAuthorization } = require("../../../middlewares/userAuthorization"); - -describe("userAuthorization Middleware", function () { - let req; - let res; - let next; - - beforeEach(function () { - req = { - params: {}, - userData: {}, - }; - res = { - boom: { - forbidden: sinon.spy((message) => { - res.status = 403; - res.message = message; - }), - }, - }; - next = sinon.spy(); - }); - - it("should call next() if userId matches userData.id", function () { - req.params.userId = "123"; - req.userData.id = "123"; - - userAuthorization(req, res, next); - - expect(next.calledOnce).to.be.true; - expect(res.boom.forbidden.notCalled).to.be.true; - }); - - it("should call res.boom.forbidden() if userId does not match userData.id", function () { - req.params.userId = "123"; - req.userData.id = "456"; - - userAuthorization(req, res, next); - - expect(res.boom.forbidden.calledOnce).to.be.true; - expect(res.status).to.equal(403); - expect(res.message).to.equal("Unauthorized access"); - expect(next.notCalled).to.be.true; - }); - - it("should call res.boom.forbidden() if userData.id is missing", function () { - req.params.userId = "123"; - - userAuthorization(req, res, next); - - expect(res.boom.forbidden.calledOnce).to.be.true; - expect(res.status).to.equal(403); - expect(res.message).to.equal("Unauthorized access"); - expect(next.notCalled).to.be.true; - }); - - it("should call res.boom.forbidden() if userId is missing", function () { - req.userData.id = "123"; - - userAuthorization(req, res, next); - - expect(res.boom.forbidden.calledOnce).to.be.true; - expect(res.status).to.equal(403); - expect(res.message).to.equal("Unauthorized access"); - expect(next.notCalled).to.be.true; - }); -}); From e99742cbd332a66051e6aa751f7b30c237945864 Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Fri, 13 Dec 2024 00:58:59 +0530 Subject: [PATCH 8/9] Revert "Revert "added one userauthorization middleware and test cases fixes"" This reverts commit 238cd1755b865562be75c182d3ce0fb4c6bb98f3. --- controllers/stocks.js | 25 +++++++ middlewares/userAuthorization.ts | 9 +++ routes/stocks.js | 5 +- .../middlewares/userAuthorization.test.ts | 70 +++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 middlewares/userAuthorization.ts create mode 100644 test/unit/middlewares/userAuthorization.test.ts diff --git a/controllers/stocks.js b/controllers/stocks.js index 1f914098b..8f25e1049 100644 --- a/controllers/stocks.js +++ b/controllers/stocks.js @@ -48,6 +48,31 @@ const getSelfStocks = async (req, res) => { try { const { id: userId } = req.userData; const userStocks = await stocks.fetchUserStocks(userId); + + res.set( + "X-Deprecation-Warning", + "WARNING: This endpoint is being deprecated and will be removed in the future. Please use `/stocks/:userId` route to get the user stocks details." + ); + return res.json({ + message: userStocks.length > 0 ? "User stocks returned successfully!" : "No stocks found", + userStocks, + }); + } catch (err) { + logger.error(`Error while getting user stocks ${err}`); + return res.boom.badImplementation(INTERNAL_SERVER_ERROR); + } +}; + +/** + * Fetches all the stocks of the authenticated user + * + * @param req {Object} - Express request object + * @param res {Object} - Express response object + */ +const getUserStocks = async (req, res) => { + try { + const userStocks = await stocks.fetchUserStocks(req.params.userId); + return res.json({ message: userStocks.length > 0 ? "User stocks returned successfully!" : "No stocks found", userStocks, diff --git a/middlewares/userAuthorization.ts b/middlewares/userAuthorization.ts new file mode 100644 index 000000000..376f2dda2 --- /dev/null +++ b/middlewares/userAuthorization.ts @@ -0,0 +1,9 @@ +import { NextFunction } from "express"; +import { CustomRequest, CustomResponse } from "../types/global"; + +export const userAuthorization = (req: CustomRequest, res: CustomResponse, next: NextFunction) => { + if (req.params.userId === req.userData.id) { + return next(); + } + res.boom.forbidden("Unauthorized access"); +}; diff --git a/routes/stocks.js b/routes/stocks.js index f9818f357..d0aab0c8c 100644 --- a/routes/stocks.js +++ b/routes/stocks.js @@ -5,9 +5,12 @@ const authorizeRoles = require("../middlewares/authorizeRoles"); const { addNewStock, fetchStocks, getSelfStocks } = require("../controllers/stocks"); const { createStock } = require("../middlewares/validators/stocks"); const { SUPERUSER } = require("../constants/roles"); +const { devFlagMiddleware } = require("../middlewares/devFlag"); +const { userAuthorization } = require("../middlewares/userAuthorization"); router.get("/", fetchStocks); router.post("/", authenticate, authorizeRoles([SUPERUSER]), createStock, addNewStock); -router.get("/user/self", authenticate, getSelfStocks); +router.get("/user/self", authenticate, getSelfStocks); // this route will soon be deprecated, please use `/stocks/:userId` route. +router.get("/:userId", devFlagMiddleware, authenticate, userAuthorization, getUserStocks); module.exports = router; diff --git a/test/unit/middlewares/userAuthorization.test.ts b/test/unit/middlewares/userAuthorization.test.ts new file mode 100644 index 000000000..0815cbc32 --- /dev/null +++ b/test/unit/middlewares/userAuthorization.test.ts @@ -0,0 +1,70 @@ +import * as sinon from "sinon"; +import chai from "chai"; +const { expect } = chai; +const { userAuthorization } = require("../../../middlewares/userAuthorization"); + +describe("userAuthorization Middleware", function () { + let req; + let res; + let next; + + beforeEach(function () { + req = { + params: {}, + userData: {}, + }; + res = { + boom: { + forbidden: sinon.spy((message) => { + res.status = 403; + res.message = message; + }), + }, + }; + next = sinon.spy(); + }); + + it("should call next() if userId matches userData.id", function () { + req.params.userId = "123"; + req.userData.id = "123"; + + userAuthorization(req, res, next); + + expect(next.calledOnce).to.be.true; + expect(res.boom.forbidden.notCalled).to.be.true; + }); + + it("should call res.boom.forbidden() if userId does not match userData.id", function () { + req.params.userId = "123"; + req.userData.id = "456"; + + userAuthorization(req, res, next); + + expect(res.boom.forbidden.calledOnce).to.be.true; + expect(res.status).to.equal(403); + expect(res.message).to.equal("Unauthorized access"); + expect(next.notCalled).to.be.true; + }); + + it("should call res.boom.forbidden() if userData.id is missing", function () { + req.params.userId = "123"; + + userAuthorization(req, res, next); + + expect(res.boom.forbidden.calledOnce).to.be.true; + expect(res.status).to.equal(403); + expect(res.message).to.equal("Unauthorized access"); + expect(next.notCalled).to.be.true; + }); + + it("should call res.boom.forbidden() if userId is missing", function () { + req.userData.id = "123"; + + userAuthorization(req, res, next); + + expect(res.boom.forbidden.calledOnce).to.be.true; + expect(res.status).to.equal(403); + expect(res.message).to.equal("Unauthorized access"); + expect(next.notCalled).to.be.true; + }); +}); From 9c06ac87a1224f0a6fe1b930b96ab0d0433ff6ed Mon Sep 17 00:00:00 2001 From: vikasosmium <59792866+vikasosmium@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:32:12 +0530 Subject: [PATCH 9/9] fixed test case msg --- test/integration/users.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/users.test.js b/test/integration/users.test.js index 3572af9c6..f607e0d61 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -1664,6 +1664,7 @@ describe("Users", function () { it("Should return 403 for Forbidden access", function (done) { const userId = "anotherUser123"; addJoinData(joinData(userId)[3]); + chai .request(app) .put(`/users/${userId}/intro?dev=true`) @@ -1674,8 +1675,7 @@ describe("Users", function () { expect(res).to.have.status(403); expect(res.body).to.be.an("object"); - expect(res.body.message).to.equal("Forbidden access"); - + expect(res.body.message).to.equal("Unauthorized access"); return done(); }); });