Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit 5752555

Browse files
committed
Improve challenge visibility control: review and search
1 parent faaeaa8 commit 5752555

19 files changed

+1703
-189
lines changed

actions/challenges.js

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@
8080
* Changes in 1.31:
8181
* - Remove screeningScorecardId and reviewScorecardId from search challenges api.
8282
* Changes in 1.32:
83-
* - validateChallenge, getRegistrants, getChallenge, getSubmissions and getPhases functions now check
84-
* if an user belongs to a group via user_group_xref for old challenges and by calling V3 API for new ones.
83+
* - validateChallenge, getRegistrants, getChallenge, getSubmissions, getPhases, searchChallenges and getChallenges
84+
* functions now check if an user belongs to a group via user_group_xref for old challenges and by calling V3 API
85+
* for new ones.
8586
*/
8687
"use strict";
8788
/*jslint stupid: true, unparam: true, continue: true, nomen: true */
@@ -155,15 +156,22 @@ var CHALLENGE_TYPE_FILTER = ' AND p.project_category_id IN (@filter@)';
155156
* This filter will return all private challenges.
156157
* @since 1.24
157158
*/
158-
var ALL_PRIVATE_CHALLENGE_FILTER = ' EXISTS (SELECT contest_id FROM contest_eligibility WHERE contest_id = p.project_id)\n';
159+
var ALL_PRIVATE_CHALLENGE_FILTER = '(@amIAdmin@ = 0 AND EXISTS (SELECT contest_id FROM contest_eligibility WHERE contest_id = p.project_id))\n';
159160

160161
/**
161162
* This filter will return private challenges that given user_id can access.
162163
* @since 1.24
163164
*/
164-
var USER_PRIVATE_CHALLENGE_FILTER = ' EXISTS (SELECT contest_id FROM contest_eligibility ce, ' +
165-
'group_contest_eligibility gce, user_group_xref x WHERE x.login_id = @user_id@ AND x.group_id = gce.group_id ' +
166-
'AND gce.contest_eligibility_id = ce.contest_eligibility_id AND ce.contest_id = p.project_id)\n';
165+
var USER_PRIVATE_CHALLENGE_FILTER = ' EXISTS (SELECT contest_id ' +
166+
'FROM contest_eligibility ce ' +
167+
'INNER JOIN group_contest_eligibility gce ON gce.contest_eligibility_id = ce.contest_eligibility_id ' +
168+
'INNER JOIN security_groups sg ON gce.group_id = sg.group_id ' +
169+
'LEFT JOIN user_group_xref x ON x.group_id = gce.group_id ' +
170+
'WHERE ce.contest_id = p.project_id AND (' +
171+
'(sg.challenge_group_ind = 0 AND x.login_id = @userId@)' +
172+
'OR (sg.challenge_group_ind <> 0 AND gce.group_id IN (@myGroups@))' +
173+
')' +
174+
')\n';
167175

