diff --git a/src/common/challenge-helper.js b/src/common/challenge-helper.js index a7372149..062c4dbb 100644 --- a/src/common/challenge-helper.js +++ b/src/common/challenge-helper.js @@ -99,6 +99,10 @@ class ChallengeHelper { // check groups authorization await helper.ensureAccessibleByGroupsAccess(currentUser, challenge); + + if (challenge.constraints) { + await this.validateChallengeConstraints(data.constraints); + } } async validateChallengeUpdateRequest(currentUser, challenge, data) { @@ -196,6 +200,32 @@ class ChallengeHelper { `Cannot set winners for challenge with non-completed ${challenge.status} status` ); } + + if (data.constraints) { + await this.validateChallengeConstraints(data.constraints); + } + } + + async validateChallengeConstraints(constraints) { + if (!_.isEmpty(constraints.allowedRegistrants)) { + await this.validateAllowedRegistrants(constraints.allowedRegistrants); + } + } + + async validateAllowedRegistrants(allowedRegistrants) { + const members = await helper.getMembersByHandles(allowedRegistrants); + const incorrectHandles = _.difference( + allowedRegistrants, + _.map(members, (m) => _.lowerCase(m.handle)) + ); + if (incorrectHandles.length > 0) { + throw new errors.BadRequestError( + `Cannot create challenge with invalid handle in constraints. [${_.join( + incorrectHandles, + "," + )}]` + ); + } } sanitizeRepeatedFieldsInUpdateRequest(data) { diff --git a/src/common/helper.js b/src/common/helper.js index 9a9612db..67a37435 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -1080,6 +1080,22 @@ async function getMemberByHandle(handle) { return res.data || {}; } +/** + * Get members by handles + * @param {Array} handles the user handle + * @returns {Object} + */ +async function getMembersByHandles(handles) { + const token = await m2mHelper.getM2MToken(); + const res = await axios.get( + `${config.MEMBERS_API_URL}/?fields=handle&handlesLower=["${_.join(handles, '","')}"]`, + { + headers: { Authorization: `Bearer ${token}` }, + } + ); + return res.data; +} + /** * Send self service notification * @param {String} type the notification type @@ -1199,6 +1215,7 @@ module.exports = { cancelPayment, sendSelfServiceNotification, getMemberByHandle, + getMembersByHandles, submitZendeskRequest, updateSelfServiceProjectInfo, getFromInternalCache, diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 15813516..336156d6 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1167,7 +1167,7 @@ createChallenge.schema = { legacyId: Joi.number().integer().positive(), constraints: Joi.object() .keys({ - allowedRegistrants: Joi.array().items(Joi.string()).optional(), + allowedRegistrants: Joi.array().items(Joi.string().trim().lowercase()).optional(), }) .optional(), startDate: Joi.date().iso(), @@ -1998,7 +1998,7 @@ updateChallenge.schema = { legacyId: Joi.number().integer().positive(), constraints: Joi.object() .keys({ - allowedRegistrants: Joi.array().items(Joi.string()).optional(), + allowedRegistrants: Joi.array().items(Joi.string().trim().lowercase()).optional(), }) .optional(), status: Joi.string().valid(_.values(constants.challengeStatuses)),