Merge pull request #6956 from ErisDS/version-mismatch

Version matching middleware
This commit is contained in:
Sebastian Gierlinger 2016-06-09 19:23:22 +02:00
commit 6826a6516b
8 changed files with 125 additions and 1 deletions

View File

@ -18,6 +18,7 @@ var _ = require('lodash'),
DataImportError = require('./data-import-error'),
TooManyRequestsError = require('./too-many-requests-error'),
TokenRevocationError = require('./token-revocation-error'),
VersionMismatchError = require('./version-mismatch-error'),
i18n = require('../i18n'),
config,
errors,
@ -445,3 +446,4 @@ module.exports.DataImportError = DataImportError;
module.exports.MethodNotAllowedError = MethodNotAllowedError;
module.exports.TooManyRequestsError = TooManyRequestsError;
module.exports.TokenRevocationError = TokenRevocationError;
module.exports.VersionMismatchError = VersionMismatchError;

View File

@ -0,0 +1,14 @@
// # Version mismatch error
// Custom error class with status code and type prefilled.
function VersionMismatchError(message) {
this.message = message;
this.stack = new Error().stack;
this.statusCode = 400;
this.errorType = this.name;
}
VersionMismatchError.prototype = Object.create(Error.prototype);
VersionMismatchError.prototype.name = 'VersionMismatchError';
module.exports = VersionMismatchError;

View File

@ -0,0 +1,20 @@
var errors = require('../../errors'),
i18n = require('../../i18n');
function checkVersionMatch(req, res, next) {
var requestVersion = req.get('X-Ghost-Version'),
currentVersion = res.locals.safeVersion;
if (requestVersion && requestVersion !== currentVersion) {
return next(new errors.VersionMismatchError(
i18n.t(
'errors.middleware.api.versionMismatch',
{requestVersion: requestVersion, currentVersion: currentVersion}
)
));
}
next();
}
module.exports = checkVersionMatch;

View File

@ -27,6 +27,7 @@ var bodyParser = require('body-parser'),
staticTheme = require('./static-theme'),
themeHandler = require('./theme-handler'),
uncapitalise = require('./uncapitalise'),
versionMatch = require('./api/version-match'),
cors = require('./cors'),
netjet = require('netjet'),
labs = require('./labs'),
@ -50,7 +51,8 @@ middleware = {
requiresAuthorizedUserPublicAPI: auth.requiresAuthorizedUserPublicAPI,
errorHandler: errors.handleAPIError,
cors: cors,
labs: labs
labs: labs,
versionMatch: versionMatch
}
};

View File

@ -23,6 +23,10 @@ apiRoutes = function apiRoutes(middleware) {
// alias delete with del
router.del = router.delete;
// Check version matches for API requests, depends on res.locals.safeVersion being set
// Therefore must come after themeHandler.ghostLocals, for now
router.use(middleware.api.versionMatch);
// ## CORS pre-flight check
router.options('*', middleware.api.cors);

View File

@ -62,6 +62,9 @@
}
},
"middleware": {
"api": {
"versionMismatch": "Request for version {requestVersion} does not match current version {currentVersion}."
},
"auth": {
"clientAuthenticationFailed": "Client Authentication Failed",
"clientCredentialsNotProvided": "Client credentials were not provided",

View File

@ -235,4 +235,27 @@ describe('Public API', function () {
done();
});
});
it('throws version mismatch error when request includes a version', function (done) {
request.get(testUtils.API.getApiQuery('posts/?client_id=ghost-admin&client_secret=not_available'))
.set('Origin', testUtils.API.getURL())
.set('X-Ghost-Version', '0.3')
.expect('Content-Type', /json/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(400)
.end(function (err, res) {
if (err) {
return done(err);
}
var jsonResponse = res.body;
should.exist(jsonResponse);
should.exist(jsonResponse.errors);
testUtils.API.checkResponseValue(jsonResponse.errors[0], ['message', 'errorType']);
jsonResponse.errors[0].errorType.should.eql('VersionMismatchError');
done();
});
});
});

View File

@ -0,0 +1,56 @@
/*globals describe, beforeEach, afterEach, it*/
var should = require('should'),
sinon = require('sinon'),
versionMatch = require('../../../../server/middleware/api/version-match'),
sandbox = sinon.sandbox.create();
// To stop jshint complaining
should.equal(true, true);
describe('Version Mismatch', function () {
var req, res, getStub, nextStub;
afterEach(function () {
sandbox.restore();
});
beforeEach(function () {
getStub = sandbox.stub();
nextStub = sandbox.stub();
req = {
get: getStub
};
res = {
locals: {
safeVersion: '0.7'
}
};
});
it('should call next if request does not include a version', function () {
versionMatch(req, res, nextStub);
nextStub.calledOnce.should.be.true();
nextStub.firstCall.args.should.be.empty();
});
it('should call next if versions match', function () {
getStub.returns('0.7');
versionMatch(req, res, nextStub);
nextStub.calledOnce.should.be.true();
nextStub.firstCall.args.should.be.empty();
});
it('should throw VersionMismatchError if request includes incorrect version', function () {
getStub.returns('0.6');
versionMatch(req, res, nextStub);
nextStub.calledOnce.should.be.true();
nextStub.firstCall.args.should.have.lengthOf(1);
nextStub.firstCall.args[0].should.have.property('errorType', 'VersionMismatchError');
nextStub.firstCall.args[0].should.have.property('statusCode', 400);
});
});