Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 90 additions & 29 deletions trainingportal/challenges.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ const aescrypto = require(path.join(__dirname, 'aescrypto'));
const https = require('https');

var modules = {};
var moduleVer = 0;
var challengeDefinitions = [];
var challengeNames = [];
var solutions = [];
var descriptions = [];
var masterSalt = "";

loadModules = function(){
loadModules = () => {
let modsPath;
if(!util.isNullOrUndefined(process.env.DATA_DIR)){
modsPath = path.join(process.env.DATA_DIR, "modules.json");
Expand All @@ -44,13 +45,18 @@ loadModules = function(){
let localModules = {};
let moduleIds = Object.keys(moduleDefs);
for(let moduleId of moduleIds){
if(moduleId === "version"){
moduleVer = moduleDefs[moduleId];
continue;
}
let disabled = config.disabledModules;
if(util.isNullOrUndefined(disabled) || !disabled.includes(moduleId)){
localModules[moduleId] = moduleDefs[moduleId];
}
}
return localModules;
}
return Object.freeze(localModules);
}


function getModulePath(moduleId){
return path.join('static/lessons/', moduleId);
Expand All @@ -71,8 +77,8 @@ function getDefinitionsForModule(moduleId){
/**
* Initializes challenges when this module is loaded
*/
function init(){
modules = Object.freeze(loadModules());
let init = async () => {
modules = loadModules();
for(let moduleId in modules){
let moduleDefinitions = getDefinitionsForModule(moduleId);
var modulePath = getModulePath(moduleId);
Expand All @@ -97,15 +103,22 @@ function init(){
else{
masterSalt=process.env.CHALLENGE_MASTER_SALT;
}

let dbModuleVersion = await db.getModuleVersion();
if(dbModuleVersion < moduleVer){
util.log("New training modules version, updating module completion for all users.")
recreateBadgesOnModulesUpdate();
db.updateModuleVersion(moduleVer);
}
}

init();

exports.getModules = function(){ return modules; }
exports.getChallengeNames = function(){ return challengeNames; }
exports.getChallengeDefinitions = function(){ return challengeDefinitions; }
getModules = function(){ return modules; }
getChallengeNames = function(){ return challengeNames; }
getChallengeDefinitions = function(){ return challengeDefinitions; }

exports.isPermittedModule = async (user, moduleId) => {
isPermittedModule = async (user, moduleId) => {
let badges = await db.fetchBadges(user.id);
if(util.isNullOrUndefined(modules[moduleId])){
return false;
Expand All @@ -131,9 +144,9 @@ exports.isPermittedModule = async (user, moduleId) => {
/**
* Get the user level based on the amount of passed challenges
*/
exports.getUserLevelForModule = async (user,moduleId) => {
getUserLevelForModule = async (user,moduleId) => {
let moduleDefinitions = getDefinitionsForModule(moduleId);
let passedChallenges = await db.getPromise(db.fetchChallengeEntriesForUser,user);
let passedChallenges = await db.fetchChallengeEntriesForUser(user);
let userLevel=-1;
for(let level of moduleDefinitions){
let passCount = 0;
Expand All @@ -157,11 +170,11 @@ exports.getUserLevelForModule = async (user,moduleId) => {
/**
* Get permitted challenges for module
*/
exports.getPermittedChallengesForUser = async (user, moduleId) => {
getPermittedChallengesForUser = async (user, moduleId) => {
if(util.isNullOrUndefined(moduleId)) return [];
if(util.isNullOrUndefined(modules[moduleId])) return [];

var permittedLevel = await exports.getUserLevelForModule(user, moduleId) + 1;
var permittedLevel = await getUserLevelForModule(user, moduleId) + 1;

var moduleDefinitions = getDefinitionsForModule(moduleId);

Expand All @@ -179,7 +192,7 @@ exports.getPermittedChallengesForUser = async (user, moduleId) => {
* @param {Object} user The session user object
* @param {Array} moduleIds The lesson module ids
*/
exports.getChallengeDefinitionsForUser = async (user, moduleId) => {
getChallengeDefinitionsForUser = async (user, moduleId) => {
var returnChallenges = [];

if(util.isNullOrUndefined(moduleId)) return [];
Expand Down Expand Up @@ -214,7 +227,7 @@ exports.getChallengeDefinitionsForUser = async (user, moduleId) => {
* Returns the solution html (converted from markdown)
* @param {The challenge id} challengeId
*/
exports.getSolution = function (challengeId) {
getSolution = function (challengeId) {
var solution = solutions[challengeId];
var solutionHtml = "";
if(!util.isNullOrUndefined(solution)){
Expand All @@ -229,7 +242,7 @@ exports.getSolution = function (challengeId) {
* Returns the description html (converted from markdown if applicable)
* @param {The challenge id} challengeId
*/
exports.getDescription = function (challengeId) {
getDescription = function (challengeId) {
var description = descriptions[challengeId];
var descriptionHtml = "";
if(util.isNullOrUndefined(description)) return "";
Expand All @@ -252,8 +265,8 @@ exports.getDescription = function (challengeId) {
/**
* Checks if the user has completed the module and issue a badge
*/
exports.verifyModuleCompletion = async (user, moduleId) => {
var userLevel = await exports.getUserLevelForModule(user, moduleId);
verifyModuleCompletion = async (user, moduleId) => {
var userLevel = await getUserLevelForModule(user, moduleId);
let moduleDefinitions = getDefinitionsForModule(moduleId);
var lastLevel = moduleDefinitions[moduleDefinitions.length-1];

Expand All @@ -268,7 +281,7 @@ exports.verifyModuleCompletion = async (user, moduleId) => {
}
}
if(!found){
util.log("WARN: Fixed badge for user.", user);
util.log(`WARN: Fixed badge for module ${moduleId} for user.`, user);
await db.insertBadge(user.id, moduleId);
}
return true;
Expand All @@ -277,11 +290,37 @@ exports.verifyModuleCompletion = async (user, moduleId) => {
return false;
}

/**
* Iterates through the entire list of users to insert badges where needed
*/
recreateBadgesOnModulesUpdate = async () => {

let users = await db.fetchUsersWithId();

for(let user of users){
let entries = await db.fetchChallengeEntriesForUser(user);

var passedChallenges = [];
for(let entry of entries){
passedChallenges.push(entry.challengeId);
}

user.passedChallenges = passedChallenges;
for(let moduleId in modules){
try {
await verifyModuleCompletion(user, moduleId);
} catch (error) {
util.log("Error with badge verification.", user);
}
}
}
}

/**
* Retrieves a code to verify completion of the level
* @param {Badge} badge
*/
exports.getBadgeCode = (badge, user) => {
getBadgeCode = (badge, user) => {
let module = modules[badge.moduleId];

if(util.isNullOrUndefined(module) || util.isNullOrUndefined(module.badgeInfo)) return null;
Expand All @@ -308,7 +347,7 @@ exports.getBadgeCode = (badge, user) => {
* Verifies a badge code and returns parsed info
* @param {Base64} badgeCode
*/
exports.verifyBadgeCode = (badgeCode) => {
verifyBadgeCode = (badgeCode) => {
urlDecoded = decodeURIComponent(badgeCode);
let parts = urlDecoded.split(".");
if(parts.length !== 2) return null;
Expand All @@ -331,7 +370,7 @@ exports.verifyBadgeCode = (badgeCode) => {
* @param {*} badgrInfo
* @param {*} user
*/
module.exports.badgrCall = function(badgrInfo, user){
badgrCall = function(badgrInfo, user){
if(!util.isNullOrUndefined(badgrInfo) && !util.isNullOrUndefined(config.encBadgrToken)){
if(user.email===null){
util.log("Cannot issue badge for this user. E-mail is null.", user);
Expand Down Expand Up @@ -379,7 +418,7 @@ module.exports.badgrCall = function(badgrInfo, user){
/**
* Logic to for the api challenge code
*/
module.exports.apiChallengeCode = async (req) => {
apiChallengeCode = async (req) => {
if(util.isNullOrUndefined(req.body.challengeId) ||
util.isNullOrUndefined(req.body.challengeCode) ||
util.isNullOrUndefined(req.body.moduleId)){
Expand Down Expand Up @@ -408,7 +447,7 @@ module.exports.apiChallengeCode = async (req) => {
var curChallengeObj = null;

//identify the current challenge object and also the available challenges for the current user level
var availableChallenges = await module.exports.getPermittedChallengesForUser(req.user, moduleId);
var availableChallenges = await getPermittedChallengesForUser(req.user, moduleId);

//search for the current challenge id
for(let availableChallenge of availableChallenges){
Expand Down Expand Up @@ -436,24 +475,24 @@ module.exports.apiChallengeCode = async (req) => {
}
//success update challenge
curChallengeObj.moduleId = moduleId;
return module.exports.insertChallengeEntry(req.user, curChallengeObj, moduleId);
return insertChallengeEntry(req.user, curChallengeObj, moduleId);

}

/**
* Inserts a challenge entry
*/
module.exports.insertChallengeEntry = async (user,curChallengeObj, moduleId) => {
insertChallengeEntry = async (user,curChallengeObj, moduleId) => {
await db.getPromise(db.insertChallengeEntry, [user.id,curChallengeObj.id]);
//issue badgr badge if enabled
module.exports.badgrCall(curChallengeObj.badgrInfo,user);
let isModuleComplete = await module.exports.verifyModuleCompletion(user,moduleId);
badgrCall(curChallengeObj.badgrInfo,user);
let isModuleComplete = await verifyModuleCompletion(user,moduleId);
//check to see if the user levelled up
curChallengeObj.moduleComplete = isModuleComplete;
if(isModuleComplete){
util.log(`User has solved the challenge ${curChallengeObj.name} and completed the module!`, user);
//issue badgr badge if enabled for module
module.exports.badgrCall(modules[moduleId].badgrInfo,user);
badgrCall(modules[moduleId].badgrInfo,user);
return {
"message":"Congratulations you solved the challenge and completed the module! You can now get your badge of completion.",
"data":curChallengeObj
Expand All @@ -466,4 +505,26 @@ module.exports.insertChallengeEntry = async (user,curChallengeObj, moduleId) =>
"data": curChallengeObj
}
}
}




module.exports = {
apiChallengeCode,
badgrCall,
getBadgeCode,
getChallengeNames,
getChallengeDefinitions,
getChallengeDefinitionsForUser,
getDescription,
getModules,
getPermittedChallengesForUser,
getUserLevelForModule,
getSolution,
insertChallengeEntry,
isPermittedModule,
verifyBadgeCode,
verifyModuleCompletion,
recreateBadgesOnModulesUpdate
}
Loading
Loading