Skip to content

Commit f8dcf0d

Browse files
author
imzhengfei
committed
fix(mysql): improve random(with LCG) password for mysql user
1 parent 53a3425 commit f8dcf0d

File tree

2 files changed

+72
-6
lines changed

2 files changed

+72
-6
lines changed

extensions/mysql/index.js

+68-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const Promise = require('bluebird');
44
const mysql = require('mysql');
5-
const crypto = require('crypto');
65
const omit = require('lodash/omit');
76
const cli = require('../../lib');
87

@@ -77,7 +76,74 @@ class MySQLExtension extends cli.Extension {
7776
}
7877

7978
createUser(ctx, dbconfig) {
80-
const randomPassword = crypto.randomBytes(10).toString('hex');
79+
// Generate random MySQL password
80+
const generateRandomPassword = () => {
81+
/**
82+
* LCG Random
83+
* @example const random = (lcgRandom())
84+
* @description Use Linear Congruential Generator to generate a random number between 0 and 1
85+
* Instead of Math.random()
86+
*/
87+
const lcgRandom = (() => {
88+
let _seed = (new Date()).getTime();
89+
return () => {
90+
_seed = (_seed * 9301 + 49297) % 233280;
91+
return _seed / (233280.0);
92+
};
93+
})();
94+
95+
/**
96+
* Random Sort
97+
* @param {Array} array
98+
* @return {Array}
99+
*/
100+
const randomSort = (array) => array.sort(() => (lcgRandom()) > .5 ? -1 : 1);
101+
102+
/**
103+
* Random Number
104+
* @param {Number} length
105+
* @return {String}
106+
*/
107+
const randomNumber = (length) => {
108+
let _str = '';
109+
for (let i = 0; i < length; i = i + 1) {
110+
_str += String.fromCharCode(Math.round((lcgRandom()) * 9) + 48);
111+
}
112+
return _str;
113+
};
114+
115+
/**
116+
* Random Letter
117+
* @param {Number} length
118+
* @param {Boolean} [capital=false]
119+
* @return {String}
120+
*/
121+
const randomLetter = (length, capital) => {
122+
let _str = '';
123+
for (let i = 0; i < length; i = i + 1) {
124+
_str += String.fromCharCode(Math.round((lcgRandom()) * 25) + 97);
125+
}
126+
return capital ? _str.toUpperCase() : _str;
127+
};
128+
129+
/**
130+
* Random Symbol
131+
* @param {Number} length
132+
* @return {String}
133+
*/
134+
const randomSymbol = (length) => {
135+
const chars = ['~', '!', '@', '#', '$'];
136+
return randomSort(chars).join('').substr(0, length);
137+
};
138+
139+
const allChars = randomNumber(6)
140+
+ randomLetter(4)
141+
+ randomLetter(4, true)
142+
+ randomSymbol(2);
143+
144+
return randomSort(allChars.split('')).join('');
145+
};
146+
const randomPassword = generateRandomPassword();
81147

82148
// IMPORTANT: we generate random MySQL usernames
83149
// e.g. you delete all your Ghost instances from your droplet and start from scratch, the MySQL users would remain and the CLI has to generate a random user name to work

extensions/mysql/test/extension-spec.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,14 @@ describe('Unit: Mysql extension', function () {
192192
expect(queryStub.calledThrice).to.be.true;
193193
expect(queryStub.args[0][0]).to.match(/^CREATE USER 'ghost-[0-9]{1,4}'@'localhost' IDENTIFIED WITH mysql_native_password;$/);
194194
expect(queryStub.args[1][0]).to.equal('SET old_passwords = 0;');
195-
expect(queryStub.args[2][0]).to.match(/^SET PASSWORD FOR 'ghost-[0-9]{1,4}'@'localhost' = PASSWORD\('[0-9A-Fa-f]*'\);$/);
195+
expect(queryStub.args[2][0]).to.match(/^SET PASSWORD FOR 'ghost-[0-9]{1,4}'@'localhost' = PASSWORD\('[0-9A-Za-z~!@#$%]*'\);$/);
196196
expect(logStub.calledThrice).to.be.true;
197197
expect(logStub.args[0][0]).to.match(/created new user/);
198198
expect(logStub.args[1][0]).to.match(/disabled old_password/);
199199
expect(logStub.args[2][0]).to.match(/successfully created password for user/);
200200
expect(ctx.mysql).to.exist;
201201
expect(ctx.mysql.username).to.match(/^ghost-[0-9]{1,4}$/);
202-
expect(ctx.mysql.password).to.match(/^[0-9A-Fa-f]*$/);
202+
expect(ctx.mysql.password).to.match(/^[0-9A-Za-z~!@#$%]*$/);
203203
});
204204
});
205205

@@ -219,15 +219,15 @@ describe('Unit: Mysql extension', function () {
219219
expect(queryStub.args[0][0]).to.match(/^CREATE USER 'ghost-[0-9]{1,4}'@'localhost' IDENTIFIED WITH mysql_native_password;$/);
220220
expect(queryStub.args[1][0]).to.match(/^CREATE USER 'ghost-[0-9]{1,4}'@'localhost' IDENTIFIED WITH mysql_native_password;$/);
221221
expect(queryStub.args[2][0]).to.equal('SET old_passwords = 0;');
222-
expect(queryStub.args[3][0]).to.match(/^SET PASSWORD FOR 'ghost-[0-9]{1,4}'@'localhost' = PASSWORD\('[0-9A-Fa-f]*'\);$/);
222+
expect(queryStub.args[3][0]).to.match(/^SET PASSWORD FOR 'ghost-[0-9]{1,4}'@'localhost' = PASSWORD\('[0-9A-Za-z~!@#$%]*'\);$/);
223223
expect(logStub.callCount).to.equal(4);
224224
expect(logStub.args[0][0]).to.match(/user exists, re-trying user creation/);
225225
expect(logStub.args[1][0]).to.match(/created new user/);
226226
expect(logStub.args[2][0]).to.match(/disabled old_password/);
227227
expect(logStub.args[3][0]).to.match(/successfully created password for user/);
228228
expect(ctx.mysql).to.exist;
229229
expect(ctx.mysql.username).to.match(/^ghost-[0-9]{1,4}$/);
230-
expect(ctx.mysql.password).to.match(/^[0-9A-Fa-f]*$/);
230+
expect(ctx.mysql.password).to.match(/^[0-9A-Za-z~!@#$%]*$/);
231231
});
232232
});
233233

0 commit comments

Comments
 (0)