168176
/**
169177
* This filter return all private challenges that in specific group.
@@ -908,7 +916,8 @@ var searchChallenges = function (api, connection, dbConnectionMap, community, ne
908916
total,
909917
challengeType,
910918
queryName,
911-
challenges;
919+
challenges,
920+
myGroups;
912921
for (prop in query) {
913922
if (query.hasOwnProperty(prop)) {
914923
query[prop.toLowerCase()] = query[prop];
@@ -963,7 +972,17 @@ var searchChallenges = function (api, connection, dbConnectionMap, community, ne
963972
sqlParams.user_id = caller.userId || 0;
964973

965974
// Check the private challenge access
966-
api.dataAccess.executeQuery('check_eligibility', sqlParams, dbConnectionMap, cb);
975+
api.v3client.getMyGroups(connection, cb);
976+
}, function (groups, cb) {
977+
myGroups = groups;
978+
sqlParams.amIAdmin = connection.caller.accessLevel === 'admin' ? 1 : 0;
979+
sqlParams.myGroups = myGroups;
980+
// Next function uses the passed parameter only to check if it's not empty
981+
if(sqlParams.amIAdmin || !_.isDefined(query.communityId) || _.indexOf(myGroups, query.communityId) !== -1) {
982+
cb(null, [1]);
983+
} else {
984+
api.dataAccess.executeQuery('check_eligibility', sqlParams, dbConnectionMap, cb);
985+
}
967986
}, function (results, cb) {
968987
if (results.length === 0) {
969988
// Return error if the user is not allowed to a specific group(communityId is set)
@@ -2310,7 +2329,8 @@ var getChallengeResults = function (api, connection, dbConnectionMap, isStudio,
23102329
cb(error);
23112330
return;
23122331
}
2313-
2332+
api.challengeHelper.checkUserChallengeEligibility(connection, challengeId, cb);
2333+
}, function (cb) {
23142334
sqlParams.challengeId = challengeId;
23152335
api.dataAccess.executeQuery("get_challenge_results_validations", sqlParams, dbConnectionMap, cb);
23162336
}, function (rows, cb) {
@@ -2319,11 +2339,6 @@ var getChallengeResults = function (api, connection, dbConnectionMap, isStudio,
23192339
return;
23202340
}
23212341

2322-
if (rows[0].is_private_challenge) {
2323-
cb(new NotFoundError('This is a private challenge. You cannot view it.'));
2324-
return;
2325-
}
2326-
23272342
if (!rows[0].is_challenge_finished) {
23282343
cb(new BadRequestError('You cannot view the results because the challenge is not yet finished or was cancelled.'));
23292344
return;
@@ -3677,7 +3692,8 @@ var getChallenges = function (api, connection, listType, isMyChallenges, next) {
36773692
result = {},
36783693
total,
36793694
challenges,
3680-
index;
3695+
index,
3696+
myGroups;
36813697
for (prop in query) {
36823698
if (query.hasOwnProperty(prop)) {
36833699
query[prop.toLowerCase()] = query[prop];
@@ -3723,11 +3739,15 @@ var getChallenges = function (api, connection, listType, isMyChallenges, next) {
37233739
},
37243740
function (cb) {
37253741
validateInputParameterV2(helper, caller, type, query, filter, pageIndex, pageSize, sortColumn, sortOrder, listType, dbConnectionMap, cb);
3726-
3742+
}, function (cb) {
3743+
api.v3client.getMyGroups(connection, cb);
3744+
}, function (groups, cb) {
3745+
myGroups = groups;
3746+
37273747
if (filter.technologies) {
37283748
filter.tech = filter.technologies.split(',')[0];
37293749
}
3730-
}, function (cb) {
3750+
37313751
if (pageIndex === -1) {
37323752
pageIndex = 1;
37333753
pageSize = helper.MAX_PAGE_SIZE;
@@ -3744,7 +3764,9 @@ var getChallenges = function (api, connection, listType, isMyChallenges, next) {
37443764
registration_phase_status: helper.LIST_TYPE_REGISTRATION_STATUS_MAP[listType],
37453765
project_status_id: helper.LIST_TYPE_PROJECT_STATUS_MAP[listType],
37463766
user_id: caller.userId || 0,
3747-
userId: caller.userId || 0
3767+
userId: caller.userId || 0,
3768+
amIAdmin: caller.accessLevel === 'admin' ? 1 : 0,
3769+
myGroups: myGroups
37483770
});
37493771

37503772
if (isMyChallenges) {
@@ -3757,7 +3779,11 @@ var getChallenges = function (api, connection, listType, isMyChallenges, next) {
37573779
}
37583780

37593781
// Check the private challenge access
3760-
api.dataAccess.executeQuery('check_eligibility', sqlParams, dbConnectionMap, cb);
3782+
if(sqlParams.amIAdmin || !_.isDefined(query.communityId) || _.indexOf(myGroups, query.communityId) !== -1) {
3783+
cb(null, [1]);
3784+
} else {
3785+
api.dataAccess.executeQuery('check_eligibility', sqlParams, dbConnectionMap, cb);
3786+
}
37613787
}, function (results, cb) {
37623788
if (results.length === 0) {
37633789
// Return error if the user is not allowed to a specific group(communityId is set)

actions/reviewOpportunities.js

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
2-
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
2+
* Copyright (C) 2013 - 2017 TopCoder Inc., All Rights Reserved.
33
*
4-
* @version 1.6
5-
* @author Sky_, Ghost_141, flytoj2ee
4+
* @version 1.7
5+
* @author Sky_, Ghost_141, flytoj2ee, GFalcon
66
* changes in 1.1
77
* - Implement the studio review opportunities.
88
* changes in 1.2
@@ -17,6 +17,10 @@
1717
* - Implement the applyStudioReviewOpportunity api.
1818
* Changes in 1.6:
1919
* - Fix bug in review opportunities api.
20+
* Changes in 1.7:
21+
* - all functions check the right of the current user to view
22+
* or apply to a challenge via user_group_xref for old challenges
23+
* and by calling V3 API for new ones.
2024
*/
2125
'use strict';
2226
/*jslint node: true, stupid: true, unparam: true, plusplus: true */
@@ -212,6 +216,7 @@ var getStudioReviewOpportunity = function (api, connection, dbConnectionMap, nex
212216
cb(error);
213217
return;
214218
}
219+
215220
sqlParams.challengeId = challengeId;
216221
api.dataAccess.executeQuery('get_studio_review_opportunity_phases', sqlParams, dbConnectionMap, cb);
217222
}, function (rows, cb) {
@@ -229,6 +234,9 @@ var getStudioReviewOpportunity = function (api, connection, dbConnectionMap, nex
229234
duration: getDuration(row.start_time, row.end_time) + ' hours'
230235
});
231236
});
237+
// Check if the user has the right to view the challenge
238+
api.challengeHelper.checkUserChallengeEligibility(connection, challengeId, cb);
239+
}, function (cb) {
232240
api.dataAccess.executeQuery('get_studio_review_opportunity_positions', sqlParams, dbConnectionMap, cb);
233241
}, function (rows, cb) {
234242
if (rows.length !== 0) {
@@ -467,6 +475,7 @@ var calculatePayment = function (reviewOpportunityInfo, adjustPayments) {
467475
*/
468476
var getPaymentValues = function (reviewOpportunityInfo, adjustPayments, isAsc) {
469477
var payment = calculatePayment(reviewOpportunityInfo, adjustPayments);
478+
470479
return _.sortBy(payment, function (item) { return (isAsc ? -item : item); });
471480
};
472481

@@ -479,7 +488,7 @@ var getPaymentValues = function (reviewOpportunityInfo, adjustPayments, isAsc) {
479488
*/
480489
var getReviewOpportunities = function (api, connection, isStudio, next) {
481490
var helper = api.helper, result = {}, sqlParams, dbConnectionMap = connection.dbConnectionMap, pageIndex,
482-
pageSize, sortOrder, sortColumn, filter = {}, reviewOpportunities, queryName;
491+
pageSize, sortOrder, sortColumn, filter = {}, reviewOpportunities, queryName, myGroups;
483492

484493
pageSize = Number(connection.params.pageSize || helper.MAX_PAGE_SIZE);
485494
pageIndex = Number(connection.params.pageIndex || 1);
@@ -491,6 +500,11 @@ var getReviewOpportunities = function (api, connection, isStudio, next) {
491500
validateInputParameter(helper, connection, filter, isStudio, cb);
492501
},
493502
function (cb) {
503+
api.v3client.getMyGroups(connection, cb)
504+
},
505+
function (groups, cb) {
506+
507+
myGroups = groups;
494508

495509
if (pageIndex === -1) {
496510
pageIndex = 1;
@@ -512,7 +526,10 @@ var getReviewOpportunities = function (api, connection, isStudio, next) {
512526
reviewStartDateSecondDate: filter.reviewStartDate.secondDate,
513527
reviewEndDateFirstDate: filter.reviewEndDate.firstDate,
514528
reviewEndDateSecondDate: filter.reviewEndDate.secondDate,
515-
challenge_id: 0
529+
challenge_id: 0,
530+
amIAdmin: connection.caller.accessLevel === 'admin' ? 1 : 0,
531+
myId: (connection.caller.userId || 0),
532+
myGroups: myGroups
516533
};
517534

518535
queryName = isStudio ? 'search_studio_review_opportunities' : 'search_software_review_opportunities_data';
@@ -653,7 +670,9 @@ var getSoftwareReviewOpportunity = function (api, connection, next) {
653670
};
654671

655672
async.parallel({
656-
privateCheck: execQuery('check_user_challenge_accessibility'),
673+
privateCheck: function (cbx) {
674+
api.challengeHelper.checkUserChallengeEligibility(connection, challengeId, cbx);
675+
},
657676
reviewCheck: execQuery('check_challenge_review_opportunity')
658677
}, cb);
659678
},
@@ -664,11 +683,6 @@ var getSoftwareReviewOpportunity = function (api, connection, next) {
664683
return;
665684
}
666685

667-
if (result.privateCheck[0].is_private && !result.privateCheck[0].has_access) {
668-
cb(new ForbiddenError('The user is not allowed to visit this challenge review opportunity detail.'));
669-
return;
670-
}
671-
672686
async.parallel({
673687
basic: execQuery('get_review_opportunity_detail_basic'),
674688
phases: execQuery('get_review_opportunity_detail_phases'),
@@ -864,8 +878,8 @@ var applyDevelopReviewOpportunity = function (api, connection, next) {
864878
api.dataAccess.executeQuery('check_reviewer', { challenge_id: challengeId, user_id: caller.userId }, dbConnectionMap, cbx);
865879
},
866880
privateCheck: function (cbx) {
867-
api.dataAccess.executeQuery('check_user_challenge_accessibility', { challengeId: challengeId, user_id: caller.userId }, dbConnectionMap, cbx);
868-
}
881+
api.challengeHelper.checkUserChallengeEligibility(connection, challengeId, cbx);
882+
},
869883
}, cb);
870884
},
871885
function (res, cb) {
@@ -875,7 +889,6 @@ var applyDevelopReviewOpportunity = function (api, connection, next) {
875889
.map(function (item) { return item.review_application_role_id; })
876890
.uniq()
877891
.value(),
878-
privateCheck = res.privateCheck[0],
879892
positionsLeft,
880893
assignedResourceRoles = res.resourceRoles,
881894
currentUserResourceRole = _.filter(assignedResourceRoles, function (item) { return item.user_id === caller.userId; });
@@ -908,13 +921,7 @@ var applyDevelopReviewOpportunity = function (api, connection, next) {
908921
return;
909922
}
910923

911-
// The caller can't access this private challenge.
912-
if (privateCheck.is_private && !privateCheck.has_access) {
913-
cb(new ForbiddenError('The user is not allowed to register this challenge review.'));
914-
return;
915-
}
916-
917-
// We only check this when caller is trying to apply review opportunity not cancel them.
924+
// We only check this when caller is trying to apply review opportunity not cancel them.
918925
// Get the review resource role that belong to the caller. If the length > 0 then the user is a reviewer already.
919926
if (reviewApplicationRoleId.length > 0 && currentUserResourceRole.length > 0) {
920927
cb(new BadRequestError('You are already assigned as reviewer for the contest.'));

db_scripts/test_eligibility.delete.sql

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,43 @@ DATABASE informixoltp;
99

1010
-- UPDATE coder SET comp_country_code = NULL WHERE user_id = 132458;
1111

12+
DATABASE tcs_dw;
13+
14+
DELETE FROM project WHERE project_id > 4440000 AND project_id < 4440100;
15+
1216
DATABASE tcs_catalog;
1317

18+
DELETE FROM project_info WHERE project_id > 5550000 AND project_id < 5550100;
19+
DELETE FROM project_phase WHERE project_id > 5550000 AND project_id < 5550100;
20+
DELETE FROM project WHERE project_id > 5550000 AND project_id < 5550100;
21+
22+
DELETE FROM project_info WHERE project_id > 4440000 AND project_id < 4440100;
23+
DELETE FROM project_phase WHERE project_id > 4440000 AND project_id < 4440100;
24+
DELETE FROM prize WHERE project_id > 4440000 AND project_id < 4440100;
25+
DELETE FROM project WHERE project_id > 4440000 AND project_id < 4440100;
26+
27+
DELETE FROM resource_info WHERE resource_id IN (SELECT resource_id FROM resource WHERE project_id > 3330000 AND project_id < 3330100);
28+
DELETE FROM resource WHERE project_id > 3330000 AND project_id < 3330100;
29+
DELETE FROM project_info WHERE project_id > 3330000 AND project_id < 3330100;
30+
DELETE FROM project_phase WHERE project_id > 3330000 AND project_id < 3330100;
31+
DELETE FROM project WHERE project_id > 3330000 AND project_id < 3330100;
32+
DELETE FROM project_studio_specification WHERE project_studio_spec_id = 3330333;
33+
34+
DELETE FROM prize WHERE project_id > 1110000 AND project_id < 1110100;
35+
DELETE FROM project_payment_adjustment WHERE project_id > 1110000 AND project_id < 1110100;
1436
DELETE FROM notification WHERE project_id > 1110000 AND project_id < 1110100;
1537
DELETE FROM project_result WHERE project_id > 1110000 AND project_id < 1110100;
1638
DELETE FROM project_user_audit WHERE project_id > 1110000 AND project_id < 1110100;
1739
DELETE FROM component_inquiry WHERE project_id > 1110000 AND project_id < 1110100;
1840
DELETE FROM resource_info WHERE resource_id IN (SELECT resource_id FROM resource WHERE project_id > 1110000 AND project_id < 1110100);
1941
DELETE FROM resource WHERE project_id > 1110000 AND project_id < 1110100;
2042

43+
DELETE FROM review_application WHERE review_auction_id IN (SELECT review_auction_id FROM review_auction WHERE project_id > 1110000 AND project_id < 1110100);
44+
DELETE FROM review_auction WHERE project_id > 1110000 AND project_id < 1110100;
2145
DELETE FROM project_info WHERE project_id > 1110000 AND project_id < 1110100;
2246
DELETE FROM comp_versions WHERE component_id = 3330333;
2347
DELETE FROM comp_catalog WHERE component_id = 3330333;
48+
DELETE FROM phase_criteria WHERE project_phase_id > 1110000 AND project_phase_id < 1110100;
2449
DELETE FROM project_phase WHERE project_id > 1110000 AND project_id < 1110100;
2550
DELETE FROM project WHERE project_id > 1110000 AND project_id < 1110100;
2651

0 commit comments

Comments
 (0)