🐛 Added multiple use grace period to tokens (#12519)
closes https://github.com/TryGhost/Ghost/issues/12347 This change allows a token to be used multiple times for the first 10 seconds after its initial use, this will stop dynamic link checking software from invaliding magic links.
This commit is contained in:
parent
a0303a246e
commit
7fdddf34b3
@ -1,5 +1,6 @@
|
||||
const ghostBookshelf = require('./base');
|
||||
const crypto = require('crypto');
|
||||
const logging = require('../../shared/logging');
|
||||
|
||||
const SingleUseToken = ghostBookshelf.Model.extend({
|
||||
tableName: 'tokens',
|
||||
@ -16,19 +17,23 @@ const SingleUseToken = ghostBookshelf.Model.extend({
|
||||
}
|
||||
}, {
|
||||
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) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await this.destroy(Object.assign({
|
||||
destroyBy: {
|
||||
id: model.id
|
||||
}
|
||||
}, unfilteredOptions));
|
||||
}, {
|
||||
...unfilteredOptions,
|
||||
transacting: null
|
||||
}));
|
||||
} catch (err) {
|
||||
logging.error(err);
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
return model;
|
||||
|
@ -1,30 +0,0 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
41
test/unit/models/single-use-token_spec.js
Normal file
41
test/unit/models/single-use-token_spec.js
Normal file
@ -0,0 +1,41 @@
|
||||
const models = require('../../../core/server/models');
|
||||
const should = require('should');
|
||||
const sinon = require('sinon');
|
||||
|
||||
let clock;
|
||||
let sandbox;
|
||||
|
||||
describe('Unit: models/single-use-token', function () {
|
||||
before(function () {
|
||||
models.init();
|
||||
sandbox = sinon.createSandbox();
|
||||
clock = sandbox.useFakeTimers();
|
||||
});
|
||||
|
||||
after(function () {
|
||||
clock.restore();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('fn: findOne', function () {
|
||||
it('Calls destroy after the grace period', async function () {
|
||||
const data = {};
|
||||
const options = {};
|
||||
const fakeModel = {
|
||||
id: 'fake_id'
|
||||
};
|
||||
|
||||
const findOneSuperStub = sandbox.stub(models.Base.Model, 'findOne').resolves(fakeModel);
|
||||
const destroyStub = sandbox.stub(models.SingleUseToken, 'destroy').resolves();
|
||||
|
||||
await models.SingleUseToken.findOne(data, options);
|
||||
|
||||
should.ok(findOneSuperStub.calledWith(data, options), 'super.findOne was called');
|
||||
|
||||
clock.tick(10000);
|
||||
|
||||
should.ok(destroyStub.called, 'destroy was called after 10 seconds');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user