mozilla/webmaker.org

View on GitHub
routes/badges.js

Summary

Maintainability
F
4 days
Test Coverage
var BadgeClient = require('badgekit-api-client');
var async = require('async');

module.exports = function (env) {
  // Error messages
  var errorHandlers = {
    unauthorized: function () {
      var err = new Error('You must be logged in to access this area.');
      err.status = 401;
      return err;
    },
    forbidden: function () {
      var errorString = 'You are not authorized to access this area. ' +
        'If you think you are supposed to have permissions, ' +
        'try logging out and in again, or contact help@webmaker.org';
      var err = new Error(errorString);
      err.status = 403;
      return err;
    }
  };

  var badgeClient = new BadgeClient(env.get('BADGES_ENDPOINT'), {
    key: env.get('BADGES_KEY'),
    secret: env.get('BADGES_SECRET')
  });

  var permissionsModel = require('../lib/badges-permissions-model.js');

  return {
    middleware: {
      // Check if a use has at least a certain level of permissions
      // Can be 'admin', 'superMentor' or 'mentor'
      atleast: function (level) {
        var levels = ['isAdmin', 'isSuperMentor', 'isMentor'];
        return function (req, res, next) {
          var user = req.session.user;
          if (!level || levels.indexOf(level) <= -1) {
            var problem = level + ' is not a valid type of user.';
            var err = new Error('There is a problem with the permissions model: ' + problem);
            return next(err);
          } else if (!user) {
            return next(errorHandlers.unauthorized());
          } else if (user.isAdmin) {
            return next();
          } else if (level === 'isSuperMentor' && (user.isAdmin || user.isSuperMentor)) {
            return next();
          } else if (level === 'isMentor' && (user.isAdmin || user.isSuperMentor || user.isMentor)) {
            return next();
          } else {
            return next(errorHandlers.forbidden());
          }
        };
      },
      // Does this user have permissions to issue, approve applications, see instances?
      hasPermissions: function (action) {
        return function (req, res, next) {
          var user = req.session.user;

          if (!user) {
            return next(errorHandlers.unauthorized());
          }

          var allowed = permissionsModel({
            badge: req.params.badge,
            user: req.session.user,
            action: action
          });

          if (!allowed) {
            return next(errorHandlers.forbidden());
          } else {
            return next();
          }
        };
      }
    },
    getAll: function (req, res, next) {
      badgeClient.getBadges({
        system: env.get('BADGES_SYSTEM')
      }, function (err, badges) {
        if (err) {
          return res.send(500, err.message);
        }
        res.json(badges);
      });
    },
    getBadge: function (req, res, next) {
      badgeClient.getBadge({
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge
      }, function (err, data) {
        if (err) {
          return res.send(500, err.message);
        }
        return res.send(data);
      });
    },
    getInstances: function (req, res, next) {
      badgeClient.getBadgeInstances({
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge
      }, function (err, instances) {
        // errorString
        if (err) {
          return res.send(500, err.message);
        }

        // We need to get the badge data too
        badgeClient.getBadge({
          system: env.get('BADGES_SYSTEM'),
          badge: req.params.badge
        }, function (err, badge) {
          if (err) {
            return res.send(500, err.message);
          }
          res.json({
            badge: badge,
            instances: instances
          });
        });
      });
    },
    deleteInstance: function (req, res, next) {
      badgeClient.deleteBadgeInstance({
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge,
        email: req.params.email
      }, function (err, result) {
        if (err) {
          console.log(err.stack);
          return res.send(500, err.message);
        }
        res.send('DELETED');
      });
    },
    details: function (req, res, next) {
      function getBadge(callback) {
        badgeClient.getBadge({
          system: env.get('BADGES_SYSTEM'),
          badge: req.params.badge
        }, function (err, data) {
          return callback(err, data);
        });
      }

      function getInstance(callback) {
        if (req.session.user) {
          badgeClient.getBadgeInstance({
            system: env.get('BADGES_SYSTEM'),
            badge: req.params.badge,
            email: req.session.user.email
          }, function (err, data) {
            if (err && err.name === 'ResourceNotFoundError') {
              err = null;
            }
            return callback(err, data);
          });
        } else {
          return callback(null, null);
        }
      }

      function getApplication(callback) {
        if (req.session.user) {
          badgeClient.getApplications({
            system: env.get('BADGES_SYSTEM'),
            badge: req.params.badge
          }, {
            email: req.session.user.email,
            processed: false
          }, function (err, data) {
            return callback(err, data);
          });
        } else {
          return callback(null, null);
        }
      }
      async.parallel(
        [getBadge, getInstance, getApplication],
        function (err, results) {
          if (err) {
            return res.render('badge-not-found.html', {
              page: 'search',
              view: 'badges'
            });
          }

          var badge = results[0];
          var instance = results[1];
          var application = (results[2] && results[2].length) ? results[2][0] : null;

          // Shim for https://bugzilla.mozilla.org/show_bug.cgi?id=1001161
          if (badge.issuer && !badge.issuer.imageUrl) {
            badge.issuer.imageUrl = 'https://webmaker.org/img/logo-webmaker.png';
          }

          // Can the current user issue this badge?
          var canIssue = permissionsModel({
            badge: req.params.badge,
            user: req.session.user,
            action: 'issue'
          });

          // Do we want to ask which Hive city the earner is affiliated with?
          var requestCity = (req.params.badge === 'hive-community-member');

          // Check if we have any criteria to display.
          var canApply = true;
          if (['Data Trail Timeline', 'Privacy Coach', 'IP Address Tracer'].indexOf(badge.name) >= 0) {
            canApply = false;
          }

          res.render('badge-detail.html', {
            page: req.params.badge,
            view: 'badges',
            badge: badge,
            canIssue: canIssue,
            canApply: canApply,
            requestCity: requestCity,
            backpackUrl: env.get('BACKPACK_PUSH_URL'),
            assertionUrl: instance ? instance.assertionUrl : null,
            application: application
          });
        });
    },
    apply: function (req, res, next) {
      var evidence = [req.body.evidence];
      var applicationSlug = req.body.applicationSlug;

      if (req.body.city) {
        evidence.push('Hive City: ' + req.body.city);
      }

      evidence = evidence.map(function (evidence) {
        return {
          reflection: evidence
        };
      });

      var application = {
        learner: req.session.user.email,
        evidence: evidence,
        slug: applicationSlug
      };

      var apiFunction;
      if (applicationSlug) {
        apiFunction = badgeClient.updateApplication.bind(badgeClient);
      } else {
        apiFunction = badgeClient.addApplication.bind(badgeClient);
      }

      apiFunction({
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge,
        application: application
      }, function (err, data) {
        if (err) {
          return res.send(500, err);
        }
        res.send(data);
      });
    },
    issue: function (req, res, next) {
      var apiFunction;
      var query = {
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge,
        comment: req.body.comment
      };

      if (req.body.emails) {
        query.emails = req.body.emails;
        apiFunction = badgeClient.createBadgeInstances.bind(badgeClient);
      } else if (req.body.email) {
        query.email = req.body.email;
        apiFunction = badgeClient.createBadgeInstance.bind(badgeClient);
      } else {
        return res.send(500, {
          error: 'No email address provided'
        });
      }

      apiFunction(query, function (err, data) {
        if (err) {
          var errorString = err.toString();
          return res.send(500, {
            error: errorString
          });
        }
        res.send(data);
      });
    },
    claim: function (req, res, next) {
      var codeQuery = {
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge,
        claimCode: req.body.claimcode
      };

      badgeClient.claimClaimCode(codeQuery, req.session.user.email, function (err, data) {
        if (err) {
          var errorString = err.message;
          if (err.code === 404) {
            errorString = 'The code "' + req.body.claimcode + '" could not be found';
          }
          return res.send(500, {
            error: errorString
          });
        }

        var instanceQuery = {
          system: env.get('BADGES_SYSTEM'),
          badge: req.params.badge,
          email: req.session.user.email
        };

        badgeClient.createBadgeInstance(instanceQuery, function (err, data) {
          if (err) {
            var errorString = err.message;
            return res.send(500, errorString);
          }

          res.send(data);
        });
      });
    },
    getApplications: function (req, res, next) {
      badgeClient.getApplications({
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge
      }, function (err, raw) {
        if (err) {
          return res.send(500, err.message);
        }

        var applications = [];
        // No way to query for pending applications only.
        // See bug 1021009
        if (req.query.processed) {
          applications = raw;
        } else {
          raw.forEach(function (application) {
            if (!application.processed) {
              applications.push(application);
            }
          });
        }

        res.send(applications);
      });
    },
    submitReview: function (req, res, next) {
      var context = {
        system: env.get('BADGES_SYSTEM'),
        badge: req.params.badge,
        application: req.params.application,
        review: {
          author: req.session.email,
          comment: req.body.comment,
          reviewItems: req.body.reviewItems
        }
      };
      badgeClient.addReview(context, function (err, review) {
        if (err) {
          return res.send(500, err.message);
        }
        return res.send(200, 'Success');
      });
    },
    create: function (req, res, next) {
      var context = {
        system: env.get('BADGES_SYSTEM'),
        badge: req.body
      };
      badgeClient.createBadge(context, function (err, badge) {
        if (err) {
          return res.send(500, err.message);
        }
        return res.send(200, badge);
      });
    },
    update: function (req, res, next) {
      var context = {
        system: env.get('BADGES_SYSTEM'),
        badge: req.body
      };
      badgeClient.updateBadge(context, function (err, badge) {
        if (err) {
          return res.send(500, err.message);
        }
        return res.send(200, badge);
      });
    }
  };
};