Home Reference Source

node-user-accounts-boilerplate-nahid/fields/strongPassword.js

"use strict";

const password = require('./password');

module.exports = Object.assign({}, password);

module.exports.assign = async function (user, field, value, fieldMeta, loginUser, config)
{
  if (typeof value === 'string' && value !== '')
  {
    this.checkStrongPassword(value);
  }
  await password.assign.call(this, user, field, value, fieldMeta, loginUser, config);
};

const sequences = [
  'abcdefghijklmnopqrstuvwxyz',
  'pyfgcrlaoeuidhtnsqjkxbmwvz',
  '0123456789',
  'qwertyuiop[]asdfghjkl;\'zxcvbnm,./',
  '!@#$%^&*()_+'
];

function findMaxSequenceSize(password)
{
  var maxSequence = 0;

  password = password.toLowerCase();

  sequences.forEach(function (sequence)
  {
    var converted = Array.prototype.map.call(password, function (c)
    {
      return sequence.indexOf(c);
    });
    var progressiveSize = 1;
    var sameSize = 1;
    for (var x = 1; x < converted.length; x++)
    {
      if (converted[x] === converted[x - 1] && converted[x] !== -1)
      {
        sameSize++;
      }
      else
      {
        if (sameSize > maxSequence)
        {
          maxSequence = sameSize;
        }
        sameSize = 1;
      }
      if (converted[x] === converted[x - 1] + 1 && converted[x - 1] !== -1)
      {
        progressiveSize++;
      }
      else
      {
        if (progressiveSize > maxSequence)
        {
          maxSequence = progressiveSize;
        }
        progressiveSize = 1;
      }
    }
    if (sameSize > maxSequence)
    {
      maxSequence = sameSize;
    }
    if (progressiveSize > maxSequence)
    {
      maxSequence = progressiveSize;
    }
  });
  return maxSequence;
}

module.exports.checkStrongPassword = function (password)
{
  if (password.length > 12)
  {
    return true;
  }

  if (!password || password.length < 10)
  {
    throw new Error('Password must be provided and must be at least 10 characters. Must consist of lowercase, uppercase letters, numbers, symbols. Must not contain spaces. Must not be sequential.');
  }

  if (!password.match(/[a-z]+/))
  {
    throw new Error('Password must consist of lowercase, uppercase letters, numbers and symbols.');
  }

  if (!password.match(/[A-Z]+/))
  {
    throw new Error('Password must consist of lowercase, uppercase letters, numbers and symbols.');
  }

  if (!password.match(/[0-9]+/))
  {
    throw new Error('Password must consist of lowercase, uppercase letters, numbers and symbols.');
  }

  if (!password.match(/[^a-zA-Z0-9]+/))
  {
    throw new Error('Password must consist of lowercase, uppercase letters, numbers and symbols.');
  }

  if (password.match(/\s+/))
  {
    throw new Error('Password must not consist whitespace characters');
  }

  if (findMaxSequenceSize(password) > 2)
  {
    throw new Error('Password must not consist of sequences of size greater than 2.');
  }

  return true;
}

/**
 * generate strong password
 * @param {number} [length=10] length of password to generate
 * @return {string}
 */
module.exports.generate = function generate(length = 10)
{
  const groups = [
    '0123456789',
    'abcdefghijklmnopqrstuvwxyz',
    'ABCDEFGJIJKLMNOPQRSTUVWXYZ',
    '!@$^*_'
  ];

  let availableOrder = [0, 1, 2, 3];
  let order = [];

  while (availableOrder.length > 0)
  {
    order.push(availableOrder.splice(Math.floor(Math.random() * availableOrder.length), 1)[0]);
  }

  let password = '';

  for (let i = 0; i < length; i++)
  {
    let currentGroup = groups[i % groups.length];

    password += currentGroup[Math.floor(Math.random() * currentGroup.length)];
  }

  return password;
}