{"size": 8458, "ext": "js", "max_stars_repo_path": "modules/auth.js", "max_stars_repo_name": "mrkvon/livegraph", "max_stars_repo_stars_event_min_datetime": "2015-10-05T17:36:08.000Z", "max_stars_repo_stars_event_max_datetime": "2015-12-27T13:19:55.000Z", "max_issues_repo_path": "modules/auth.js", "max_issues_repo_name": "mrkvon/livegraph", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "modules/auth.js", "max_forks_repo_name": "mrkvon/livegraph", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 2, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 29.4703832753, "max_line_length": 126, "alphanum_fraction": 0.6558288011}
'use strict'

/** this module serves as wrapper to provide higher-level account functions
  * it can contain functions like: verifying new email, resetting passwords etc.
  *
  * @module modules/account
  */

//var validate = require('../services/validation');
var database = require('../modules/database');
var cryptoFcs = require('./auth/crypto');
//var mailer = require('../services/mailer/mailer');

const ITERATIONS = 10000;//accountConfig.password.iterations;

var badInput = new Error('incorrect function input');


var exports = module.exports = {};

/**
 * This function will:
 *  create code, salt and hash for email verification
 *  save new email, salt and hash to database and set verified to false
 *  send verification email with url https://ditup.org/account/verify-email/:username/:code
 *
 * @param {Object} data
 * @param {string} data.username
 * @param {string} data.email
 * @returns {Promise}
 *//*
exports.initEmailVerification = function (data) {
  var username = data.username;
  var email = data.email;
  //create verification code, salt & hash

  var code;
  var salt;
  var hash;

  return accountService.generateHexCode(16)
    .then(function (_code){
      code = _code;
      return accountService.generateSalt();
    })
    .then(function (_salt) {
      salt = _salt;
      return accountService.hashPassword(code, salt, ITERATIONS);
    })
    .then(function (_hash) {
      hash = _hash;

      var data = {
        create_date: Date.now(),
        hash: hash,
        salt: salt,
        iterations: ITERATIONS
      };
      var user = {
        username: username,
        email: email
      };
      return database.updateUserEmailVerifyCode(user, data);
    })
    .then(function sendEmail() {
      var mailerData = {
        username: username,
        email: email,
        url: 'https://ditup.org/account/verify-email/'+username+'/'+code
      };
      return mailer.send.verifyEmail(mailerData);
    });
};*/

/**
 * This function will
 *  check if email is not yet verified
 *  check if code is not expired (not older than 2 hours)
 *  check if code is correct
 *  and if all above is true, will verify email in database (verified = true, hex & salt & iterations = null, set verify_date)
 *
 * @param {Object} data
 * @param {string} data.username
 * @param {string} data.code
 * @returns {Promise}
 */
exports.verifyEmail = function (data) {
  var username = data.username;
  var code = data.code;

  var hash, salt, iterations, createDate;
  
  //first read user from database (async)
  return database.readUser({username: username})
    .then(function (user) {
      //if user is already verified, don't continue
      if(user.account.email.verified === true) throw new Error('user ' + username + ' has already verified email');

      hash = user.account.email.hash;
      salt = user.account.email.salt;
      iterations = user.account.email.iterations;
      createDate = user.account.email.create_date;
      
      //check that create_date of code is not older than 2 hours
      var isExpired = Date.now() - createDate > 2*3600*1000;
      if(isExpired) throw new Error('code is expired');

      //hash verification code (async)
      return accountService.hashPassword(code, salt, iterations);
    })
    .then(function (hash2) {
      //compare hash codes
      var areHashesEqual = accountService.compareHashes(hash, hash2);
      if(areHashesEqual !== true) throw new Error('code is wrong');
      //
      return database.updateUserEmailVerified({username: username}, {verified: true, verifyDate: Date.now()});
    });
};

/**
 * This function will:
 *  create code, salt and hash for resetting password
 *  save salt, hash and creation time to database and set verified to false
 *  send reset-password email with url https://ditup.org/account/reset-password/:username/:code
 * @param {Object} user
 * @param {string} user.username
 * @param {string} user.email
 * @returns {Promise}
 */
