Skip to content

Commit 665e532

Browse files
liuliquanrakibansary
liuliquan
andauthored
PLAT-3453 Stop copilots from paying themselves (#666)
* PLAT-3453 Stop copilots from paying themselves 1. Stop copilots from paying themselves, thus copilots will need to contact manager to launch/complete the task 2. When updateChallenge, the helper.getChallengeResources function is called multiple times, now it's called only once * Only restrict launch/complete self-assigned Task Also validate winners is present in request payload when complete a Task * Task status change (#665) --------- Co-authored-by: Rakib Ansary <[email protected]>
1 parent 7a10e43 commit 665e532

File tree

3 files changed

+66
-12
lines changed

3 files changed

+66
-12
lines changed

src/common/challenge-helper.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ class ChallengeHelper {
105105
}
106106
}
107107

108-
async validateChallengeUpdateRequest(currentUser, challenge, data) {
108+
async validateChallengeUpdateRequest(currentUser, challenge, data, challengeResources) {
109109
if (process.env.LOCAL != "true") {
110-
await helper.ensureUserCanModifyChallenge(currentUser, challenge);
110+
await helper.ensureUserCanModifyChallenge(currentUser, challenge, challengeResources);
111111
}
112112

113113
helper.ensureNoDuplicateOrNullElements(data.tags, "tags");

src/common/helper.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -528,14 +528,17 @@ async function getResourceRoles() {
528528
* Check if a user has full access on a challenge
529529
* @param {String} challengeId the challenge UUID
530530
* @param {String} userId the user ID
531+
* @param {Array} challengeResources the challenge resources
531532
*/
532-
async function userHasFullAccess(challengeId, userId) {
533+
async function userHasFullAccess(challengeId, userId, challengeResources) {
533534
const resourceRoles = await getResourceRoles();
534535
const rolesWithFullAccess = _.map(
535536
_.filter(resourceRoles, (r) => r.fullWriteAccess),
536537
"id"
537538
);
538-
const challengeResources = await getChallengeResources(challengeId);
539+
if (!challengeResources) {
540+
challengeResources = await getChallengeResources(challengeId);
541+
}
539542
return (
540543
_.filter(
541544
challengeResources,
@@ -982,13 +985,14 @@ async function ensureUserCanViewChallenge(currentUser, challenge) {
982985
*
983986
* @param {Object} currentUser the user who perform operation
984987
* @param {Object} challenge the challenge to check
988+
* @param {Array} challengeResources the challenge resources
985989
* @returns {Promise}
986990
*/
987-
async function ensureUserCanModifyChallenge(currentUser, challenge) {
991+
async function ensureUserCanModifyChallenge(currentUser, challenge, challengeResources) {
988992
// check groups authorization
989993
await ensureAccessibleByGroupsAccess(currentUser, challenge);
990994
// check full access
991-
const isUserHasFullAccess = await userHasFullAccess(challenge.id, currentUser.userId);
995+
const isUserHasFullAccess = await userHasFullAccess(challenge.id, currentUser.userId, challengeResources);
992996
if (
993997
!currentUser.isMachine &&
994998
!hasAdminRole(currentUser) &&

src/services/ChallengeService.js

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,10 +1364,9 @@ function isDifferentPrizeSets(prizeSets = [], otherPrizeSets = []) {
13641364
/**
13651365
* Validate the winners array.
13661366
* @param {Array} winners the Winner Array
1367-
* @param {String} winchallengeIdners the challenge ID
1367+
* @param {Array} challengeResources the challenge resources
13681368
*/
1369-
async function validateWinners(winners, challengeId) {
1370-
const challengeResources = await helper.getChallengeResources(challengeId);
1369+
async function validateWinners(winners, challengeResources) {
13711370
const registrants = _.filter(challengeResources, (r) => r.roleId === config.SUBMITTER_ROLE_ID);
13721371
for (const prizeType of _.values(constants.prizeSetTypes)) {
13731372
const filteredWinners = _.filter(winners, (w) => w.type === prizeType);
@@ -1410,6 +1409,55 @@ async function validateWinners(winners, challengeId) {
14101409
}
14111410
}
14121411

1412+
/**
1413+
* Task shouldn't be launched/completed when it is assigned to the current user self.
1414+
* E.g: stop copilots from paying themselves, thus copilots will need to contact manager to launch/complete the task.
1415+
* @param {Object} currentUser the user who perform operation
1416+
* @param {Object} challenge the existing challenge
1417+
* @param {Object} data the new input challenge data
1418+
* @param {Array} challengeResources the challenge resources
1419+
*/
1420+
function validateTask(currentUser, challenge, data, challengeResources) {
1421+
if (!_.get(challenge, "legacy.pureV5Task")) {
1422+
// Not a Task
1423+
return;
1424+
}
1425+
1426+
// Status changed to Active, indicating launch a Task
1427+
const isLaunchTask =
1428+
data.status === constants.challengeStatuses.Active &&
1429+
challenge.status !== constants.challengeStatuses.Active;
1430+
1431+
// Status changed to Completed, indicating complete a Task
1432+
const isCompleteTask =
1433+
data.status === constants.challengeStatuses.Completed &&
1434+
challenge.status !== constants.challengeStatuses.Completed;
1435+
1436+
// When complete a Task, input data should have winners
1437+
if (isCompleteTask && (!data.winners || !data.winners.length)) {
1438+
throw new errors.BadRequestError("The winners is required to complete a Task");
1439+
}
1440+
1441+
if (!currentUser.isMachine && (isLaunchTask || isCompleteTask)) {
1442+
// Whether task is assigned to current user
1443+
const assignedToCurrentUser =
1444+
_.filter(
1445+
challengeResources,
1446+
(r) =>
1447+
r.roleId === config.SUBMITTER_ROLE_ID &&
1448+
_.toString(r.memberId) === _.toString(currentUser.userId)
1449+
).length > 0;
1450+
1451+
if (assignedToCurrentUser) {
1452+
throw new errors.ForbiddenError(
1453+
`You are not allowed to ${
1454+
data.status === constants.challengeStatuses.Active ? "lanuch" : "complete"
1455+
} task assigned to yourself. Please contact manager to operate.`
1456+
);
1457+
}
1458+
}
1459+
}
1460+
14131461
/**
14141462
* Update challenge.
14151463
* @param {Object} currentUser the user who perform operation
@@ -1441,7 +1489,10 @@ async function updateChallenge(currentUser, challengeId, data) {
14411489
data = sanitizeData(sanitizeChallenge(data), challenge);
14421490
logger.debug(`Sanitized Data: ${JSON.stringify(data)}`);
14431491

1444-
await validateChallengeUpdateRequest(currentUser, challenge, data);
1492+
const challengeResources = await helper.getChallengeResources(challengeId);
1493+
1494+
await validateChallengeUpdateRequest(currentUser, challenge, data, challengeResources);
1495+
validateTask(currentUser, challenge, data, challengeResources);
14451496

14461497
let sendActivationEmail = false;
14471498
let sendSubmittedEmail = false;
@@ -1716,7 +1767,7 @@ async function updateChallenge(currentUser, challengeId, data) {
17161767
}
17171768

17181769
if (data.winners && data.winners.length && data.winners.length > 0) {
1719-
await validateWinners(data.winners, challengeId);
1770+
await validateWinners(data.winners, challengeResources);
17201771
}
17211772

17221773
// Only m2m tokens are allowed to modify the `task.*` information on a challenge
@@ -1777,7 +1828,6 @@ async function updateChallenge(currentUser, challengeId, data) {
17771828

17781829
if (_.get(type, "isTask")) {
17791830
if (!_.isEmpty(_.get(data, "task.memberId"))) {
1780-
const challengeResources = await helper.getChallengeResources(challengeId);
17811831
const registrants = _.filter(
17821832
challengeResources,
17831833
(r) => r.roleId === config.SUBMITTER_ROLE_ID

0 commit comments

Comments
 (0)