mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Added SingleUseToken model (#12215)
no-issue This is a model for the tokens table, which handles the single use aspect by customising the `findOne` method to automatically destroy the model after reading from it
This commit is contained in:
parent
f01f088066
commit
812e4b682f
3 changed files with 77 additions and 1 deletions
|
@ -35,7 +35,8 @@ const models = [
|
|||
'email',
|
||||
'email-batch',
|
||||
'email-recipient',
|
||||
'label'
|
||||
'label',
|
||||
'single-use-token'
|
||||
];
|
||||
|
||||
function init() {
|
||||
|
|
45
core/server/models/single-use-token.js
Normal file
45
core/server/models/single-use-token.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
const ghostBookshelf = require('./base');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const SingleUseToken = ghostBookshelf.Model.extend({
|
||||
tableName: 'tokens',
|
||||
|
||||
defaults() {
|
||||
return {
|
||||
token: crypto
|
||||
.randomBytes(192 / 8)
|
||||
.toString('base64')
|
||||
// base64url encoding means the tokens are URL safe
|
||||
.replace('+', '-')
|
||||
.replace('/', '_')
|
||||
};
|
||||
}
|
||||
}, {
|
||||
async findOne(data, unfilteredOptions = {}) {
|
||||
if (!unfilteredOptions.transacting) {
|
||||
return ghostBookshelf.transaction((transacting) => {
|
||||
return this.findOne(data, Object.assign({transacting}, unfilteredOptions));
|
||||
});
|
||||
}
|
||||
const model = await ghostBookshelf.Model.findOne.call(this, data, unfilteredOptions);
|
||||
|
||||
if (model) {
|
||||
await this.destroy(Object.assign({
|
||||
destroyBy: {
|
||||
id: model.id
|
||||
}
|
||||
}, unfilteredOptions));
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
});
|
||||
|
||||
const SingleUseTokens = ghostBookshelf.Collection.extend({
|
||||
model: SingleUseToken
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
SingleUseToken: ghostBookshelf.model('SingleUseToken', SingleUseToken),
|
||||
SingleUseTokens: ghostBookshelf.collection('SingleUseTokens', SingleUseTokens)
|
||||
};
|
30
test/regression/models/model_single_use_token_spec.js
Normal file
30
test/regression/models/model_single_use_token_spec.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const models = require('../../../core/server/models');
|
||||
const should = require('should');
|
||||
|
||||
describe('Regression: models/single-use-token', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
});
|
||||
|
||||
describe('findOne', function () {
|
||||
it('Does not allow the same token to be read twice', async function () {
|
||||
const insertedToken = await models.SingleUseToken.add({
|
||||
data: 'some_data'
|
||||
}, {});
|
||||
|
||||
const tokenFirstRead = await models.SingleUseToken.findOne({
|
||||
token: insertedToken.get('token')
|
||||
});
|
||||
|
||||
should.exist(tokenFirstRead);
|
||||
should.equal(tokenFirstRead.id, insertedToken.id);
|
||||
|
||||
const tokenSecondRead = await models.SingleUseToken.findOne({
|
||||
token: insertedToken.get('token')
|
||||
});
|
||||
|
||||
should.not.exist(tokenSecondRead);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Add table
Reference in a new issue