"use strict";
/**
* Creates new validator with some rules overriden. Use `tweaked.js` export to more friendly configuration (object with
* defaults, instead of long list of required arguments)
* @param {number} fullPoints - points required to score for value to be marked as "100% secure"
* @param {number} requiredPercentage - percentage value required for password to be considered safe
* @param {number} pointsPerChar - how many points are added per character
* @param {number} pointsPerUnique - how many additional points are added per unique character
* @param {number} repeatedCharPenalty - how many points are taken for repeated characters
* @param {number} repeatedCharCount - how many times a character should be repeated before being considered
* "repeated"
* @param {number} repeatedCharMaxPosition - position in string after characters are no longer considered
* repeated (this is used to avoid longer passwords to be considered less and less secure, because user run out of
* unique characters on keyboard)
* @param {number} minUniqueChars - minimal count of unique characters required to not lowering the points to
* `maxNonUniqueScore` value
* @param {number} maxNonUniqueScore - if minimal count of unique characters is not met - this is the point to
* the score will be lowered (only if already higher)
* @returns {Validator}
*/
const createValidator = (
fullPoints, requiredPercentage, pointsPerChar, pointsPerUnique, repeatedCharPenalty, repeatedCharCount,
repeatedCharMaxPosition, minUniqueChars, maxNonUniqueScore,
) => {
const calculate = (value) => {
const chars = value.split("");
const map = {};
let points = 0;
chars.forEach((char, index) => {
if (!map[char]) { map[char] = 0; }
map[char]++;
points += pointsPerChar; // every new char
if (map[char] === 1) { // unique char
points += pointsPerUnique;
}
if (map[char] > repeatedCharCount) { // non unique char
if (index <= repeatedCharMaxPosition) { // for char on position <= 12
points -= repeatedCharPenalty;
}
}
});
if (Object.keys(map).length <= minUniqueChars) {
if (points > maxNonUniqueScore) {
points = maxNonUniqueScore;
}
}
return points;
};
const format = (points) => {
const percentage = Math.min(points * (100 / fullPoints), 100); // eslint-disable-line no-magic-numbers
const valid = percentage >= requiredPercentage;
return {
points,
percentage,
valid,
};
};
return value => format(calculate(value));
};
/**
* @typedef {Object} Result
* @property {number} points - scored points
* @property {number} percentage - percentage value of password security (0 - 100)
* @property {boolean} valid - is password score considered secure enough
*/
/**
* @typedef {function} Validator
* @param {string} value - password to measure its security level
* @returns {Result}
*/
/**
* @type {Validator}
*/
const validate = createValidator(18, 75, 1, 0.5, 1.5, 3, 7, 5, 5); // eslint-disable-line no-magic-numbers
validate.createValidator = createValidator;
module.exports = validate;