exports.initResetPassword = function (user) {
  var username = user.username;
  var email = user.email;
  //create verification code, salt & hash

  var code;
  var salt;
  var hash;

  return Promise.all([accountService.generateHexCode(16), accountService.generateSalt()])
    .then(function (_ret){
      code = _ret[0];
      salt = _ret[1];
      return accountService.hashPassword(code, salt, ITERATIONS);
    })
    .then(function (_hash) {
      hash = _hash;

      var data = {
        create_date: Date.now(),
        hash: hash,
        salt: salt,
        iterations: ITERATIONS
      };
      var user = {
        username: username,
      };
      return database.updateUserResetPasswordCode(user, data);
    })
    .then(function sendEmail() {
      var mailerData = {
        username: username,
        email: email,
        url: 'https://ditup.org/account/reset-password/'+username+'/'+code
      };
      return mailer.send.resetPassword(mailerData);
    });
};

exports.isResetPasswordCodeValid = function (data) {
  if(data.hasOwnProperty('username') !== true || data.hasOwnProperty('code') !== true) throw badInput;
  //{username: username, code: code}
  var username = data.username;
  var code = data.code;

  var hash, salt, iterations, createDate;
  
  //first read user from database (async)
  return database.readUser({username: username})
    .then(function (user) {

      //if reset_password is not there, throw not initialised error
      let notThere = !user.account.hasOwnProperty('reset_password') ||
        !user.account.reset_password ||
        !user.account.reset_password.hash ||
        !user.account.reset_password.salt ||
        !user.account.reset_password.iterations ||
        !user.account.reset_password.create_date;

      if(notThere) throw new Error('reset is not initialised');

      hash = user.account.reset_password.hash;
      salt = user.account.reset_password.salt;
      iterations = user.account.reset_password.iterations;
      createDate = user.account.reset_password.create_date;
      
      //check that create_date of code is not older than 30 minutes
      var isExpired = Date.now() - createDate > 1800*1000;
      if(isExpired) throw new Error('code is expired');

      //hash verification code (async)
      return accountService.hashPassword(code, salt, iterations);
    })
    .then(function (hash2) {
      //compare hash codes
      var areHashesEqual = accountService.compareHashes(hash, hash2);
      if(areHashesEqual !== true) throw new Error('code is wrong');
      //
      return true;
    });
};

exports.updatePassword = function (data) {
  if(!data.hasOwnProperty('username') || !data.hasOwnProperty('password')) throw badInput;
  var username = data.username;
  var password = data.password;
  var salt;
  var hash;

  //generate salt for password hash
  return accountService.generateSalt()
    .then(function (_salt) {
      salt = _salt;

      //generate hash
      return accountService.hashPassword(password, salt, ITERATIONS);
    })
    .then(function (_hash) {
      hash = _hash;

      //this user should be saved to database
      var login = {
        salt: salt,
        hash: hash,
        iterations: ITERATIONS
      };

      var user = {username: username};
      
      //save new user to database
      return database.updateUserPassword(user, login);
    });
};

/**
 * @param {Object} data
 * @param {string} data.username
 * @param {string} data.password
 * @returns {Promise<boolean>}
 */
exports.matchPassword = function (data) {
  if(!data.hasOwnProperty('username') || !data.hasOwnProperty('password')) throw badInput;

  var username = data.username;
  var password = data.password;
  var hash, salt, iterations;

  return database.readUser({username: username})
    .then(function (user) {
      hash = user.login.hash;
      salt = user.login.salt;
      iterations = user.login.iterations;

      //hash the provided password
      return accountService.hashPassword(password, salt, iterations);
    })
    .then(function (hash2) {
      //compare password hashes
      var isPasswordCorrect = accountService.compareHashes(hash, hash2);
      if(isPasswordCorrect === true) {
        return true;
      }

      return false;
    });
};

/**
 * @params {Object} user
 * @params {string} user.username
 * @returns {Promise}
 */
exports.deleteUser = function (user) {
  //more fancy and complicated deleting of user content, ideally in transaction

  return database.deleteUser(user);
};
