UNPKG

4.25 kBJavaScriptView Raw
1var crypto = require('crypto');
2
3const RANDOM_BATCH_SIZE = 256;
4
5var randomIndex;
6var randomBytes;
7
8var getNextRandomValue = function() {
9 if (randomIndex === undefined || randomIndex >= randomBytes.length) {
10 randomIndex = 0;
11 randomBytes = crypto.randomBytes(RANDOM_BATCH_SIZE);
12 }
13
14 var result = randomBytes[randomIndex];
15 randomIndex += 1;
16
17 return result;
18};
19
20// Generates a random number
21var randomNumber = function(max) {
22 // gives a number between 0 (inclusive) and max (exclusive)
23 var rand = getNextRandomValue();
24 while (rand >= 256 - (256 % max)) {
25 rand = getNextRandomValue();
26 }
27 return rand % max;
28};
29
30// Possible combinations
31var lowercase = 'abcdefghijklmnopqrstuvwxyz',
32 uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
33 numbers = '0123456789',
34 symbols = '!@#$%^&*()+_-=}{[]|:;"/?.><,`~',
35 similarCharacters = /[ilLI|`oO0]/g,
36 strictRules = [
37 { name: 'lowercase', rule: /[a-z]/ },
38 { name: 'uppercase', rule: /[A-Z]/ },
39 { name: 'numbers', rule: /[0-9]/ },
40 { name: 'symbols', rule: /[!@#$%^&*()+_\-=}{[\]|:;"/?.><,`~]/ }
41 ];
42
43var generate = function(options, pool) {
44 var password = '',
45 optionsLength = options.length,
46 poolLength = pool.length;
47
48 for (var i = 0; i < optionsLength; i++) {
49 password += pool[randomNumber(poolLength)];
50 }
51
52 if (options.strict) {
53 // Iterate over each rule, checking to see if the password works.
54 var fitsRules = strictRules.every(function(rule) {
55 // If the option is not checked, ignore it.
56 if (options[rule.name] == false) return true;
57
58 // Treat symbol differently if explicit string is provided
59 if (rule.name === 'symbols' && typeof options[rule.name] === 'string') {
60 // Create a regular expression from the provided symbols
61 var re = new RegExp('['+options[rule.name]+']');
62 return re.test(password);
63 }
64
65 // Run the regex on the password and return whether
66 // or not it matches.
67 return rule.rule.test(password);
68 });
69
70 // If it doesn't fit the rules, generate a new one (recursion).
71 if (!fitsRules) return generate(options, pool);
72 }
73
74 return password;
75};
76
77// Generate a random password.
78module.exports.generate = function(options) {
79 // Set defaults.
80 options = options || {};
81 if (!Object.prototype.hasOwnProperty.call(options, 'length')) options.length = 10;
82 if (!Object.prototype.hasOwnProperty.call(options, 'numbers')) options.numbers = false;
83 if (!Object.prototype.hasOwnProperty.call(options, 'symbols')) options.symbols = false;
84 if (!Object.prototype.hasOwnProperty.call(options, 'exclude')) options.exclude = '';
85 if (!Object.prototype.hasOwnProperty.call(options, 'uppercase')) options.uppercase = true;
86 if (!Object.prototype.hasOwnProperty.call(options, 'lowercase')) options.lowercase = true;
87 if (!Object.prototype.hasOwnProperty.call(options, 'excludeSimilarCharacters')) options.excludeSimilarCharacters = false;
88 if (!Object.prototype.hasOwnProperty.call(options, 'strict')) options.strict = false;
89
90 if (options.strict) {
91 var minStrictLength = 1 + (options.numbers ? 1 : 0) + (options.symbols ? 1 : 0) + (options.uppercase ? 1 : 0);
92 if (minStrictLength > options.length) {
93 throw new TypeError('Length must correlate with strict guidelines');
94 }
95 }
96
97 // Generate character pool
98 var pool = '';
99
100 // lowercase
101 if (options.lowercase) {
102 pool += lowercase;
103 }
104
105 // uppercase
106 if (options.uppercase) {
107 pool += uppercase;
108 }
109 // numbers
110 if (options.numbers) {
111 pool += numbers;
112 }
113 // symbols
114 if (options.symbols) {
115 if (typeof options.symbols === 'string') {
116 pool += options.symbols;
117 } else {
118 pool += symbols;
119 }
120 }
121
122 // Throw error if pool is empty.
123 if (!pool) {
124 throw new TypeError('At least one rule for pools must be true');
125 }
126
127 // similar characters
128 if (options.excludeSimilarCharacters) {
129 pool = pool.replace(similarCharacters, '');
130 }
131
132 // excludes characters from the pool
133 var i = options.exclude.length;
134 while (i--) {
135 pool = pool.replace(options.exclude[i], '');
136 }
137
138 var password = generate(options, pool);
139
140 return password;
141};
142
143// Generates multiple passwords at once with the same options.
144module.exports.generateMultiple = function(amount, options) {
145 var passwords = [];
146
147 for (var i = 0; i < amount; i++) {
148 passwords[i] = module.exports.generate(options);
149 }
150
151 return passwords;
152};