mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
✨ add token utils (#7554)
no issue - preperation for User model refactoring - add independent util to generate reset hash, compare a hash and extract information out of it - this code is basically a copy/paste of User model (generateResetToken, validateToken)
This commit is contained in:
parent
5b9c213849
commit
4abb9590a1
3 changed files with 194 additions and 1 deletions
|
@ -105,7 +105,8 @@ utils = {
|
|||
zipFolder: require('./zip-folder'),
|
||||
readThemes: require('./read-themes'),
|
||||
generateAssetHash: require('./asset-hash'),
|
||||
url: require('./url')
|
||||
url: require('./url'),
|
||||
tokens: require('./tokens')
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
|
|
84
core/server/utils/tokens.js
Normal file
84
core/server/utils/tokens.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
var crypto = require('crypto');
|
||||
|
||||
exports.resetToken = {
|
||||
generateHash: function generateHash(options) {
|
||||
options = options || {};
|
||||
|
||||
var hash = crypto.createHash('sha256'),
|
||||
expires = options.expires,
|
||||
email = options.email,
|
||||
dbHash = options.dbHash,
|
||||
password = options.password,
|
||||
text = '';
|
||||
|
||||
hash.update(String(expires).toLocaleLowerCase());
|
||||
hash.update(String(email).toLocaleLowerCase());
|
||||
hash.update(String(dbHash).toLocaleLowerCase());
|
||||
hash.update(String(password).toLocaleLowerCase());
|
||||
|
||||
text += [expires, email, hash.digest('base64')].join('|');
|
||||
return new Buffer(text).toString('base64');
|
||||
},
|
||||
extract: function extract(options) {
|
||||
options = options || {};
|
||||
|
||||
var token = options.token,
|
||||
tokenText = new Buffer(token, 'base64').toString('ascii'),
|
||||
parts,
|
||||
expires,
|
||||
email;
|
||||
|
||||
parts = tokenText.split('|');
|
||||
|
||||
// Check if invalid structure
|
||||
if (!parts || parts.length !== 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
expires = parseInt(parts[0], 10);
|
||||
email = parts[1];
|
||||
|
||||
return {
|
||||
expires: expires,
|
||||
email: email
|
||||
};
|
||||
},
|
||||
/*jslint bitwise:true*/
|
||||
compare: function compare(options) {
|
||||
options = options || {};
|
||||
|
||||
var tokenToCompare = options.token,
|
||||
parts = exports.resetToken.extract({token: tokenToCompare}),
|
||||
dbHash = options.dbHash,
|
||||
password = options.password,
|
||||
generatedToken,
|
||||
diff = 0,
|
||||
i;
|
||||
|
||||
if (isNaN(parts.expires)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if token is expired to prevent replay attacks
|
||||
if (parts.expires < Date.now()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
generatedToken = exports.resetToken.generateHash({
|
||||
email: parts.email,
|
||||
expires: parts.expires,
|
||||
dbHash: dbHash,
|
||||
password: password
|
||||
});
|
||||
|
||||
if (tokenToCompare.length !== generatedToken.length) {
|
||||
diff = 1;
|
||||
}
|
||||
|
||||
for (i = tokenToCompare.length - 1; i >= 0; i = i - 1) {
|
||||
diff |= tokenToCompare.charCodeAt(i) ^ generatedToken.charCodeAt(i);
|
||||
}
|
||||
|
||||
return diff === 0;
|
||||
}
|
||||
};
|
108
core/test/unit/utils/tokens_spec.js
Normal file
108
core/test/unit/utils/tokens_spec.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
var uuid = require('node-uuid'),
|
||||
should = require('should'),
|
||||
utils = require('../../../server/utils');
|
||||
|
||||
should.equal(true, true);
|
||||
|
||||
describe('Utils: tokens', function () {
|
||||
it('generate', function () {
|
||||
var expires = Date.now() + 60 * 1000,
|
||||
dbHash = uuid.v4(), token;
|
||||
|
||||
token = utils.tokens.resetToken.generateHash({
|
||||
email: 'test1@ghost.org',
|
||||
expires: expires,
|
||||
dbHash: dbHash
|
||||
});
|
||||
|
||||
should.exist(token);
|
||||
token.length.should.be.above(0);
|
||||
});
|
||||
|
||||
it('compare: success', function () {
|
||||
var expires = Date.now() + 60 * 1000,
|
||||
dbHash = uuid.v4(), token, tokenIsCorrect;
|
||||
|
||||
token = utils.tokens.resetToken.generateHash({
|
||||
email: 'test1@ghost.org',
|
||||
expires: expires,
|
||||
password: '12345678',
|
||||
dbHash: dbHash
|
||||
});
|
||||
|
||||
tokenIsCorrect = utils.tokens.resetToken.compare({
|
||||
token: token,
|
||||
dbHash: dbHash,
|
||||
password: '12345678'
|
||||
});
|
||||
|
||||
tokenIsCorrect.should.eql(true);
|
||||
});
|
||||
|
||||
it('compare: error', function () {
|
||||
var expires = Date.now() + 60 * 1000,
|
||||
dbHash = uuid.v4(), token, tokenIsCorrect;
|
||||
|
||||
token = utils.tokens.resetToken.generateHash({
|
||||
email: 'test1@ghost.org',
|
||||
expires: expires,
|
||||
password: '12345678',
|
||||
dbHash: dbHash
|
||||
});
|
||||
|
||||
tokenIsCorrect = utils.tokens.resetToken.compare({
|
||||
token: token,
|
||||
dbHash: dbHash,
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
tokenIsCorrect.should.eql(false);
|
||||
});
|
||||
|
||||
it('extract', function () {
|
||||
var expires = Date.now() + 60 * 1000,
|
||||
dbHash = uuid.v4(), token, parts, email = 'test1@ghost.org';
|
||||
|
||||
token = utils.tokens.resetToken.generateHash({
|
||||
email: email,
|
||||
expires: expires,
|
||||
password: '12345678',
|
||||
dbHash: dbHash
|
||||
});
|
||||
|
||||
parts = utils.tokens.resetToken.extract({
|
||||
token: token
|
||||
});
|
||||
|
||||
parts.email.should.eql(email);
|
||||
parts.expires.should.eql(expires);
|
||||
should.not.exist(parts.password);
|
||||
should.not.exist(parts.dbHash);
|
||||
});
|
||||
|
||||
it('can validate an URI encoded reset token', function () {
|
||||
var expires = Date.now() + 60 * 1000,
|
||||
dbHash = uuid.v4(), token, tokenIsCorrect;
|
||||
|
||||
token = utils.tokens.resetToken.generateHash({
|
||||
email: 'test1@ghost.org',
|
||||
expires: expires,
|
||||
password: '12345678',
|
||||
dbHash: dbHash
|
||||
});
|
||||
|
||||
token = utils.encodeBase64URLsafe(token);
|
||||
token = encodeURIComponent(token);
|
||||
token = decodeURIComponent(token);
|
||||
token = utils.decodeBase64URLsafe(token);
|
||||
|
||||
tokenIsCorrect = utils.tokens.resetToken.compare({
|
||||
token: token,
|
||||
dbHash: dbHash,
|
||||
password: '12345678'
|
||||
});
|
||||
|
||||
tokenIsCorrect.should.eql(true);
|
||||
});
|
||||
});
|
||||
|
Loading…
Add table
Reference in a new issue