@@ -24,6 +24,7 @@ const validator = require('validator');
2424const crypto = require ( 'crypto' ) ;
2525const aescrypto = require ( path . join ( __dirname , 'aescrypto' ) ) ;
2626const https = require ( 'https' ) ;
27+ const qna = require ( path . join ( __dirname , 'qna' ) ) ;
2728
2829var modules = { } ;
2930var moduleVer = 0 ;
@@ -33,7 +34,7 @@ var solutions = [];
3334var descriptions = [ ] ;
3435var masterSalt = "" ;
3536
36- loadModules = ( ) => {
37+ let loadModules = ( ) => {
3738 let modsPath ;
3839 if ( ! util . isNullOrUndefined ( process . env . DATA_DIR ) ) {
3940 modsPath = path . join ( process . env . DATA_DIR , "modules.json" ) ;
@@ -58,11 +59,11 @@ loadModules = () => {
5859 }
5960
6061
61- function getModulePath ( moduleId ) {
62+ let getModulePath = ( moduleId ) => {
6263 return path . join ( 'static/lessons/' , moduleId ) ;
6364}
6465
65- function getDefinitionsForModule ( moduleId ) {
66+ let getDefinitionsForModule = ( moduleId ) => {
6667
6768 try {
6869 var defs = Object . freeze ( require ( path . join ( __dirname , getModulePath ( moduleId ) , '/definitions.json' ) ) ) ;
@@ -104,21 +105,30 @@ let init = async () => {
104105 masterSalt = process . env . CHALLENGE_MASTER_SALT ;
105106 }
106107
107- let dbModuleVersion = await db . getModuleVersion ( ) ;
108- if ( dbModuleVersion < moduleVer ) {
109- util . log ( "New training modules version, updating module completion for all users." )
110- recreateBadgesOnModulesUpdate ( ) ;
111- db . updateModuleVersion ( moduleVer ) ;
112- }
108+ try {
109+ let dbModuleVersion = await db . getModuleVersion ( ) ;
110+ if ( dbModuleVersion < moduleVer ) {
111+ util . log ( "New training modules version, updating module completion for all users." )
112+ recreateBadgesOnModulesUpdate ( ) ;
113+ db . updateModuleVersion ( moduleVer ) ;
114+ if ( dbModuleVersion < moduleVer ) {
115+ util . log ( "New training modules version, updating module completion for all users." )
116+ recreateBadgesOnModulesUpdate ( ) ;
117+ db . updateModuleVersion ( moduleVer ) ;
118+ }
119+ }
120+ } catch ( error ) {
121+ console . log ( `Error handling module version ${ error . message } ` ) ;
122+ }
123+
113124}
114125
115126init ( ) ;
116127
117- getModules = function ( ) { return modules ; }
118- getChallengeNames = function ( ) { return challengeNames ; }
119- getChallengeDefinitions = function ( ) { return challengeDefinitions ; }
128+ let getModules = function ( ) { return modules ; }
129+ let getChallengeNames = function ( ) { return challengeNames ; }
120130
121- isPermittedModule = async ( user , moduleId ) => {
131+ let isPermittedModule = async ( user , moduleId ) => {
122132 let badges = await db . fetchBadges ( user . id ) ;
123133 if ( util . isNullOrUndefined ( modules [ moduleId ] ) ) {
124134 return false ;
@@ -144,7 +154,7 @@ isPermittedModule = async (user, moduleId) => {
144154/**
145155 * Get the user level based on the amount of passed challenges
146156 */
147- getUserLevelForModule = async ( user , moduleId ) => {
157+ let getUserLevelForModule = async ( user , moduleId ) => {
148158 let moduleDefinitions = getDefinitionsForModule ( moduleId ) ;
149159 let passedChallenges = await db . fetchChallengeEntriesForUser ( user ) ;
150160 let userLevel = - 1 ;
@@ -170,7 +180,7 @@ getUserLevelForModule = async (user,moduleId) => {
170180/**
171181 * Get permitted challenges for module
172182 */
173- getPermittedChallengesForUser = async ( user , moduleId ) => {
183+ let getPermittedChallengesForUser = async ( user , moduleId ) => {
174184 if ( util . isNullOrUndefined ( moduleId ) ) return [ ] ;
175185 if ( util . isNullOrUndefined ( modules [ moduleId ] ) ) return [ ] ;
176186
@@ -189,10 +199,9 @@ getPermittedChallengesForUser = async (user, moduleId) => {
189199
190200/**
191201 * Construct the challenge definitions loaded on the client side based on the users level
192- * @param {Object } user The session user object
193202 * @param {Array } moduleIds The lesson module ids
194203 */
195- getChallengeDefinitionsForUser = async ( user , moduleId ) => {
204+ let getChallengeDefinitions = async ( moduleId ) => {
196205 var returnChallenges = [ ] ;
197206
198207 if ( util . isNullOrUndefined ( moduleId ) ) return [ ] ;
@@ -209,10 +218,13 @@ getChallengeDefinitionsForUser = async (user, moduleId) => {
209218 if ( ! util . isNullOrUndefined ( playLink ) ) {
210219 challenge . playLink = playLink ;
211220 }
212- var description = challenge . description ;
213- if ( ! util . isNullOrUndefined ( description ) && description . indexOf ( modulePath ) === - 1 ) {
214- challenge . description = path . join ( modulePath , description ) ;
215- }
221+ }
222+ var description = challenge . description ;
223+ if ( ! util . isNullOrUndefined ( description ) && description . indexOf ( modulePath ) === - 1 ) {
224+ challenge . description = path . join ( modulePath , description ) ;
225+ }
226+ if ( challenge . type === "quiz" ) {
227+ challenge . question = qna . getCode ( challenge . id ) ;
216228 }
217229 }
218230 returnChallenges . push ( level ) ;
@@ -227,7 +239,7 @@ getChallengeDefinitionsForUser = async (user, moduleId) => {
227239 * Returns the solution html (converted from markdown)
228240 * @param {The challenge id } challengeId
229241 */
230- getSolution = function ( challengeId ) {
242+ let getSolution = function ( challengeId ) {
231243 var solution = solutions [ challengeId ] ;
232244 var solutionHtml = "" ;
233245 if ( ! util . isNullOrUndefined ( solution ) ) {
@@ -242,7 +254,7 @@ getSolution = function (challengeId) {
242254 * Returns the description html (converted from markdown if applicable)
243255 * @param {The challenge id } challengeId
244256 */
245- getDescription = function ( challengeId ) {
257+ let getDescription = function ( challengeId ) {
246258 var description = descriptions [ challengeId ] ;
247259 var descriptionHtml = "" ;
248260 if ( util . isNullOrUndefined ( description ) ) return "" ;
@@ -265,7 +277,7 @@ getDescription = function (challengeId) {
265277/**
266278 * Checks if the user has completed the module and issue a badge
267279 */
268- verifyModuleCompletion = async ( user , moduleId ) => {
280+ let verifyModuleCompletion = async ( user , moduleId ) => {
269281 var userLevel = await getUserLevelForModule ( user , moduleId ) ;
270282 let moduleDefinitions = getDefinitionsForModule ( moduleId ) ;
271283 var lastLevel = moduleDefinitions [ moduleDefinitions . length - 1 ] ;
@@ -293,7 +305,7 @@ verifyModuleCompletion = async (user, moduleId) => {
293305/**
294306 * Iterates through the entire list of users to insert badges where needed
295307 */
296- recreateBadgesOnModulesUpdate = async ( ) => {
308+ let recreateBadgesOnModulesUpdate = async ( ) => {
297309
298310 let users = await db . fetchUsersWithId ( ) ;
299311
@@ -320,7 +332,7 @@ recreateBadgesOnModulesUpdate = async () => {
320332 * Retrieves a code to verify completion of the level
321333 * @param {Badge } badge
322334 */
323- getBadgeCode = ( badge , user ) => {
335+ let getBadgeCode = ( badge , user ) => {
324336 let module = modules [ badge . moduleId ] ;
325337
326338 if ( util . isNullOrUndefined ( module ) || util . isNullOrUndefined ( module . badgeInfo ) ) return null ;
@@ -347,7 +359,7 @@ getBadgeCode = (badge, user) => {
347359 * Verifies a badge code and returns parsed info
348360 * @param {Base64 } badgeCode
349361 */
350- verifyBadgeCode = ( badgeCode ) => {
362+ let verifyBadgeCode = ( badgeCode ) => {
351363 urlDecoded = decodeURIComponent ( badgeCode ) ;
352364 let parts = urlDecoded . split ( "." ) ;
353365 if ( parts . length !== 2 ) return null ;
@@ -370,7 +382,7 @@ verifyBadgeCode = (badgeCode) => {
370382 * @param {* } badgrInfo
371383 * @param {* } user
372384 */
373- badgrCall = function ( badgrInfo , user ) {
385+ let badgrCall = function ( badgrInfo , user ) {
374386 if ( ! util . isNullOrUndefined ( badgrInfo ) && ! util . isNullOrUndefined ( config . encBadgrToken ) ) {
375387 if ( user . email === null ) {
376388 util . log ( "Cannot issue badge for this user. E-mail is null." , user ) ;
@@ -418,26 +430,42 @@ badgrCall = function(badgrInfo, user){
418430/**
419431 * Logic to for the api challenge code
420432 */
421- apiChallengeCode = async ( req ) => {
433+ let apiChallengeCode = async ( req ) => {
422434 if ( util . isNullOrUndefined ( req . body . challengeId ) ||
423435 util . isNullOrUndefined ( req . body . challengeCode ) ||
424436 util . isNullOrUndefined ( req . body . moduleId ) ) {
425437 throw Error ( "invalidRequest" ) ;
426438 }
427439
440+
428441 var moduleId = req . body . moduleId . trim ( ) ;
429442 var challengeId = req . body . challengeId . trim ( ) ;
430443 var challengeCode = req . body . challengeCode . trim ( ) ;
431444
432- if ( util . isNullOrUndefined ( challengeCode ) || validator . isBase64 ( challengeCode ) == false ) {
445+ let challengeType = "page" ;
446+ if ( ! util . isNullOrUndefined ( req . body . challengeType ) ) {
447+ challengeType = req . body . challengeType ;
448+ }
449+
450+ if ( [ "page" , "quiz" ] . indexOf ( challengeType ) === - 1 ) {
451+ throw Error ( "invalidChallengeType" ) ;
452+ }
453+
454+ let answer = null ;
455+ if ( ! util . isNullOrUndefined ( req . body . answer ) ) {
456+ answer = req . body . answer . trim ( ) ;
457+ }
458+
459+ if ( util . isNullOrUndefined ( challengeCode ) ||
460+ ( validator . isAlphanumeric ( challengeCode ) === false && validator . isBase64 ( challengeCode ) === false ) ) {
433461 throw Error ( "invalidCode" ) ;
434462 }
435463
436- if ( util . isNullOrUndefined ( moduleId ) || validator . isAlphanumeric ( moduleId ) == false ) {
464+ if ( util . isNullOrUndefined ( moduleId ) || validator . isAlphanumeric ( moduleId ) === false ) {
437465 throw Error ( "invalidModuleId" ) ;
438466 }
439467
440- if ( util . isNullOrUndefined ( challengeId ) || util . isAlphanumericOrUnderscore ( challengeId ) == false ) {
468+ if ( util . isNullOrUndefined ( challengeId ) || util . isAlphanumericOrUnderscore ( challengeId ) === false ) {
441469 throw Error ( "invalidChallengeId" ) ;
442470 }
443471
@@ -466,13 +494,24 @@ apiChallengeCode = async (req) => {
466494 if ( util . isNullOrUndefined ( modules [ moduleId ] . skipMasterSalt ) || modules [ moduleId ] . skipMasterSalt === false ) {
467495 ms = masterSalt ;
468496 }
497+
498+ if ( challengeType !== "quiz" ) {
499+ answer = challengeId + req . user . codeSalt ;
500+ }
501+
469502 //either hex or base64 formats should work
470- //we're looking at the first 10 characters only for situations where the challenge code may get truncated - pcaps, IPS logs
471- var verificationHashB64 = crypto . createHash ( 'sha256' ) . update ( challengeId + req . user . codeSalt + ms ) . digest ( 'base64' ) . substr ( 0 , 10 ) ;
472- var verificationHashHex = crypto . createHash ( 'sha256' ) . update ( challengeId + req . user . codeSalt + ms ) . digest ( 'hex' ) . substr ( 0 , 10 ) ;
503+ let verificationHashB64 = crypto . createHash ( 'sha256' ) . update ( answer + ms ) . digest ( 'base64' ) ;
504+ let verificationHashHex = crypto . createHash ( 'sha256' ) . update ( answer + ms ) . digest ( 'hex' ) ;
505+
473506 if ( challengeCode . indexOf ( verificationHashB64 ) !== 0 && challengeCode . indexOf ( verificationHashHex ) !== 0 ) {
474- throw Error ( "invalidCode" ) ;
507+ if ( challengeType === "quiz" ) {
508+ throw Error ( "invalidAnswer" ) ;
509+ }
510+ else {
511+ throw Error ( "invalidCode" ) ;
512+ }
475513 }
514+
476515 //success update challenge
477516 curChallengeObj . moduleId = moduleId ;
478517 return insertChallengeEntry ( req . user , curChallengeObj , moduleId ) ;
@@ -482,7 +521,7 @@ apiChallengeCode = async (req) => {
482521/**
483522 * Inserts a challenge entry
484523 */
485- insertChallengeEntry = async ( user , curChallengeObj , moduleId ) => {
524+ let insertChallengeEntry = async ( user , curChallengeObj , moduleId ) => {
486525 await db . getPromise ( db . insertChallengeEntry , [ user . id , curChallengeObj . id ] ) ;
487526 //issue badgr badge if enabled
488527 badgrCall ( curChallengeObj . badgrInfo , user ) ;
@@ -509,14 +548,12 @@ insertChallengeEntry = async (user,curChallengeObj, moduleId) => {
509548
510549
511550
512-
513551module . exports = {
514552 apiChallengeCode,
515553 badgrCall,
516554 getBadgeCode,
517555 getChallengeNames,
518556 getChallengeDefinitions,
519- getChallengeDefinitionsForUser,
520557 getDescription,
521558 getModules,
522559 getPermittedChallengesForUser,
0 commit comments