From f84a028823191354ae18e69d795fdb2bf6715b31 Mon Sep 17 00:00:00 2001 From: Randhir Kumar Singh <97341921+heyrandhir@users.noreply.github.com> Date: Fri, 4 Aug 2023 02:15:02 +0530 Subject: [PATCH 1/3] Dev to Main Sync (#1357) * Feat : username and twitter id contains @ / special characters in the value in some user data. (#1320) * feat:add validations in the signup api to check that username must be in the specific format and social id must not contain the special character @ to it also modify the error with a proper error message * fix:the error message description * fix:change the error message according to specific social id change * fix:change the regex validation such that now it will accept numbers or characters or combination of both * refactor:change the error message to meaningful text * feat:add tests for invalid username and valid username and invalid social id * fix:match the error message with the failing test case message, update the error messages in the signup api * fix:remove unnecessary validation for linkedin_id, intagram_id we are not sending it during signup also remove the tests for that * fix:remove unnecessary validation for linkedin_id, intagram_id we are not sending it during signup also remove the tests for that * feat:add unit testing for the update user function to check if invalid username and twitter_id is passed or not --------- Co-authored-by: pushpendu * add option to set privilaged roles in staging backend * remove console logs * modify the condition to check non production env * add support to other roles update in staging (#1354) * add support to other roles * modify condition to forbid in production * add an api to remove privilaged roles to all users (#1355) * add an api to remove privilaged roles to all users * add middleware to allow body * adds metrics for users update route (#1347) * Fixing Task Reassignment Status Update Issue (#1351) * adds the logic to modify the user status of old assignee * Adds integration test * Consolidated Multi-Line Variable Declarations into Single Line --------- Co-authored-by: Abhay Patil <71277980+Abhay5855@users.noreply.github.com> Co-authored-by: pushpendu Co-authored-by: ritikjaiswal75 Co-authored-by: Amit Prakash <34869115+iamitprakash@users.noreply.github.com> Co-authored-by: Ritik Jaiswal <57758447+RitikJaiswal75@users.noreply.github.com> --- controllers/staging.js | 86 +++++++++++++++++++ controllers/tasks.js | 5 ++ controllers/userStatus.js | 3 +- middlewares/validators/staging.js | 39 +++++++++ middlewares/validators/user.js | 14 ++- models/userStatus.js | 26 +++++- routes/index.js | 2 +- routes/staging.js | 10 +++ .../integration/taskBasedStatusUpdate.test.js | 55 ++++++++++++ test/integration/userStatus.test.js | 21 +++++ test/integration/users.test.js | 69 +++++++++++++++ test/unit/middlewares/user-validator.test.js | 57 ++++++++++++ utils/tasks.js | 17 ++-- 13 files changed, 391 insertions(+), 13 deletions(-) create mode 100644 controllers/staging.js create mode 100644 middlewares/validators/staging.js create mode 100644 routes/staging.js diff --git a/controllers/staging.js b/controllers/staging.js new file mode 100644 index 000000000..2afe18c45 --- /dev/null +++ b/controllers/staging.js @@ -0,0 +1,86 @@ +const { addOrUpdate, getUsersByRole } = require("../models/users"); + +const updateRoles = async (req, res) => { + try { + const userData = await req.userData; + if (process.env.NODE_ENV === "production") { + return res.status(403).json({ + message: "FORBIDDEN | To be used only in staging and development", + }); + } + const userId = req.userData.id; + await addOrUpdate( + { + roles: { + ...userData.roles, + ...req.body, + }, + }, + userId + ); + return res.status(200).json({ + message: "Roles Updated successfully", + }); + } catch (err) { + logger.error(`Oops an error occured: ${err}`); + return res.status(500).json({ + message: "Oops an internal error occured", + }); + } +}; + +const removePrivileges = async (req, res) => { + if (process.env.NODE_ENV === "production") { + return res.status(403).json({ + message: "FORBIDDEN | To be used only in staging and development", + }); + } + try { + const updateUserPromises = []; + const members = await getUsersByRole("member"); + const superUsers = await getUsersByRole("super_user"); + + members.forEach((member) => { + updateUserPromises.push( + addOrUpdate( + { + roles: { + ...member.roles, + member: false, + }, + }, + member.id + ) + ); + }); + superUsers.forEach((superUser) => { + updateUserPromises.push( + addOrUpdate( + { + roles: { + ...superUser.roles, + super_user: false, + }, + }, + superUser.id + ) + ); + }); + + await Promise.all(updateUserPromises); + + return res.status(200).json({ + message: "Roles Updated successfully", + }); + } catch (err) { + logger.error(`Oops an error occurred: ${err}`); + return res.status(500).json({ + message: "Oops an internal error occurred", + }); + } +}; + +module.exports = { + updateRoles, + removePrivileges, +}; diff --git a/controllers/tasks.js b/controllers/tasks.js index dfd36134b..b40134915 100644 --- a/controllers/tasks.js +++ b/controllers/tasks.js @@ -256,7 +256,12 @@ const updateTask = async (req, res) => { } await tasks.updateTask(req.body, req.params.id); if (isUserStatusEnabled && req.body.assignee) { + // New Assignee Status Update await updateUserStatusOnTaskUpdate(req.body.assignee); + // Old Assignee Status Update if available + if (task.taskData.assigneeId) { + await updateStatusOnTaskCompletion(task.taskData.assigneeId); + } } return res.status(204).send(); diff --git a/controllers/userStatus.js b/controllers/userStatus.js index 0274f1ad3..fa851eff8 100644 --- a/controllers/userStatus.js +++ b/controllers/userStatus.js @@ -135,9 +135,10 @@ const updateUserStatus = async (req, res) => { */ const updateAllUserStatus = async (req, res) => { try { - await userStatusModel.updateAllUserStatus(); + const data = await userStatusModel.updateAllUserStatus(); return res.status(200).json({ message: "All User Status updated successfully.", + data, }); } catch (err) { logger.error(`Error while updating the User Data: ${err}`); diff --git a/middlewares/validators/staging.js b/middlewares/validators/staging.js new file mode 100644 index 000000000..e9180826e --- /dev/null +++ b/middlewares/validators/staging.js @@ -0,0 +1,39 @@ +const joi = require("joi"); + +const validateUserRoles = async (req, res, next) => { + const schema = joi.object().strict().keys({ + super_user: joi.boolean().optional(), + member: joi.boolean().optional(), + archive: joi.boolean().optional(), + in_discord: joi.boolean().optional(), + }); + + try { + await schema.validateAsync(req.body); + next(); + } catch (err) { + logger.error(`Error validating validateUserRoles payload : ${err}`); + res.boom.badRequest(JSON.stringify({ allowedParameters: { super_user: "boolean", member: "boolean" } })); + } +}; + +const validateRevokePrivileges = async (req, res, next) => { + const schema = joi + .object() + .strict() + .keys({ + action: joi.string().equal("revoke"), + }); + try { + await schema.validateAsync(req.body); + next(); + } catch (err) { + logger.error(`Error validating validateUserRoles payload : ${err}`); + res.boom.badRequest(JSON.stringify({ allowedParameters: { action: "revoke" } })); + } +}; + +module.exports = { + validateUserRoles, + validateRevokePrivileges, +}; diff --git a/middlewares/validators/user.js b/middlewares/validators/user.js index 9e4dfdfb8..131707c36 100644 --- a/middlewares/validators/user.js +++ b/middlewares/validators/user.js @@ -13,7 +13,13 @@ const updateUser = async (req, res, next) => { .keys({ phone: joi.string().optional(), email: joi.string().optional(), - username: joi.string().optional(), + username: joi + .string() + .optional() + .min(4) + .max(20) + .regex(/^[a-zA-Z0-9]+$/) + .message("Username must be between 4 and 20 characters long and contain only letters or numbers."), first_name: joi.string().optional(), last_name: joi.string().optional(), yoe: joi.number().min(0).optional(), @@ -21,7 +27,11 @@ const updateUser = async (req, res, next) => { designation: joi.string().optional(), img: joi.string().optional(), linkedin_id: joi.string().optional(), - twitter_id: joi.string().optional(), + twitter_id: joi + .string() + .optional() + .regex(/^[^@]*$/) + .message("Invalid Twitter ID. ID should not contain special character @"), instagram_id: joi.string().optional(), website: joi.string().optional(), status: joi diff --git a/models/userStatus.js b/models/userStatus.js index ac121da7c..ae463d5c7 100644 --- a/models/userStatus.js +++ b/models/userStatus.js @@ -157,8 +157,16 @@ const updateUserStatus = async (userId, newStatusData) => { */ const updateAllUserStatus = async () => { + const summary = { + usersCount: 0, + oooUsersAltered: 0, + oooUsersUnaltered: 0, + nonOooUsersAltered: 0, + nonOooUsersUnaltered: 0, + }; try { const userStatusDocs = await userStatusModel.where("futureStatus.state", "in", ["ACTIVE", "IDLE", "OOO"]).get(); + summary.usersCount = userStatusDocs._size; const batch = firestore.batch(); const today = new Date().getTime(); userStatusDocs.forEach(async (document) => { @@ -170,15 +178,23 @@ const updateAllUserStatus = async () => { const { state: futureState } = futureStatus; if (futureState === "ACTIVE" || futureState === "IDLE") { if (today >= futureStatus.from) { + // OOO period is over and we need to update their current status newStatusData.currentStatus = { ...futureStatus, until: "", updatedAt: today }; - newStatusData.futureStatus = {}; + delete newStatusData.futureStatus; toUpdate = !toUpdate; + summary.oooUsersAltered++; + } else { + summary.oooUsersUnaltered++; } } else { + // futureState is OOO if (today > futureStatus.until) { - newStatusData.futureStatus = {}; + // the OOO period is over + delete newStatusData.futureStatus; toUpdate = !toUpdate; + summary.nonOooUsersAltered++; } else if (today <= doc.futureStatus.until && today >= doc.futureStatus.from) { + // the current date i.e today lies in between the from and until so we need to swap the status let newCurrentStatus = {}; let newFutureStatus = {}; newCurrentStatus = { ...futureStatus, updatedAt: today }; @@ -188,6 +204,9 @@ const updateAllUserStatus = async () => { newStatusData.currentStatus = newCurrentStatus; newStatusData.futureStatus = newFutureStatus; toUpdate = !toUpdate; + summary.nonOooUsersAltered++; + } else { + summary.nonOooUsersUnaltered++; } } if (toUpdate) { @@ -200,13 +219,12 @@ const updateAllUserStatus = async () => { ); } await batch.commit(); - return { status: 204, message: "User Status updated Successfully." }; + return summary; } catch (error) { logger.error(`error in updating User Status Documents ${error}`); return { status: 500, message: "User Status couldn't be updated Successfully." }; } }; - /** * Updates the user status based on a new task assignment. * @param {string} userId - The ID of the user. diff --git a/routes/index.js b/routes/index.js index dce538991..737f945ce 100644 --- a/routes/index.js +++ b/routes/index.js @@ -30,5 +30,5 @@ app.use("/discord-actions", require("./discordactions.js")); app.use("/issues", require("./issues.js")); app.use("/progresses", require("./progresses.js")); app.use("/monitor", require("./monitor.js")); - +app.use("/staging", require("./staging.js")); module.exports = app; diff --git a/routes/staging.js b/routes/staging.js new file mode 100644 index 000000000..64b4d9319 --- /dev/null +++ b/routes/staging.js @@ -0,0 +1,10 @@ +const express = require("express"); +const authenticate = require("../middlewares/authenticate"); +const { validateUserRoles, validateRevokePrivileges } = require("../middlewares/validators/staging"); +const { updateRoles, removePrivileges } = require("../controllers/staging"); +const router = express.Router(); + +router.patch("/user", validateUserRoles, authenticate, updateRoles); +router.post("/users/privileges", validateRevokePrivileges, removePrivileges); + +module.exports = router; diff --git a/test/integration/taskBasedStatusUpdate.test.js b/test/integration/taskBasedStatusUpdate.test.js index 130807c6f..1ea2d96a7 100644 --- a/test/integration/taskBasedStatusUpdate.test.js +++ b/test/integration/taskBasedStatusUpdate.test.js @@ -537,4 +537,59 @@ describe("Task Based Status Updates", function () { ); }); }); + + describe("PATCH Update User Status on Task Assignment by SuperUser", function () { + let userId1, user2Name, superUserId, superUserJwt, taskArr; + const reqBody = {}; + + beforeEach(async function () { + userId1 = await addUser(userData[6]); + superUserId = await addUser(userData[4]); + superUserJwt = authService.generateAuthToken({ userId: superUserId }); + await addUser(userData[0]); + user2Name = userData[0].username; + taskArr = allTasks(); + const sampleTask1 = taskArr[0]; + sampleTask1.assignee = userId1; + sampleTask1.createdBy = superUserId; + await firestore.collection("tasks").doc("taskid123").set(sampleTask1); + const statusData = generateStatusDataForState(userId1, userState.ACTIVE); + await firestore.collection("usersStatus").doc("userStatusDoc001").set(statusData); + }); + + afterEach(async function () { + await cleanDb(); + }); + + it("Update the old assignee status to IDLE on task reassignment if no tasks is in progress in their name", async function () { + reqBody.assignee = user2Name; + const res = await chai + .request(app) + .patch(`/tasks/taskid123?userStatusFlag=true`) + .set("cookie", `${cookieName}=${superUserJwt}`) + .send(reqBody); + expect(res.status).to.equal(204); + const userStatus002Data = (await userStatusModel.doc("userStatusDoc001").get()).data(); + expect(userStatus002Data).to.have.keys(["userId", "currentStatus"]); + expect(userStatus002Data.currentStatus.state).to.equal(userState.IDLE); + }); + + it("Should maintain the old assignee status to ACTIVE on task reassignment if another task is in progress in their name", async function () { + const sampleTask2 = taskArr[1]; + sampleTask2.assignee = userId1; + sampleTask2.createdBy = superUserId; + await firestore.collection("tasks").doc("taskid234").set(sampleTask2); + + reqBody.assignee = user2Name; + const res = await chai + .request(app) + .patch(`/tasks/taskid123?userStatusFlag=true`) + .set("cookie", `${cookieName}=${superUserJwt}`) + .send(reqBody); + expect(res.status).to.equal(204); + const userStatus002Data = (await userStatusModel.doc("userStatusDoc001").get()).data(); + expect(userStatus002Data).to.have.keys(["userId", "currentStatus"]); + expect(userStatus002Data.currentStatus.state).to.equal(userState.ACTIVE); + }); + }); }); diff --git a/test/integration/userStatus.test.js b/test/integration/userStatus.test.js index fcc414691..1862763c1 100644 --- a/test/integration/userStatus.test.js +++ b/test/integration/userStatus.test.js @@ -175,6 +175,13 @@ describe("UserStatus", function () { .send(); expect(response3).to.have.status(200); expect(response3.body.message).to.equal("All User Status updated successfully."); + expect(response3.body.data).to.deep.equal({ + usersCount: 1, + oooUsersAltered: 0, + oooUsersUnaltered: 0, + nonOooUsersAltered: 1, + nonOooUsersUnaltered: 0, + }); // Checking the current status const response4 = await chai.request(app).get(`/users/status/self`).set("Cookie", `${cookieName}=${testUserJwt}`); @@ -195,6 +202,13 @@ describe("UserStatus", function () { .send(); expect(response5).to.have.status(200); expect(response5.body.message).to.equal("All User Status updated successfully."); + expect(response5.body.data).to.deep.equal({ + usersCount: 1, + oooUsersAltered: 1, + oooUsersUnaltered: 0, + nonOooUsersAltered: 0, + nonOooUsersUnaltered: 0, + }); const response6 = await chai.request(app).get(`/users/status/self`).set("Cookie", `${cookieName}=${testUserJwt}`); expect(response6).to.have.status(200); @@ -236,6 +250,13 @@ describe("UserStatus", function () { .send(); expect(response3).to.have.status(200); expect(response3.body.message).to.equal("All User Status updated successfully."); + expect(response3.body.data).to.deep.equal({ + usersCount: 1, + oooUsersAltered: 0, + oooUsersUnaltered: 0, + nonOooUsersAltered: 1, + nonOooUsersUnaltered: 0, + }); // Checking the current status const response4 = await chai.request(app).get(`/users/status/self`).set("Cookie", `${cookieName}=${testUserJwt}`); diff --git a/test/integration/users.test.js b/test/integration/users.test.js index 3252dc717..27285d85a 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -100,6 +100,25 @@ describe("Users", function () { }); }); + it("Should update the username with valid username", function (done) { + chai + .request(app) + .patch("/users/self") + .set("cookie", `${cookieName}=${jwt}`) + .send({ + username: "validUsername123", + }) + .end((err, res) => { + if (err) { + return done(err); + } + + expect(res).to.have.status(204); + + return done(); + }); + }); + it("Should return 400 for invalid status value", function (done) { chai .request(app) @@ -124,6 +143,56 @@ describe("Users", function () { return done(); }); }); + + it("Should return 400 for invalid username", function (done) { + chai + .request(app) + .patch("/users/self") + .set("cookie", `${cookieName}=${jwt}`) + .send({ + username: "@invalidUser-name", + }) + .end((err, res) => { + if (err) { + return done(err); + } + + expect(res).to.have.status(400); + expect(res.body).to.be.an("object"); + expect(res.body).to.eql({ + statusCode: 400, + error: "Bad Request", + message: "Username must be between 4 and 20 characters long and contain only letters or numbers.", + }); + + return done(); + }); + }); + + it("Should return 400 for invalid Twitter ID", function (done) { + chai + .request(app) + .patch("/users/self") + .set("cookie", `${cookieName}=${jwt}`) + .send({ + twitter_id: "invalid@twitter_id", + }) + .end((err, res) => { + if (err) { + return done(err); + } + + expect(res).to.have.status(400); + expect(res.body).to.be.an("object"); + expect(res.body).to.eql({ + statusCode: 400, + error: "Bad Request", + message: "Invalid Twitter ID. ID should not contain special character @", + }); + + return done(); + }); + }); }); describe("GET /users", function () { diff --git a/test/unit/middlewares/user-validator.test.js b/test/unit/middlewares/user-validator.test.js index ecbc9fb89..a579390a0 100644 --- a/test/unit/middlewares/user-validator.test.js +++ b/test/unit/middlewares/user-validator.test.js @@ -1,7 +1,9 @@ const sinon = require("sinon"); const { validateJoinData } = require("./../../../middlewares/validators/user"); const joinData = require("./../../fixtures/user/join"); +const userData = require("./../../fixtures/user/user"); const { expect } = require("chai"); +const { updateUser } = require("./../../../middlewares/validators/user"); describe("Middleware | Validators | User", function () { describe("Create user validator for validateJoinData", function () { @@ -34,4 +36,59 @@ describe("Middleware | Validators | User", function () { expect(nextSpy.calledOnce).to.be.equal(false); }); }); + + describe("Create user validator for updateUser", function () { + it("lets the request pass to next", async function () { + const req = { + body: userData[1], + }; + + const res = {}; + const next = sinon.spy(); + await updateUser(req, res, next); + expect(next.calledOnce).to.be.equal(true); + }); + + it("Stops the propagation of the next if twitter_id is invalid", async function () { + const req = { + body: { + last_name: "patil", + first_name: "Abhay", + username: "invalidusername", + twitter_id: "@abhayisawesome", + }, + }; + const res = { + boom: { + badRequest: () => {}, + }, + }; + const nextSpy = sinon.spy(); + await updateUser(req, res, nextSpy).catch((err) => { + expect(err).to.be.an.instanceOf(Error); + }); + expect(nextSpy.calledOnce).to.be.equal(false); + }); + + it("Stops the propagation of the next if username is invalid", async function () { + const req = { + body: { + last_name: "patil", + first_name: "Abhay", + username: "@invalidusername-12", + twitter_id: "abhayisawesome", + }, + }; + const res = { + boom: { + badRequest: () => {}, + }, + }; + const nextSpy = sinon.spy(); + await updateUser(req, res, nextSpy).catch((err) => { + expect(err).to.be.an.instanceOf(Error); + }); + expect(nextSpy.calledOnce).to.be.equal(false); + }); + }); }); diff --git a/utils/tasks.js b/utils/tasks.js index df071beae..e9ae22219 100644 --- a/utils/tasks.js +++ b/utils/tasks.js @@ -6,26 +6,33 @@ const fromFirestoreData = async (task) => { return task; } - let { createdBy, assignee, participants, type } = task; + let { createdBy, assignee: assigneeId, participants, type } = task; + let assigneeName; if (createdBy) { createdBy = await getUsername(createdBy); } - if (assignee) { - assignee = await getUsername(assignee); + if (assigneeId) { + assigneeName = await getUsername(assigneeId); } if (type === TASK_TYPE.GROUP) { participants = await getParticipantUsernames(participants); } - return { + const updatedTask = { ...task, createdBy, - assignee, participants, }; + + if (assigneeName || assigneeId) { + updatedTask.assignee = assigneeName; + updatedTask.assigneeId = assigneeId; + } + + return updatedTask; }; const toFirestoreData = async (task) => { From 9559ae4a74af27c801fb35bab2425808ef65195c Mon Sep 17 00:00:00 2001 From: Randhir Kumar Singh <97341921+heyrandhir@users.noreply.github.com> Date: Sun, 6 Aug 2023 14:06:57 +0530 Subject: [PATCH 2/3] fix the lint errors --- test/integration/users.test.js | 2 -- test/unit/middlewares/user-validator.test.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/integration/users.test.js b/test/integration/users.test.js index 38f275e28..7f3f58272 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -142,7 +142,6 @@ describe("Users", function () { }); }); - it("Should return 400 for invalid status value", function (done) { chai .request(app) @@ -168,7 +167,6 @@ describe("Users", function () { }); }); - it("Should return 400 if required roles is missing", function (done) { chai .request(app) diff --git a/test/unit/middlewares/user-validator.test.js b/test/unit/middlewares/user-validator.test.js index e1c552856..aa16bd9bf 100644 --- a/test/unit/middlewares/user-validator.test.js +++ b/test/unit/middlewares/user-validator.test.js @@ -86,7 +86,7 @@ describe("Middleware | Validators | User", function () { }); expect(nextSpy.calledOnce).to.be.equal(false); }); - + it("Stops the propagation of the next if twitter_id is invalid", async function () { const req = { body: { From 3423a5f024eec35eb388ce30123a52af022d6762 Mon Sep 17 00:00:00 2001 From: Amit Prakash <34869115+iamitprakash@users.noreply.github.com> Date: Sat, 12 Aug 2023 23:17:42 +0530 Subject: [PATCH 3/3] Revert "Dev to Main Sync" --- constants/userDataLevels.js | 21 ---- constants/users.js | 3 + controllers/discordactions.js | 19 +--- controllers/events.js | 74 +----------- controllers/members.js | 26 +---- controllers/users.js | 11 +- middlewares/authenticate.js | 7 +- middlewares/validators/events.js | 46 -------- models/discordactions.js | 31 ----- models/events.js | 106 ------------------ models/logs.js | 22 +--- models/users.js | 26 ----- routes/events.js | 2 - services/dataAccessLayer.js | 81 +++++-------- .../fixtures/discordactions/discordactions.js | 6 +- test/fixtures/logs/archievedUsers.js | 68 ----------- test/fixtures/user/user.js | 29 +---- test/integration/discord.test.js | 31 ----- test/integration/discordactions.test.js | 55 --------- test/integration/members.test.js | 69 ++++-------- test/integration/tasks.test.js | 2 +- test/integration/users.test.js | 25 +++++ test/integration/usersFilter.test.js | 1 + test/unit/models/discordactions.test.js | 37 ------ test/unit/models/events.test.js | 64 ++--------- test/unit/models/logs.test.js | 84 -------------- test/unit/models/users.test.js | 32 +----- test/unit/services/dataAccessLayer.test.js | 103 +++++------------ 28 files changed, 148 insertions(+), 933 deletions(-) delete mode 100644 constants/userDataLevels.js delete mode 100644 test/fixtures/logs/archievedUsers.js diff --git a/constants/userDataLevels.js b/constants/userDataLevels.js deleted file mode 100644 index a907e265a..000000000 --- a/constants/userDataLevels.js +++ /dev/null @@ -1,21 +0,0 @@ -const ACCESS_LEVEL = { - PUBLIC: "public", - INTERNAL: "internal", - PRIVATE: "private", - CONFIDENTIAL: "confidential", -}; - -const ROLE_LEVEL = { - private: ["super_user"], - internal: ["super_user"], - confidential: ["super_user"], -}; - -const KEYS_NOT_ALLOWED = { - public: ["email", "phone", "chaincode"], - internal: ["phone", "chaincode"], - private: ["chaincode"], - confidential: [], -}; - -module.exports = { ACCESS_LEVEL, KEYS_NOT_ALLOWED, ROLE_LEVEL }; diff --git a/constants/users.js b/constants/users.js index eaf6c73ac..3a11cf768 100644 --- a/constants/users.js +++ b/constants/users.js @@ -4,6 +4,8 @@ const profileStatus = { NOT_APPROVED: "NOT APPROVED", }; +const USER_SENSITIVE_DATA = ["phone", "email", "chaincode", "tokens"]; + const USER_STATUS = { OOO: "ooo", IDLE: "idle", @@ -21,4 +23,5 @@ module.exports = { profileStatus, USER_STATUS, ALLOWED_FILTER_PARAMS, + USER_SENSITIVE_DATA, }; diff --git a/controllers/discordactions.js b/controllers/discordactions.js index f91da7504..a24bf665b 100644 --- a/controllers/discordactions.js +++ b/controllers/discordactions.js @@ -3,7 +3,6 @@ const admin = require("firebase-admin"); const config = require("config"); const jwt = require("jsonwebtoken"); const discordRolesModel = require("../models/discordactions"); -const { retrieveUsers } = require("../services/dataAccessLayer"); /** * Creates a role @@ -67,25 +66,9 @@ const createGroupRole = async (req, res) => { const getAllGroupRoles = async (req, res) => { try { const { groups } = await discordRolesModel.getAllGroupRoles(); - const groupsWithMemberCount = await discordRolesModel.getNumberOfMemberForGroups(groups); - const groupCreatorIds = groupsWithMemberCount.reduce((ids, group) => { - ids.add(group.createdBy); - return ids; - }, new Set()); - const groupCreatorsDetails = await retrieveUsers({ userIds: Array.from(groupCreatorIds) }); - const groupsWithUserDetails = groupsWithMemberCount.map((group) => { - const groupCreator = groupCreatorsDetails[group.createdBy]; - return { - ...group, - firstName: groupCreator.first_name, - lastName: groupCreator.last_name, - image: groupCreator.picture?.url, - }; - }); - return res.json({ message: "Roles fetched successfully!", - groups: groupsWithUserDetails, + groups, }); } catch (err) { logger.error(`Error while getting roles: ${err}`); diff --git a/controllers/events.js b/controllers/events.js index 1429d65d7..b0b77bbdc 100644 --- a/controllers/events.js +++ b/controllers/events.js @@ -1,11 +1,9 @@ +/* eslint-disable camelcase */ const { GET_ALL_EVENTS_LIMIT_MIN, UNWANTED_PROPERTIES_FROM_100MS } = require("../constants/events"); -const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages"); - const { EventTokenService, EventAPIService } = require("../services"); +const { removeUnwantedProperties } = require("../utils/events"); const eventQuery = require("../models/events"); - const logger = require("../utils/logger"); -const { removeUnwantedProperties } = require("../utils/events"); const tokenService = new EventTokenService(); const apiService = new EventAPIService(tokenService); @@ -102,7 +100,7 @@ const joinEvent = async (req, res) => { }); } catch (error) { logger.error({ error }); - return res.boom.badImplementation(INTERNAL_SERVER_ERROR); + return res.status(500).send("Internal Server Error"); } }; @@ -195,70 +193,6 @@ const endActiveEvent = async (req, res) => { } }; -/** - * Adds a peer to an event. - * - * @async - * @function - * @param {Object} req - The Express request object. - * @param {Object} res - The Express response object. - * @returns {Promise} The JSON response with the added peer data and a success message. - * @throws {Object} The JSON response with an error message if an error occurred while adding the peer. - */ -const addPeerToEvent = async (req, res) => { - try { - const data = await eventQuery.addPeerToEvent({ - peerId: req.body.peerId, - name: req.body.name, - role: req.body.role, - joinedAt: req.body.joinedAt, - eventId: req.params.id, - }); - return res.status(200).json({ - data, - message: `Selected Participant is added to the event.`, - }); - } catch (error) { - logger.error({ error }); - return res.status(500).json({ - error: error.code, - message: "You can't add selected Participant. Please ask Admin or Host for help.", - }); - } -}; - -/** - * Kicks out a peer from an event. - * - * @async - * @function - * @param {Object} req - The Express request object. - * @param {Object} res - The Express response object. - * @returns {Promise} The JSON response with a success message if the peer is successfully kicked out. - * @throws {Object} The JSON response with an error message if an error occurred while kicking out the peer. - */ -const kickoutPeer = async (req, res) => { - const { id } = req.params; - const payload = { - peer_id: req.body.peerId, - reason: req.body.reason, - }; - - try { - await apiService.post(`/active-rooms/${id}/remove-peers`, payload); - await eventQuery.kickoutPeer({ eventId: id, peerId: payload.peer_id, reason: req.body.reason }); - return res.status(200).json({ - message: `Selected Participant is removed from event.`, - }); - } catch (error) { - logger.error({ error }); - return res.status(500).json({ - error: error.code, - message: "You can't remove selected Participant from Remove, Please ask Admin or Host for help.", - }); - } -}; - module.exports = { createEvent, getAllEvents, @@ -266,6 +200,4 @@ module.exports = { getEventById, updateEvent, endActiveEvent, - addPeerToEvent, - kickoutPeer, }; diff --git a/controllers/members.js b/controllers/members.js index f44f8e0ea..abb6af3f5 100644 --- a/controllers/members.js +++ b/controllers/members.js @@ -1,9 +1,8 @@ const ROLES = require("../constants/roles"); const members = require("../models/members"); const tasks = require("../models/tasks"); -const { SOMETHING_WENT_WRONG, INTERNAL_SERVER_ERROR } = require("../constants/errorMessages"); +const { SOMETHING_WENT_WRONG } = require("../constants/errorMessages"); const dataAccess = require("../services/dataAccessLayer"); -const { addLog } = require("../models/logs"); /** * Fetches the data about our members * @@ -84,38 +83,17 @@ const archiveMembers = async (req, res) => { try { const { username } = req.params; const user = await dataAccess.retrieveUsers({ username }); - const superUserId = req.userData.id; - const { reason } = req.body; - const roles = req?.userData?.roles; - const isReasonNullOrUndefined = !reason; - const isReasonEmptyOrWhitespace = /^\s*$/.test(reason); - if (isReasonNullOrUndefined || isReasonEmptyOrWhitespace) { - return res.boom.badRequest("Reason is required"); - } if (user?.userExists) { const successObject = await members.addArchiveRoleToMembers(user.user.id); if (successObject.isArchived) { return res.boom.badRequest("User is already archived"); } - const body = { - reason: reason, - archived_user: { - user_id: user.user.id, - username: user.user.username, - }, - archived_by: { - user_id: superUserId, - roles: roles, - }, - }; - - addLog("archived-details", {}, body); return res.status(204).send(); } return res.boom.notFound("User doesn't exist"); } catch (err) { logger.error(`Error while retriving contributions ${err}`); - return res.boom.badImplementation(INTERNAL_SERVER_ERROR); + return res.boom.badImplementation(SOMETHING_WENT_WRONG); } }; diff --git a/controllers/users.js b/controllers/users.js index 00d26a1dc..1a990dd9d 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -101,10 +101,9 @@ const getUsers = async (req, res) => { } const data = await dataAccess.retrieveUsers({ query: req.query }); - return res.json({ message: "Users returned successfully!", - users: data.users, + users: data.allUsers, links: { next: data.nextId ? getPaginationLink(req.query, "next", data.nextId) : "", prev: data.prevId ? getPaginationLink(req.query, "prev", data.prevId) : "", @@ -206,9 +205,10 @@ const getUsernameAvailabilty = async (req, res) => { const getSelfDetails = async (req, res) => { try { if (req.userData) { - const user = await dataAccess.retrieveUsers({ - userdata: req.userData, - }); + if (req.query.private) { + return res.send(req.userData); + } + const user = await dataAccess.retrieveUsers({ userdata: req.userData }); return res.send(user); } return res.boom.notFound("User doesn't exist"); @@ -407,7 +407,6 @@ const updateUser = async (req, res) => { const generateChaincode = async (req, res) => { try { const { id } = req.userData; - const chaincode = await chaincodeQuery.storeChaincode(id); await userQuery.addOrUpdate({ chaincode }, id); return res.json({ diff --git a/middlewares/authenticate.js b/middlewares/authenticate.js index 667d41b32..d72b2c71f 100644 --- a/middlewares/authenticate.js +++ b/middlewares/authenticate.js @@ -1,5 +1,5 @@ const authService = require("../services/authService"); -const dataAccess = require("../services/dataAccessLayer"); +const users = require("../models/users"); /** * Middleware to check if the user has been restricted. If user is restricted, @@ -54,7 +54,7 @@ module.exports = async (req, res, next) => { const { userId } = authService.verifyAuthToken(token); // add user data to `req.userData` for further use - const userData = await dataAccess.retrieveUsers({ id: userId }); + const userData = await users.fetchUser({ userId }); req.userData = userData.user; return checkRestricted(req, res, next); @@ -79,7 +79,8 @@ module.exports = async (req, res, next) => { }); // add user data to `req.userData` for further use - req.userData = await dataAccess.retrieveUsers({ id: userId }); + req.userData = await users.fetchUser({ userId }); + return checkRestricted(req, res, next); } else { return res.boom.unauthorized("Unauthenticated User"); diff --git a/middlewares/validators/events.js b/middlewares/validators/events.js index 10e479fc3..c5196f2c7 100644 --- a/middlewares/validators/events.js +++ b/middlewares/validators/events.js @@ -100,50 +100,6 @@ const endActiveEvent = async (req, res, next) => { } }; -const addPeerToEvent = async (req, res, next) => { - const { id } = req.params; - const { peerId, name, role, joinedAt } = req.body; - - const schema = joi.object({ - peerId: joi.string().required(), - name: joi.string().required(), - id: joi.string().required(), - role: joi.string().required(), - joinedAt: joi.date().required(), - }); - - const validationOptions = { abortEarly: false }; - - try { - await schema.validateAsync({ peerId, name, id, role, joinedAt }, validationOptions); - next(); - } catch (error) { - logger.error(`Error while adding a peer to the event: ${error}`); - res.boom.badRequest(error.details[0].message); - } -}; - -const kickoutPeer = async (req, res, next) => { - const { id } = req.params; - const { peerId, reason } = req.body; - - const schema = joi.object({ - id: joi.string().required(), - peerId: joi.string().required(), - reason: joi.string().required(), - }); - - const validationOptions = { abortEarly: false }; - - try { - await schema.validateAsync({ id, peerId, reason }, validationOptions); - next(); - } catch (error) { - logger.error(`We encountered some error while removing selected Participant from event: ${error}`); - res.boom.badRequest(error.details[0].message); - } -}; - module.exports = { createEvent, getAllEvents, @@ -151,6 +107,4 @@ module.exports = { getEventById, updateEvent, endActiveEvent, - addPeerToEvent, - kickoutPeer, }; diff --git a/models/discordactions.js b/models/discordactions.js index c8e30230d..cb93d3336 100644 --- a/models/discordactions.js +++ b/models/discordactions.js @@ -114,41 +114,10 @@ const updateDiscordImageForVerification = async (userDiscordId) => { } }; -const getNumberOfMemberForGroups = async (groups = []) => { - try { - if (!groups.length) { - return []; - } - const roleIds = groups.map((group) => group.roleid); - - const snapshots = await memberRoleModel.where("roleid", "in", roleIds).get(); - const roleCount = {}; - - snapshots.forEach((doc) => { - const roleToMemberMapping = doc.data(); - - if (roleCount[roleToMemberMapping.roleid]) { - roleCount[roleToMemberMapping.roleid] += 1; - } else { - roleCount[roleToMemberMapping.roleid] = 1; - } - }); - - return groups.map((group) => ({ - ...group, - memberCount: roleCount[group.roleid] || 0, - })); - } catch (err) { - logger.error("Error while counting members for each group", err); - throw err; - } -}; - module.exports = { createNewRole, getAllGroupRoles, addGroupRoleToMember, isGroupRoleExists, updateDiscordImageForVerification, - getNumberOfMemberForGroups, }; diff --git a/models/events.js b/models/events.js index 46077ced2..b78f2fbfe 100644 --- a/models/events.js +++ b/models/events.js @@ -1,9 +1,7 @@ -const Firestore = require("@google-cloud/firestore"); const firestore = require("../utils/firestore"); const logger = require("../utils/logger"); const eventModel = firestore.collection("events"); -const peerModel = firestore.collection("peers"); /** * Creates a new event document in Firestore and returns the data for the created document. @@ -68,112 +66,8 @@ const endActiveEvent = async ({ id, reason, lock }) => { } }; -/** - * Adds a peer to an event in the Firestore database. - * @async - * @function - * @param {Object} peerData - The data of the peer to be added. - * @param {string} peerData.name - The name of the peer. - * @param {string} peerData.eventId - The unique identifier of the event the peer is being added to. - * @param {string} peerData.role - The role of the peer in the event. - * @param {Date} peerData.joinedAt - The timestamp indicating when the peer joined the event. - * @returns {Promise} The data of the added peer. - * @throws {Error} If an error occurs while adding the peer to the event. - */ - -const addPeerToEvent = async (peerData) => { - try { - const batch = firestore.batch(); - - const peerRef = peerModel.doc(peerData.peerId); - const peerDocSnapshot = await peerRef.get(); - - if (!peerDocSnapshot.exists) { - // If the peer document doesn't exist, create a new one - const peerDocData = { - peerId: peerData.peerId, - name: peerData.name, - joinedEvents: [ - { - event_id: peerData.eventId, - role: peerData.role, - joined_at: peerData.joinedAt, - }, - ], - }; - batch.set(peerRef, peerDocData); - } else { - // If the peer document exists, update the joinedEvents array - batch.update(peerRef, { - joinedEvents: Firestore.FieldValue.arrayUnion({ - event_id: peerData.eventId, - role: peerData.role, - joined_at: peerData.joinedAt, - }), - }); - } - - const eventRef = eventModel.doc(peerData.eventId); - batch.update(eventRef, { - peers: Firestore.FieldValue.arrayUnion(peerRef.id), - }); - - await batch.commit(); - - const updatedPeerSnapshot = await peerRef.get(); - return updatedPeerSnapshot.data(); - } catch (error) { - logger.error("Error in adding peer to the event", error); - throw error; - } -}; - -/** - * Removes a peer from an event and marks them as kicked out in the Firestore database. - * @async - * @function - * @param {Object} params - The parameters for kicking out the peer. - * @param {string} params.eventId - The unique identifier of the event from which the peer is being kicked out. - * @param {string} params.peerId - The unique identifier of the peer being kicked out. - * @param {string} params.reason - The reason for kicking out the peer from the event. - * @returns {Promise} The updated data of the kicked-out peer. - * @throws {Error} If the peer is not found or is not part of the specified event. - */ -const kickoutPeer = async ({ eventId, peerId, reason }) => { - try { - const peerRef = peerModel.doc(peerId); - const peerSnapshot = await peerRef.get(); - - if (!peerSnapshot.exists) { - throw new Error("Participant not found"); - } - - const peerData = peerSnapshot.data(); - const joinedEvents = peerData.joinedEvents; - - const eventIndex = joinedEvents.findIndex((event) => event.event_id === eventId); - if (eventIndex === -1) { - throw new Error("Participant is not part of the specified event"); - } - - const updatedJoinedEvents = joinedEvents.map((event, index) => - index === eventIndex ? { ...event, left_at: new Date(), reason: reason, isKickedout: true } : event - ); - - await peerRef.update({ joinedEvents: updatedJoinedEvents }); - - const updatedPeerSnapshot = await peerRef.get(); - return updatedPeerSnapshot.data(); - } catch (error) { - logger.error("Error in removing peer from the event.", error); - throw error; - } -}; - module.exports = { createEvent, updateEvent, endActiveEvent, - addPeerToEvent, - kickoutPeer, }; diff --git a/models/logs.js b/models/logs.js index 8779d0601..80daf676f 100644 --- a/models/logs.js +++ b/models/logs.js @@ -3,7 +3,6 @@ const { getBeforeHourTime } = require("../utils/time"); const logsModel = firestore.collection("logs"); const admin = require("firebase-admin"); const { logType } = require("../constants/logs"); -const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages"); /** * Adds log @@ -23,7 +22,7 @@ const addLog = async (type, meta, body) => { return await logsModel.add(log); } catch (err) { logger.error("Error in adding log", err); - throw new Error(INTERNAL_SERVER_ERROR); + throw err; } }; @@ -43,27 +42,14 @@ const fetchLogs = async (query, param) => { } }); - const { limit, lastDocId, userId } = query; + const { limit, lastDocId } = query; let lastDoc; const limitDocuments = Number(limit); if (lastDocId) { lastDoc = await logsModel.doc(lastDocId).get(); } - if (userId) { - const logsSnapshot = await logsModel - .where("type", "==", param) - .where("body.archived_user.user_id", "==", userId) - .orderBy("timestamp", "desc") - .get(); - const logs = []; - logsSnapshot.forEach((doc) => { - logs.push({ - ...doc.data(), - }); - }); - return logs; - } + const logsSnapshotQuery = call.orderBy("timestamp", "desc").startAfter(lastDoc ?? ""); const snapshot = limit ? await logsSnapshotQuery.limit(limitDocuments).get() @@ -78,7 +64,7 @@ const fetchLogs = async (query, param) => { return logs; } catch (err) { logger.error("Error in adding log", err); - throw new Error(INTERNAL_SERVER_ERROR); + throw err; } }; diff --git a/models/users.js b/models/users.js index 9915d2baf..5aeac0924 100644 --- a/models/users.js +++ b/models/users.js @@ -584,31 +584,6 @@ const fetchUsersWithToken = async () => { return users; } catch (err) { logger.error(`Error while fetching all users with tokens field: ${err}`); - return []; - } -}; -/** - * - * @param {[string]} userIds Array id's of user - * @returns Object containing the details of the users whose userId was provided. - */ -const fetchUserByIds = async (userIds = []) => { - if (userIds.length === 0) { - return {}; - } - try { - const users = {}; - const usersRefs = userIds.map((docId) => userModel.doc(docId)); - const documents = await firestore.getAll(...usersRefs); - documents.forEach((snapshot) => { - if (snapshot.exists) { - users[snapshot.id] = snapshot.data(); - } - }); - - return users; - } catch (err) { - logger.error("Error retrieving user data", err); throw err; } }; @@ -691,5 +666,4 @@ module.exports = { fetchUsersWithToken, removeGitHubToken, getUsersByRole, - fetchUserByIds, }; diff --git a/routes/events.js b/routes/events.js index 5a2b2ce8c..9c5df3116 100644 --- a/routes/events.js +++ b/routes/events.js @@ -10,7 +10,5 @@ router.post("/join", eventsValidator.joinEvent, events.joinEvent); router.get("/:id", eventsValidator.getEventById, events.getEventById); router.patch("/", authenticate, eventsValidator.updateEvent, events.updateEvent); router.patch("/end", authenticate, eventsValidator.endActiveEvent, events.endActiveEvent); -router.post("/:id/peers", authenticate, eventsValidator.addPeerToEvent, events.addPeerToEvent); -router.patch("/:id/peers/kickout", authenticate, eventsValidator.kickoutPeer, events.kickoutPeer); module.exports = router; diff --git a/services/dataAccessLayer.js b/services/dataAccessLayer.js index 4810e1bbf..83c4ef3bd 100644 --- a/services/dataAccessLayer.js +++ b/services/dataAccessLayer.js @@ -1,17 +1,8 @@ const userQuery = require("../models/users"); const members = require("../models/members"); -const { ROLE_LEVEL, KEYS_NOT_ALLOWED, ACCESS_LEVEL } = require("../constants/userDataLevels"); +const { USER_SENSITIVE_DATA } = require("../constants/users"); -const retrieveUsers = async ({ - id = null, - username = null, - usernames = null, - query = null, - userdata, - level = ACCESS_LEVEL.PUBLIC, - role = null, - userIds = [], -}) => { +const retrieveUsers = async ({ id = null, username = null, usernames = null, query = null, userdata }) => { if (id || username) { let result; if (id != null) { @@ -19,87 +10,66 @@ const retrieveUsers = async ({ } else { result = await userQuery.fetchUser({ username: username }); } - const user = levelSpecificAccess(result.user, level, role); - result.user = user; + removeSensitiveInfo(result.user); return result; } else if (usernames) { const { users } = await userQuery.fetchUsers(usernames); - const result = []; - users.forEach((userdata) => { - const user = levelSpecificAccess(userdata, level, role); - result.push(user); + users.forEach((element) => { + removeSensitiveInfo(element); }); - return result; - } else if (userIds.length > 0) { - const userDetails = await userQuery.fetchUserByIds(userIds); - Object.keys(userDetails).forEach((userId) => { - removeSensitiveInfo(userDetails[userId]); - }); - return userDetails; + return users; } else if (query) { const { allUsers, nextId, prevId } = await userQuery.fetchPaginatedUsers(query); - const users = []; - allUsers.forEach((userdata) => { - const user = levelSpecificAccess(userdata, level, role); - users.push(user); + allUsers.forEach((element) => { + removeSensitiveInfo(element); }); - return { users, nextId, prevId }; + return { allUsers, nextId, prevId }; } else { - const result = await userQuery.fetchUser({ userId: userdata.id }); - return levelSpecificAccess(result.user, level, role); + removeSensitiveInfo(userdata); + return userdata; } }; -const retrieveDiscordUsers = async (level = ACCESS_LEVEL.PUBLIC, role = null) => { +const retrieveDiscordUsers = async () => { const users = await userQuery.getDiscordUsers(); - const usersData = []; - users.forEach((userdata) => { - const user = levelSpecificAccess(userdata, level, role); - usersData.push(user); + users.forEach((element) => { + removeSensitiveInfo(element); }); - return usersData; + return users; }; const retreiveFilteredUsers = async (query) => { const users = await userQuery.getUsersBasedOnFilter(query); - users.forEach((userdata) => { - removeSensitiveInfo(userdata); + users.forEach((element) => { + removeSensitiveInfo(element); }); return users; }; const retrieveMembers = async (query) => { const allUsers = await members.fetchUsers(query); - allUsers.forEach((userdata) => { - removeSensitiveInfo(userdata); + allUsers.forEach((element) => { + removeSensitiveInfo(element); }); return allUsers; }; const retrieveUsersWithRole = async (role) => { const users = await members.fetchUsersWithRole(role); - users.forEach((userdata) => { - removeSensitiveInfo(userdata); + users.forEach((element) => { + removeSensitiveInfo(element); }); return users; }; -const removeSensitiveInfo = function (obj, level = ACCESS_LEVEL.PUBLIC) { - for (let i = 0; i < KEYS_NOT_ALLOWED[level].length; i++) { - if (Object.prototype.hasOwnProperty.call(obj, KEYS_NOT_ALLOWED[level][i])) { - delete obj[KEYS_NOT_ALLOWED[level][i]]; +const removeSensitiveInfo = function (obj) { + for (let i = 0; i < USER_SENSITIVE_DATA.length; i++) { + if (Object.prototype.hasOwnProperty.call(obj, USER_SENSITIVE_DATA[i])) { + delete obj[USER_SENSITIVE_DATA[i]]; } } }; -const levelSpecificAccess = (user, level = ACCESS_LEVEL.PUBLIC, role = null) => { - if (level === ACCESS_LEVEL.PUBLIC || ROLE_LEVEL[level].includes(role)) { - removeSensitiveInfo(user, level); - return user; - } - return "unauthorized"; -}; - module.exports = { retrieveUsers, removeSensitiveInfo, @@ -107,5 +77,4 @@ module.exports = { retrieveMembers, retrieveUsersWithRole, retreiveFilteredUsers, - levelSpecificAccess, }; diff --git a/test/fixtures/discordactions/discordactions.js b/test/fixtures/discordactions/discordactions.js index 0a84b617d..f14c57b52 100644 --- a/test/fixtures/discordactions/discordactions.js +++ b/test/fixtures/discordactions/discordactions.js @@ -1,7 +1,7 @@ const groupData = [ - { rolename: "Group 1", roleid: 1 }, - { rolename: "Group 2", roleid: 2 }, - { rolename: "Group 3", roleid: 3 }, + { id: "1", name: "Group 1" }, + { id: "2", name: "Group 2" }, + { id: "3", name: "Group 3" }, ]; const roleData = { diff --git a/test/fixtures/logs/archievedUsers.js b/test/fixtures/logs/archievedUsers.js deleted file mode 100644 index 66f9b3497..000000000 --- a/test/fixtures/logs/archievedUsers.js +++ /dev/null @@ -1,68 +0,0 @@ -const archivedUserDetailsModal = [ - { - type: "archived-details", - meta: {}, - body: { - reason: "test reason", - archived_user: { user_id: "R5kljdsleH4Gr2t7tvr0Z", username: "testUser1" }, - archived_by: { - user_id: "ReMyuklislajwooncVL", - roles: { - in_discord: true, - super_user: false, - member: true, - archived: false, - }, - }, - }, - timestamp: { - _seconds: 1657193216, - _nanoseconds: 912000000, - }, - }, - { - type: "archived-details", - meta: {}, - body: { - reason: "test reason", - archived_user: { user_id: "R5kljdsleH4Gr2t7tvr0Z", username: "testUser1" }, - archived_by: { - user_id: "ReMyuklislajwooncVL", - roles: { - in_discord: true, - super_user: false, - member: true, - archived: false, - }, - }, - }, - timestamp: { - _seconds: 1657193216, - _nanoseconds: 912000000, - }, - }, - { - type: "archived-details", - meta: {}, - body: { - reason: "test reason", - archived_user: { user_id: "Efskee4Gr2t7tvr0Z", username: "testUser2" }, - archived_by: { - user_id: "ReMyuklislajwooncVL", - roles: { - in_discord: true, - super_user: false, - member: true, - archived: false, - }, - }, - }, - timestamp: { - _seconds: 1657193216, - _nanoseconds: 912000000, - }, - }, -]; -module.exports = { - archivedUserDetailsModal, -}; diff --git a/test/fixtures/user/user.js b/test/fixtures/user/user.js index 2123328f4..caf2735c5 100644 --- a/test/fixtures/user/user.js +++ b/test/fixtures/user/user.js @@ -267,6 +267,12 @@ module.exports = () => { linkedin_id: "testuser1", github_id: "testuser1", github_display_name: "Test User", + phone: "1234567890", + email: "tu@gmail.com", + chaincode: "1234", + tokens: { + githubAccessToken: "githubAccessToken", + }, roles: { member: true, }, @@ -323,29 +329,6 @@ module.exports = () => { twitter_id: "ramsingh123", linkedin_id: "ramsingh123", }, - { - username: "testuser3", - first_name: "test3", - last_name: "user3", - yoe: 1, - img: "./img.png", - linkedin_id: "testuser1", - github_id: "testuser", - github_display_name: "Test User 3", - phone: "1234567890", - email: "abcd@gmail.com", - chaincode: "12345", - tokens: { - githubAccessToken: "githubAccessToken", - }, - roles: { - member: true, - }, - picture: { - publicId: "profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar", - url: "https://res.cloudinary.com/realdevsquad/image/upload/v1667685133/profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar.jpg", - }, - }, { username: "sahsisunny", first_name: "sunny", diff --git a/test/integration/discord.test.js b/test/integration/discord.test.js index 9a801157c..5147efee3 100644 --- a/test/integration/discord.test.js +++ b/test/integration/discord.test.js @@ -8,14 +8,6 @@ const authService = require("../../services/authService"); const userData = require("../fixtures/user/user")(); const { requestRoleData } = require("../fixtures/discordactions/discordactions"); -const firestore = require("../../utils/firestore"); -const discordRoleModel = firestore.collection("discord-roles"); -const userModel = firestore.collection("users"); - -const { addGroupRoleToMember } = require("../../models/discordactions"); - -const { groupData } = require("../fixtures/discordactions/discordactions"); - const cookieName = config.get("userToken.cookieName"); let userId; @@ -67,29 +59,6 @@ describe("test discord actions", function () { const user = { ...userData[4], discordId: "123456789" }; userId = await addUser(user); jwt = authService.generateAuthToken({ userId }); - - let allIds = []; - - const addUsersPromises = userData.map((user) => userModel.add({ ...user })); - const responses = await Promise.all(addUsersPromises); - allIds = responses.map((response) => response.id); - - const addRolesPromises = [ - discordRoleModel.add({ roleid: groupData[0].roleid, rolename: groupData[0].rolename, createdBy: allIds[1] }), - discordRoleModel.add({ roleid: groupData[1].roleid, rolename: groupData[1].rolename, createdBy: allIds[0] }), - ]; - await Promise.all(addRolesPromises); - - const addGroupRolesPromises = [ - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: allIds[0] }), - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: allIds[1] }), - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: allIds[1] }), - addGroupRoleToMember({ roleid: groupData[1].roleid, userid: allIds[0] }), - ]; - await Promise.all(addGroupRolesPromises); - }); - afterEach(async function () { - await cleanDb(); }); it("returns 200 for active users get method", function (done) { diff --git a/test/integration/discordactions.test.js b/test/integration/discordactions.test.js index 928091bc5..74ec16085 100644 --- a/test/integration/discordactions.test.js +++ b/test/integration/discordactions.test.js @@ -16,11 +16,6 @@ const cookieName = config.get("userToken.cookieName"); const firestore = require("../../utils/firestore"); const { userPhotoVerificationData } = require("../fixtures/user/photo-verification"); const photoVerificationModel = firestore.collection("photo-verification"); -const discordRoleModel = firestore.collection("discord-roles"); -const userModel = firestore.collection("users"); - -const { groupData } = require("../fixtures/discordactions/discordactions"); -const { addGroupRoleToMember } = require("../../models/discordactions"); chai.use(chaiHttp); describe("Discord actions", function () { @@ -90,54 +85,4 @@ describe("Discord actions", function () { }); }); }); - - describe("GET /discord-actions/groups", function () { - before(async function () { - let allIds = []; - - const addUsersPromises = userData.map((user) => userModel.add({ ...user })); - const responses = await Promise.all(addUsersPromises); - allIds = responses.map((response) => response.id); - - const addRolesPromises = [ - discordRoleModel.add({ roleid: groupData[0].roleid, rolename: groupData[0].rolename, createdBy: allIds[1] }), - discordRoleModel.add({ roleid: groupData[1].roleid, rolename: groupData[1].rolename, createdBy: allIds[0] }), - ]; - await Promise.all(addRolesPromises); - - const addGroupRolesPromises = [ - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: allIds[0] }), - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: allIds[1] }), - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: allIds[1] }), - addGroupRoleToMember({ roleid: groupData[1].roleid, userid: allIds[0] }), - ]; - await Promise.all(addGroupRolesPromises); - }); - - after(async function () { - await cleanDb(); - }); - - it("should successfully return all groups detail", function (done) { - chai - .request(app) - .get(`/discord-actions/groups`) - .set("cookie", `${cookieName}=${superUserAuthToken}`) - .end((err, res) => { - if (err) { - return done(err); - } - - expect(res).to.have.status(200); - expect(res.body).to.be.an("object"); - // Verify presence of specific properties in each group - const expectedProps = ["roleid", "rolename", "memberCount", "firstName", "lastName", "image"]; - res.body.groups.forEach((group) => { - expect(group).to.include.all.keys(expectedProps); - }); - expect(res.body.message).to.equal("Roles fetched successfully!"); - return done(); - }); - }); - }); }); diff --git a/test/integration/members.test.js b/test/integration/members.test.js index ee74b0689..7788b8f0e 100644 --- a/test/integration/members.test.js +++ b/test/integration/members.test.js @@ -12,9 +12,6 @@ const userData = require("../fixtures/user/user")(); const config = require("config"); const cookieName = config.get("userToken.cookieName"); -const Sinon = require("sinon"); -const { INTERNAL_SERVER_ERROR } = require("../../constants/errorMessages"); -const members = require("../../models/members"); chai.use(chaiHttp); @@ -261,42 +258,16 @@ describe("Members", function () { }); describe("PATCH /members/archiveMembers/:username", function () { - let archiveRoleToMemberStub; beforeEach(async function () { const superUserId = await addUser(superUser); jwt = authService.generateAuthToken({ userId: superUserId }); }); - afterEach(async function () { - Sinon.restore(); - await cleanDb(); - }); - it("Should return an object with status 500 and an error message", function (done) { - archiveRoleToMemberStub = Sinon.stub(members, "addArchiveRoleToMembers"); - archiveRoleToMemberStub.throws(new Error(INTERNAL_SERVER_ERROR)); - addUser(userToBeArchived).then(() => { - chai - .request(app) - .patch(`/members/archiveMembers/${userToBeArchived.username}`) - .set("cookie", `${cookieName}=${jwt}`) - .send({ reason: "some reason" }) - .end((err, res) => { - if (err) { - return done(err); - } - expect(res).to.have.status(500); - expect(res.body).to.be.a("object"); - expect(res.body.message).to.equal(INTERNAL_SERVER_ERROR); - return done(); - }); - }); - }); it("Should return 404 if user doesn't exist", function (done) { chai .request(app) .patch(`/members/archiveMembers/${userDoesNotExists.username}`) .set("cookie", `${cookieName}=${jwt}`) - .send({ reason: "some reason" }) .end((err, res) => { if (err) { return done(err); @@ -307,31 +278,13 @@ describe("Members", function () { return done(); }); }); - it("Should return 400 if body is empty", function (done) { - chai - .request(app) - .patch(`/members/archiveMembers/${userToBeArchived.username}`) - .set("cookie", `${cookieName}=${jwt}`) - .send({}) - .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.equal("Reason is required"); - return done(); - }); - }); it("Should archive the user", function (done) { addUser(userToBeArchived).then(() => { chai .request(app) .patch(`/members/archiveMembers/${userToBeArchived.username}`) .set("cookie", `${cookieName}=${jwt}`) - .send({ reason: "some reason" }) .end((err, res) => { if (err) { return done(err); @@ -352,7 +305,6 @@ describe("Members", function () { .request(app) .patch(`/members/archiveMembers/${userAlreadyArchived.username}`) .set("cookie", `${cookieName}=${jwt}`) - .send({ reason: "some reason" }) .end((err, res) => { if (err) { return done(err); @@ -366,5 +318,26 @@ describe("Members", function () { }); }); }); + + it("Should return 401 if user is not a super user", function (done) { + addUser(nonSuperUser).then((nonSuperUserId) => { + const nonSuperUserJwt = authService.generateAuthToken({ userId: nonSuperUserId }); + chai + .request(app) + .patch(`/members/moveToMembers/${nonSuperUser.username}`) + .set("cookie", `${cookieName}=${nonSuperUserJwt}`) + .end((err, res) => { + if (err) { + return done(err); + } + + expect(res).to.have.status(401); + expect(res.body).to.be.a("object"); + expect(res.body.message).to.equal("You are not authorized for this action."); + + return done(); + }); + }); + }); }); }); diff --git a/test/integration/tasks.test.js b/test/integration/tasks.test.js index 906f108de..668411976 100644 --- a/test/integration/tasks.test.js +++ b/test/integration/tasks.test.js @@ -767,7 +767,7 @@ describe("Tasks", function () { }); it("Should return Forbidden error if task is not assigned to self", async function () { - const userId = await addUser(userData[0]); + const { userId } = await addUser(userData[0]); const jwt = authService.generateAuthToken({ userId }); const res = await chai.request(app).patch(`/tasks/self/${taskId1}`).set("cookie", `${cookieName}=${jwt}`); diff --git a/test/integration/users.test.js b/test/integration/users.test.js index d41b5e154..7f3f58272 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -290,6 +290,7 @@ describe("Users", function () { expect(res.body.users).to.be.a("array"); expect(res.body.users[0]).to.not.have.property("phone"); expect(res.body.users[0]).to.not.have.property("email"); + expect(res.body.users[0]).to.not.have.property("tokens"); expect(res.body.users[0]).to.not.have.property("chaincode"); return done(); @@ -314,6 +315,7 @@ describe("Users", function () { }); expect(res.body.users[0]).to.not.have.property("phone"); expect(res.body.users[0]).to.not.have.property("email"); + expect(res.body.users[0]).to.not.have.property("tokens"); expect(res.body.users[0]).to.not.have.property("chaincode"); return done(); }); @@ -339,6 +341,7 @@ describe("Users", function () { expect(res.body.users.length).to.equal(1); expect(res.body.users[0]).to.not.have.property("phone"); expect(res.body.users[0]).to.not.have.property("email"); + expect(res.body.users[0]).to.not.have.property("tokens"); expect(res.body.users[0]).to.not.have.property("chaincode"); return done(); }); @@ -549,11 +552,31 @@ describe("Users", function () { expect(res.body).to.be.a("object"); expect(res.body).to.not.have.property("phone"); expect(res.body).to.not.have.property("email"); + expect(res.body).to.not.have.property("tokens"); expect(res.body).to.not.have.property("chaincode"); return done(); }); }); + it("Should return details with phone and email when query 'private' is true", function (done) { + chai + .request(app) + .get("/users/self") + .query({ private: true }) + .set("cookie", `${cookieName}=${jwt}`) + .end((err, res) => { + if (err) { + return done(); + } + + expect(res).to.have.status(200); + expect(res.body).to.be.a("object"); + expect(res.body).to.have.property("phone"); + expect(res.body).to.have.property("email"); + return done(); + }); + }); + it("Should return 401 if not logged in", function (done) { chai .request(app) @@ -593,6 +616,7 @@ describe("Users", function () { expect(res.body.user).to.be.a("object"); expect(res.body.user).to.not.have.property("phone"); expect(res.body.user).to.not.have.property("email"); + expect(res.body.user).to.not.have.property("tokens"); expect(res.body.user).to.not.have.property("chaincode"); return done(); }); @@ -634,6 +658,7 @@ describe("Users", function () { expect(res.body.user).to.be.a("object"); expect(res.body.user).to.not.have.property("phone"); expect(res.body.user).to.not.have.property("email"); + expect(res.body.user).to.not.have.property("tokens"); expect(res.body.user).to.not.have.property("chaincode"); return done(); }); diff --git a/test/integration/usersFilter.test.js b/test/integration/usersFilter.test.js index 1263b2f77..9e0f5ed97 100644 --- a/test/integration/usersFilter.test.js +++ b/test/integration/usersFilter.test.js @@ -365,6 +365,7 @@ describe("Filter Users", function () { res.body.users.forEach((user) => { expect(user).to.not.have.property("phone"); expect(user).to.not.have.property("email"); + expect(user).to.not.have.property("tokens"); }); return done(); }); diff --git a/test/unit/models/discordactions.test.js b/test/unit/models/discordactions.test.js index 2405c0a70..6c3b2fa66 100644 --- a/test/unit/models/discordactions.test.js +++ b/test/unit/models/discordactions.test.js @@ -12,7 +12,6 @@ const { isGroupRoleExists, addGroupRoleToMember, updateDiscordImageForVerification, - getNumberOfMemberForGroups, } = require("../../../models/discordactions"); const { groupData, roleData, existingRole } = require("../../fixtures/discordactions/discordactions"); const cleanDb = require("../../utils/cleanDb"); @@ -244,40 +243,4 @@ describe("discordactions", function () { } }); }); - - describe("getNumberOfMemberForGroups", function () { - before(async function () { - await Promise.all([ - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: 1 }), - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: 2 }), - addGroupRoleToMember({ roleid: groupData[0].roleid, userid: 3 }), - addGroupRoleToMember({ roleid: groupData[1].roleid, userid: 1 }), - ]); - }); - - after(async function () { - await cleanDb(); - }); - - it("should return an empty array if the parameter is an empty array", async function () { - const result = await getNumberOfMemberForGroups([]); - expect(result).to.be.an("array"); - expect(result.length).to.equal(0); - }); - - it("should return an empty array if the parameter no parameter is passed", async function () { - const result = await getNumberOfMemberForGroups(); - expect(result).to.be.an("array"); - expect(result.length).to.equal(0); - }); - - it("should return group details with memberCount details ", async function () { - const result = await getNumberOfMemberForGroups(groupData); - expect(result).to.deep.equal([ - { rolename: groupData[0].rolename, roleid: 1, memberCount: 3 }, - { rolename: groupData[1].rolename, roleid: 2, memberCount: 1 }, - { rolename: groupData[2].rolename, roleid: 3, memberCount: 0 }, - ]); - }); - }); }); diff --git a/test/unit/models/events.test.js b/test/unit/models/events.test.js index 49902f414..4dd937923 100644 --- a/test/unit/models/events.test.js +++ b/test/unit/models/events.test.js @@ -6,7 +6,6 @@ const firestore = require("../../../utils/firestore"); const eventQuery = require("../../../models/events"); const eventModel = firestore.collection("events"); -const peerModel = firestore.collection("peers"); const eventDataArray = require("../../fixtures/events/events")(); const eventData = eventDataArray[0]; @@ -18,100 +17,61 @@ describe("Events", function () { describe("createEvent", function () { it("should create a new event in firestore", async function () { + // Call the function with sample data const result = await eventQuery.createEvent(eventData); + // Add sample data to Firestore const data = (await eventModel.doc(eventData.room_id).get()).data(); + // Verify that the event was created expect(result).to.deep.equal(data); }); }); describe("updateEvent", function () { it("should update the enabled property of a event", async function () { + // Add sample data to Firestore const docRef = eventModel.doc(eventData.room_id); await docRef.set(eventData); + // Call the function with sample data await eventQuery.updateEvent({ id: "641e3b43a42edf3910cbc8bf", enabled: true }, eventModel); + // Get updated data from Firestore const docSnapshot = await eventModel.doc(docRef.id).get(); const data = docSnapshot.data(); + // Verify that the enabled property was updated expect(data.enabled).to.equal(true); }); }); describe("endActiveEvent", function () { it("should update the lock, reason, and status of a event", async function () { + // Add sample data to Firestore const docRef = await eventModel.add(eventData); try { + // Call the function with sample data await eventQuery.endActiveEvent({ id: docRef.id, reason: "test reason", lock: true, }); + // Get updated data from Firestore const docSnapshot = await eventModel.doc(docRef.id).get(); const data = docSnapshot.data(); + // Verify that the lock, reason, and status properties were updated expect(data.lock).to.equal(true); expect(data.reason).to.equal("test reason"); expect(data.status).to.equal("inactive"); } catch (error) { + // Check that the function threw an error expect(error).to.exist(); expect(error.message).to.equal("Error in enabling event."); } }); }); - - describe("addPeerToEvent", function () { - it("should create a new peer document if it doesn't exist", async function () { - const docRef = await eventModel.add(eventData); - - const peerData = { - peerId: "someid", - name: "NonExistingPeer", - eventId: docRef.id, - role: "participant", - joinedAt: new Date(), - }; - - const result = await eventQuery.addPeerToEvent(peerData); - - const docSnapshot = await peerModel.doc(result.peerId).get(); - const data = docSnapshot.data(); - - expect(data.name).to.equal(peerData.name); - expect(data.joinedEvents).to.have.lengthOf(1); - expect(data.joinedEvents[0].event_id).to.equal(peerData.eventId); - expect(data.joinedEvents[0].role).to.equal(peerData.role); - }); - - it("should update the joinedEvents array if the peer document exists", async function () { - const docRef = await eventModel.add(eventData); - - const peerData = { - peerId: "someid", - name: "ExistingPeer", - eventId: docRef.id, - role: "participant", - joinedAt: new Date(), - }; - - await peerModel.add({ - peerId: peerData.peerId, - name: peerData.name, - joinedEvents: [], - }); - - await eventQuery.addPeerToEvent(peerData); - - const docSnapshot = await peerModel.doc(peerData.peerId).get(); - const data = docSnapshot.data(); - - expect(data.joinedEvents).to.have.lengthOf(1); - expect(data.joinedEvents[0].event_id).to.equal(peerData.eventId); - expect(data.joinedEvents[0].role).to.equal(peerData.role); - }); - }); }); diff --git a/test/unit/models/logs.test.js b/test/unit/models/logs.test.js index 7526ba1ed..9d3df2ccd 100644 --- a/test/unit/models/logs.test.js +++ b/test/unit/models/logs.test.js @@ -4,17 +4,6 @@ const { expect } = chai; const cleanDb = require("../../utils/cleanDb"); const logsQuery = require("../../../models/logs"); const cacheData = require("../../fixtures/cloudflareCache/data"); -const logsData = require("../../fixtures/logs/archievedUsers"); -const app = require("../../../server"); -const Sinon = require("sinon"); -const { INTERNAL_SERVER_ERROR } = require("../../../constants/errorMessages"); -const userData = require("../../fixtures/user/user")(); -const addUser = require("../../utils/addUser"); -const cookieName = config.get("userToken.cookieName"); -const authService = require("../../../services/authService"); - -const superUser = userData[4]; -const userToBeMadeMember = userData[1]; describe("Logs", function () { after(async function () { @@ -48,77 +37,4 @@ describe("Logs", function () { expect(data[0].timestamp._nanoseconds).to.be.a("number"); }); }); - - describe("GET /logs/archived-details", function () { - let addLogsStub; - let jwt; - beforeEach(async function () { - const superUserId = await addUser(superUser); - jwt = authService.generateAuthToken({ userId: superUserId }); - await cleanDb(); - }); - afterEach(function () { - Sinon.restore(); - }); - - it("Should return an Internal server error message", async function () { - addLogsStub = Sinon.stub(logsQuery, "fetchLogs"); - addLogsStub.throws(new Error(INTERNAL_SERVER_ERROR)); - - addUser(userToBeMadeMember).then(() => { - const res = chai.request(app).get("/logs/archived-details").set("cookie", `${cookieName}=${jwt}`).send(); - - expect(res.body.message).to.equal(INTERNAL_SERVER_ERROR); - }); - }); - it("Should return empty array if no logs found", async function () { - const { type } = logsData.archivedUserDetailsModal[0]; - const query = {}; - - const data = await logsQuery.fetchLogs(query, type); - - expect(data).to.be.an("array").with.lengthOf(0); - }); - it("Should fetch all archived logs", async function () { - const { type, meta, body } = logsData.archivedUserDetailsModal[0]; - const query = {}; - - await logsQuery.addLog(type, meta, body); - const data = await logsQuery.fetchLogs(query, type); - - expect(data).to.be.an("array").with.lengthOf.greaterThan(0); - expect(data[0]).to.have.property("timestamp").that.is.an("object"); - expect(data[0].timestamp).to.have.property("_seconds").that.is.a("number"); - expect(data[0].timestamp).to.have.property("_nanoseconds").that.is.a("number"); - expect(data[0].body.archived_user).to.have.property("username").that.is.a("string"); - expect(data[0].body).to.have.property("reason").that.is.a("string"); - }); - it("Should fetch all archived logs for given user_id", async function () { - const { type, meta, body } = logsData.archivedUserDetailsModal[0]; - const query = { - userId: body.archived_user.user_id, - }; - await logsQuery.addLog(type, meta, body); - const data = await logsQuery.fetchLogs(query, type); - - expect(data).to.be.an("array").with.lengthOf.greaterThan(0); - expect(data[0]).to.have.property("timestamp").that.is.an("object"); - expect(data[0].timestamp).to.have.property("_seconds").that.is.a("number"); - expect(data[0].timestamp).to.have.property("_nanoseconds").that.is.a("number"); - expect(data[0].body).to.have.property("reason").that.is.a("string"); - }); - it("Should throw response status 404, if username is incorrect in the query", async function () { - const { type, meta, body } = logsData.archivedUserDetailsModal[0]; - const query = { - userId: "1234_test", // incorrect username - }; - await logsQuery.addLog(type, meta, body); - const data = await logsQuery.fetchLogs(query, type); - const response = await chai.request(app).get(`/logs/${type}/${query}`); - - expect(data).to.be.an("array").with.lengthOf(0); - expect(response).to.have.status(404); - expect(response.body.message).to.be.equal("Not Found"); - }); - }); }); diff --git a/test/unit/models/users.test.js b/test/unit/models/users.test.js index 474b28f11..42d7cad61 100644 --- a/test/unit/models/users.test.js +++ b/test/unit/models/users.test.js @@ -94,7 +94,7 @@ describe("users", function () { }); it("It should have created_At and updated_At fields", async function () { - const userData = userDataArray[15]; + const userData = userDataArray[14]; await users.addOrUpdate(userData); const githubUsername = "sahsisunny"; const { user, userExists } = await users.fetchUser({ githubUsername }); @@ -279,7 +279,7 @@ describe("users", function () { }); it("returns users with member role", async function () { const members = await users.getUsersByRole("member"); - expect(members.length).to.be.equal(7); + expect(members.length).to.be.equal(6); members.forEach((member) => { expect(member.roles.member).to.be.equal(true); }); @@ -290,32 +290,4 @@ describe("users", function () { }); }); }); - describe("fetch users by id", function () { - let allIds = []; - before(async function () { - const addUsersPromises = []; - userDataArray.forEach((user, index) => { - addUsersPromises.push(userModel.add({ ...user })); - }); - const responses = await Promise.all(addUsersPromises); - allIds = responses.map((response) => response.id); - }); - - after(async function () { - await cleanDb(); - }); - - it("should fetch the details of users whose ids are present in the array", async function () { - const randomIds = allIds.sort(() => 0.5 - Math.random()).slice(0, 3); // Select random ids from allIds - const result = await users.fetchUserByIds(randomIds); - const fetchedUserIds = Object.keys(result); - expect(fetchedUserIds).to.deep.equal(randomIds); - }); - - it("should return empty object if no ids are passed", async function () { - const result = await users.fetchUserByIds(); - const fetchedUserIds = Object.keys(result); - expect(fetchedUserIds).to.deep.equal([]); - }); - }); }); diff --git a/test/unit/services/dataAccessLayer.test.js b/test/unit/services/dataAccessLayer.test.js index 0a15dff9b..c12e8ad11 100644 --- a/test/unit/services/dataAccessLayer.test.js +++ b/test/unit/services/dataAccessLayer.test.js @@ -12,17 +12,14 @@ const { retrieveUsersWithRole, retrieveMembers, retreiveFilteredUsers, - levelSpecificAccess, } = require("../../../services/dataAccessLayer"); -const { KEYS_NOT_ALLOWED, ACCESS_LEVEL } = require("../../../constants/userDataLevels"); - const userData = require("../../fixtures/user/user")(); +const { USER_SENSITIVE_DATA } = require("../../../constants/users"); chai.use(chaiHttp); const expect = chai.expect; let fetchUserStub; - describe("Data Access Layer", function () { describe("retrieveUsers", function () { it("should fetch a single user by ID and remove sensitive info", async function () { @@ -31,7 +28,7 @@ describe("Data Access Layer", function () { const result = await retrieveUsers({ id: userData[12].id }); removeSensitiveInfo(userData[12]); expect(result.user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { + USER_SENSITIVE_DATA.forEach((key) => { expect(result.user).to.not.have.property(key); }); }); @@ -41,7 +38,7 @@ describe("Data Access Layer", function () { const result = await retrieveUsers({ username: userData[12].username }); removeSensitiveInfo(userData[12]); expect(result.user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { + USER_SENSITIVE_DATA.forEach((key) => { expect(result.user).to.not.have.property(key); }); }); @@ -51,10 +48,10 @@ describe("Data Access Layer", function () { fetchUserStub.returns(Promise.resolve({ users: [userData[12]] })); const result = await retrieveUsers({ usernames: [userData[12].username] }); removeSensitiveInfo(userData[12]); - result.forEach((user) => { - expect(user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(user).to.not.have.property(key); + result.forEach((element) => { + expect(element).to.deep.equal(userData[12]); + USER_SENSITIVE_DATA.forEach((key) => { + expect(element).to.not.have.property(key); }); }); }); @@ -65,10 +62,10 @@ describe("Data Access Layer", function () { const query = { page: 1 }; const result = await retrieveUsers({ query }); removeSensitiveInfo(userData[12]); - result.users.forEach((user) => { - expect(user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(user).to.not.have.property(key); + result.allUsers.forEach((element) => { + expect(element).to.deep.equal(userData[12]); + USER_SENSITIVE_DATA.forEach((key) => { + expect(element).to.not.have.property(key); }); }); }); @@ -77,7 +74,7 @@ describe("Data Access Layer", function () { const userdata = userData[12]; await retrieveUsers({ userdata }); removeSensitiveInfo(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { + USER_SENSITIVE_DATA.forEach((key) => { expect(userdata).to.not.have.property(key); }); }); @@ -88,10 +85,10 @@ describe("Data Access Layer", function () { const fetchUserStub = sinon.stub(userQuery, "getDiscordUsers"); fetchUserStub.returns(Promise.resolve([userData[12]])); const result = await retrieveDiscordUsers(); - result.forEach((user) => { - expect(user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(user).to.not.have.property(key); + result.forEach((element) => { + expect(element).to.deep.equal(userData[12]); + USER_SENSITIVE_DATA.forEach((key) => { + expect(element).to.not.have.property(key); }); }); }); @@ -103,10 +100,10 @@ describe("Data Access Layer", function () { fetchUserStub.returns(Promise.resolve([userData[12]])); const query = { showArchived: true }; const result = await retrieveUsersWithRole(query); - result.forEach((user) => { - expect(user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(user).to.not.have.property(key); + result.forEach((element) => { + expect(element).to.deep.equal(userData[12]); + USER_SENSITIVE_DATA.forEach((key) => { + expect(element).to.not.have.property(key); }); }); }); @@ -117,23 +114,10 @@ describe("Data Access Layer", function () { const fetchUserStub = sinon.stub(members, "fetchUsers"); fetchUserStub.returns(Promise.resolve([userData[12]])); const result = await retrieveMembers(); - result.forEach((user) => { - expect(user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(user).to.not.have.property(key); - }); - }); - }); - - it("should fetch multiple users details based on ids and remove sensitive data", async function () { - const fetchUserStub = sinon.stub(userQuery, "fetchUserByIds"); - fetchUserStub.returns(Promise.resolve({ [userData[12].id]: userData[12] })); - const result = await retrieveUsers({ userIds: [userData[12].id] }); - removeSensitiveInfo(userData[12]); - Object.keys(result).forEach((id) => { - expect(result[id]).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(result[id]).to.not.have.property(key); + result.forEach((element) => { + expect(element).to.deep.equal(userData[12]); + USER_SENSITIVE_DATA.forEach((key) => { + expect(element).to.not.have.property(key); }); }); }); @@ -145,10 +129,10 @@ describe("Data Access Layer", function () { fetchUserStub.returns(Promise.resolve([userData[12]])); const query = { state: "ACTIVE" }; const result = await retreiveFilteredUsers(query); - result.forEach((user) => { - expect(user).to.deep.equal(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(user).to.not.have.property(key); + result.forEach((element) => { + expect(element).to.deep.equal(userData[12]); + USER_SENSITIVE_DATA.forEach((key) => { + expect(element).to.not.have.property(key); }); }); }); @@ -156,37 +140,10 @@ describe("Data Access Layer", function () { describe("removeSensitiveInfo", function () { it("should remove sensitive information from the users object", function () { - removeSensitiveInfo(userData[12]); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { + removeSensitiveInfo(userData); + USER_SENSITIVE_DATA.forEach((key) => { expect(userData[12]).to.not.have.property(key); }); }); }); - - describe("levelSpecificAccess", function () { - it("should return the user object for PUBLIC level after removing all sensitive info", function () { - const result = levelSpecificAccess({ ...userData[12] }, ACCESS_LEVEL.PUBLIC); - KEYS_NOT_ALLOWED[ACCESS_LEVEL.PUBLIC].forEach((key) => { - expect(result).to.not.have.property(key); - }); - }); - - it('should return "unauthorized" for non-superuser role', function () { - const unauthorizedRole = "member"; - const result = levelSpecificAccess({ ...userData[12] }, ACCESS_LEVEL.PRIVATE, unauthorizedRole); - expect(result).to.equal("unauthorized"); - }); - - it("should keep sensitive info for valid role and level", function () { - const user = { ...userData[12], email: "a@b.com", phone: "7890654329", chaincode: "78906" }; - const role = "super_user"; - const level = ACCESS_LEVEL.PRIVATE; - const result = levelSpecificAccess(user, level, role); - KEYS_NOT_ALLOWED[level].forEach((key) => { - expect(result).to.not.have.property(key); - }); - expect(result).to.have.property("phone"); - expect(result).to.have.property("email"); - }); - }); });