From 158e8d932ccc43b71b50901718d0d657d130235a Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 15:46:26 +0200 Subject: [PATCH 1/8] fix: send project info only for PM and admin role users --- src/routes/copilotOpportunity/list.js | 10 ++++++++-- src/routes/copilotRequest/list.js | 10 +++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index 9a806290..264805b1 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -2,7 +2,7 @@ import _ from 'lodash'; import models from '../../models'; import util from '../../util'; -import DEFAULT_PAGE_SIZE from '../../constants'; +import DEFAULT_PAGE_SIZE, { USER_ROLE } from '../../constants'; module.exports = [ (req, res, next) => { @@ -15,6 +15,7 @@ module.exports = [ return util.handleError('Invalid sort criteria', null, req, next); } const sortParams = sort.split(' '); + const isAdminOrManager = util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN, USER_ROLE.PROJECT_MANAGER]); // Extract pagination parameters const page = parseInt(req.query.page, 10) || 1; @@ -42,7 +43,7 @@ module.exports = [ baseOrder.push([sortParams[0], sortParams[1]]); return models.CopilotOpportunity.findAll({ - include: [ + include: isAdminOrManager ?[ { model: models.CopilotRequest, as: 'copilotRequest', @@ -52,6 +53,11 @@ module.exports = [ as: 'project', attributes: ['name'], }, + ] : [ + { + model: models.CopilotRequest, + as: 'copilotRequest', + } ], order: baseOrder, limit, diff --git a/src/routes/copilotRequest/list.js b/src/routes/copilotRequest/list.js index ef36d26b..92b73614 100644 --- a/src/routes/copilotRequest/list.js +++ b/src/routes/copilotRequest/list.js @@ -4,7 +4,7 @@ import { Op, Sequelize } from 'sequelize'; import models from '../../models'; import util from '../../util'; import { PERMISSION } from '../../permissions/constants'; -import { DEFAULT_PAGE_SIZE } from '../../constants'; +import { DEFAULT_PAGE_SIZE, USER_ROLE } from '../../constants'; module.exports = [ (req, res, next) => { @@ -17,6 +17,8 @@ module.exports = [ return next(err); } + const isAdminOrManager = util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN, USER_ROLE.PROJECT_MANAGER]); + const page = parseInt(req.query.page, 10) || 1; const pageSize = parseInt(req.query.pageSize, 10) || DEFAULT_PAGE_SIZE; const offset = (page - 1) * pageSize; @@ -46,7 +48,7 @@ module.exports = [ let order = [[sortParams[0], sortParams[1]]]; const relationBasedSortParams = ['projectName']; const jsonBasedSortParams = ['opportunityTitle', 'projectType']; - if (relationBasedSortParams.includes(sortParams[0])) { + if (relationBasedSortParams.includes(sortParams[0]) && isAdminOrManager) { order = [ [{model: models.Project, as: 'project'}, 'name', sortParams[1]], ['id', 'DESC'] @@ -64,9 +66,11 @@ module.exports = [ return models.CopilotRequest.findAndCountAll({ where: whereCondition, - include: [ + include: isAdminOrManager ? [ { model: models.CopilotOpportunity, as: 'copilotOpportunity', required: false }, { model: models.Project, as: 'project', required: false }, + ] : [ + { model: models.CopilotOpportunity, as: 'copilotOpportunity', required: false }, ], order, limit: pageSize, From def3f5c9cd004bf9305b9c27bbeef4c1c61adf52 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 15:47:01 +0200 Subject: [PATCH 2/8] fix: send project info only for PM and admin role users --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8f587714..e2ac728e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -149,7 +149,7 @@ workflows: context : org-global filters: branches: - only: ['develop', 'migration-setup', 'PM-1612'] + only: ['develop', 'migration-setup', 'PM-1612', 'fix-project-exposing'] - deployProd: context : org-global filters: From 344671c19eab736e0adca32cd836752afc8f1c2f Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 15:59:00 +0200 Subject: [PATCH 3/8] fix: send project info only for PM and admin role users --- src/routes/copilotOpportunity/list.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index 264805b1..e64577f2 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -66,6 +66,11 @@ module.exports = [ .then((copilotOpportunities) => { const formattedOpportunities = copilotOpportunities.map((opportunity) => { const plainOpportunity = opportunity.get({ plain: true }); + // For users who are not admin or manager, we dont want to expose + // the project id + if (!isAdminOrManager) { + delete plainOpportunity.projectId; + } return Object.assign({}, plainOpportunity, plainOpportunity.copilotRequest ? plainOpportunity.copilotRequest.data : {}, { copilotRequest: undefined }, From 11ae715c6e2e2e9360bb7b19d77703be19b49dbb Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 17:28:28 +0200 Subject: [PATCH 4/8] fix: project object optional --- src/routes/copilotOpportunity/list.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index e64577f2..d4871e9d 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -66,6 +66,8 @@ module.exports = [ .then((copilotOpportunities) => { const formattedOpportunities = copilotOpportunities.map((opportunity) => { const plainOpportunity = opportunity.get({ plain: true }); + + req.debug.info(isAdminOrManager, 'admin or manager', plainOpportunity); // For users who are not admin or manager, we dont want to expose // the project id if (!isAdminOrManager) { From 3d2216a89791b6d8c8f910488811d6e0aa3199ae Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 17:50:55 +0200 Subject: [PATCH 5/8] fix: project object optional --- src/routes/copilotOpportunity/list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index d4871e9d..cf3796ab 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -67,7 +67,7 @@ module.exports = [ const formattedOpportunities = copilotOpportunities.map((opportunity) => { const plainOpportunity = opportunity.get({ plain: true }); - req.debug.info(isAdminOrManager, 'admin or manager', plainOpportunity); + req.log.debug(isAdminOrManager, 'admin or manager', plainOpportunity); // For users who are not admin or manager, we dont want to expose // the project id if (!isAdminOrManager) { From 6fb8f34a75dabc30935fe32d3a1ab062a91a649f Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 20:50:26 +0200 Subject: [PATCH 6/8] fix: project object optional --- src/routes/copilotOpportunity/list.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index cf3796ab..c0db026b 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -68,15 +68,18 @@ module.exports = [ const plainOpportunity = opportunity.get({ plain: true }); req.log.debug(isAdminOrManager, 'admin or manager', plainOpportunity); + + const formatted = Object.assign({}, plainOpportunity, + plainOpportunity.copilotRequest ? plainOpportunity.copilotRequest.data : {}, + { copilotRequest: undefined }, + ); + // For users who are not admin or manager, we dont want to expose // the project id if (!isAdminOrManager) { delete plainOpportunity.projectId; } - return Object.assign({}, plainOpportunity, - plainOpportunity.copilotRequest ? plainOpportunity.copilotRequest.data : {}, - { copilotRequest: undefined }, - ); + return formatted; }); return util.setPaginationHeaders(req, res, { count: copilotOpportunities.count, From f8f52e0fc9d749b5484f24510cc185fcf50ced99 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 20:53:06 +0200 Subject: [PATCH 7/8] fix: project object optional --- src/routes/copilotOpportunity/get.js | 23 +++++++++++++++++++---- src/routes/copilotOpportunity/list.js | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/routes/copilotOpportunity/get.js b/src/routes/copilotOpportunity/get.js index e4bad5c8..f71394fd 100644 --- a/src/routes/copilotOpportunity/get.js +++ b/src/routes/copilotOpportunity/get.js @@ -1,3 +1,4 @@ +import { USER_ROLE } from '../../constants'; import models from '../../models'; import util from '../../util'; @@ -8,9 +9,11 @@ module.exports = [ return util.handleError('Invalid opportunity ID', null, req, next, 400); } + const isAdminOrManager = util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN, USER_ROLE.PROJECT_MANAGER]); + return models.CopilotOpportunity.findOne({ where: { id }, - include: [ + include: isAdminOrManager ? [ { model: models.CopilotRequest, as: 'copilotRequest', @@ -27,17 +30,25 @@ module.exports = [ }, ] }, + ]: [ + { + model: models.CopilotRequest, + as: 'copilotRequest', + }, ], }) .then((copilotOpportunity) => { const plainOpportunity = copilotOpportunity.get({ plain: true }); - const memberIds = plainOpportunity.project.members && plainOpportunity.project.members.map((member) => member.userId); + const memberIds = (plainOpportunity.project && plainOpportunity.project.members && plainOpportunity.project.members.map((member) => member.userId)) || []; let canApplyAsCopilot = false; if (req.authUser) { canApplyAsCopilot = !memberIds.includes(req.authUser.userId) } - // This shouldn't be exposed to the clientside - delete plainOpportunity.project.members; + + if (plainOpportunity.project) { + // This shouldn't be exposed to the clientside + delete plainOpportunity.project.members; + } const formattedOpportunity = Object.assign({ members: memberIds, canApplyAsCopilot, @@ -45,6 +56,10 @@ module.exports = [ plainOpportunity.copilotRequest ? plainOpportunity.copilotRequest.data : {}, { copilotRequest: undefined }, ); + + if (!isAdminOrManager) { + delete formattedOpportunity.projectId; + } res.json(formattedOpportunity); }) .catch((err) => { diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index c0db026b..fb4065fd 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -73,11 +73,11 @@ module.exports = [ plainOpportunity.copilotRequest ? plainOpportunity.copilotRequest.data : {}, { copilotRequest: undefined }, ); - + // For users who are not admin or manager, we dont want to expose // the project id if (!isAdminOrManager) { - delete plainOpportunity.projectId; + delete formatted.projectId; } return formatted; }); From d81d76347b176c09cfcac3cbca19ac5727762110 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Tue, 2 Sep 2025 21:14:42 +0200 Subject: [PATCH 8/8] fix: removed debug log --- src/routes/copilotOpportunity/list.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/routes/copilotOpportunity/list.js b/src/routes/copilotOpportunity/list.js index fb4065fd..9206157c 100644 --- a/src/routes/copilotOpportunity/list.js +++ b/src/routes/copilotOpportunity/list.js @@ -66,8 +66,6 @@ module.exports = [ .then((copilotOpportunities) => { const formattedOpportunities = copilotOpportunities.map((opportunity) => { const plainOpportunity = opportunity.get({ plain: true }); - - req.log.debug(isAdminOrManager, 'admin or manager', plainOpportunity); const formatted = Object.assign({}, plainOpportunity, plainOpportunity.copilotRequest ? plainOpportunity.copilotRequest.data : {},