Added easy way to enable admin api key authentication

refs #9865

- small refactoring to make both session and admin api key handling similar
- admin api key authentication is still disabled, but easy to enable
- added proof test how to authenticate using admin api keys
This commit is contained in:
kirrg001 2019-01-18 17:03:03 +01:00
parent 4f7783939d
commit 1126997244
6 changed files with 632 additions and 662 deletions

View File

@ -35,9 +35,10 @@ const _extractTokenFromHeader = function extractTokenFromHeader(header) {
* https://tools.ietf.org/html/rfc7519#section-4.1.3
*/
const authenticate = (req, res, next) => {
// we don't have an Authorization header so allow fallthrough to other
// CASE: we don't have an Authorization header so allow fallthrough to other
// auth middleware or final "ensure authenticated" check
if (!req.headers || !req.headers.authorization) {
req.api_key = null;
return next();
}

View File

@ -102,9 +102,10 @@ const authenticate = {
)(req, res, next);
},
// ### v2 API auth middleware
authenticateAdminApi: [session.safeGetSession, session.getUser],
authenticateAdminApiKey: apiKeyAuth.admin.authenticateAdminApiKey,
// @NOTE: authentication for admin api keys is disabled
// authenticateAdminApi: [apiKeyAuth.admin.authenticate, session.authenticate],
authenticateAdminApi: [session.authenticate],
authenticateContentApi: [apiKeyAuth.content.authenticateContentApiKey, members.authenticateMembersToken]
};

View File

@ -1,16 +1,5 @@
module.exports = {
get getSession() {
return require('./middleware').getSession;
},
get cookieCsrfProtection() {
return require('./middleware').cookieCsrfProtection;
},
get safeGetSession() {
return require('./middleware').safeGetSession;
},
// @TODO: expose files/units and not functions of units
get createSession() {
return require('./middleware').createSession;
},
@ -19,7 +8,7 @@ module.exports = {
return require('./middleware').destroySession;
},
get getUser() {
return require('./middleware').getUser;
get authenticate() {
return require('./middleware').authenticate;
}
};

View File

@ -1,10 +1,10 @@
const url = require('url');
const session = require('express-session');
const common = require('../../../lib/common');
const constants = require('../../../lib/constants');
const config = require('../../../config');
const settingsCache = require('../../settings/cache');
const models = require('../../../models');
const session = require('express-session');
const SessionStore = require('./store');
const urlService = require('../../url');
@ -76,46 +76,65 @@ const destroySession = (req, res, next) => {
});
};
const getUser = (req, res, next) => {
if (!req.session || !req.session.user_id) {
req.user = null;
return next();
}
models.User.findOne({id: req.session.user_id})
.then((user) => {
req.user = user;
next();
}).catch(() => {
req.user = null;
next();
});
};
const cookieCsrfProtection = (req, res, next) => {
const cookieCsrfProtection = (req) => {
// If there is no origin on the session object it means this is a *new*
// session, that hasn't been initialised yet. So we don't need CSRF protection
if (!req.session.origin) {
return next();
return;
}
const origin = getOrigin(req);
if (req.session.origin !== origin) {
return next(new common.errors.BadRequestError({
throw new common.errors.BadRequestError({
message: common.i18n.t('errors.middleware.auth.mismatchedOrigin', {
expected: req.session.origin,
actual: origin
})
}));
});
}
};
const authenticate = (req, res, next) => {
// CASE: we don't have a cookie header so allow fallthrough to other
// auth middleware or final "ensure authenticated" check
if (!req.headers || !req.headers.cookie) {
req.user = null;
return next();
}
return next();
getSession(req, res, function (err) {
if (err) {
return next(err);
}
try {
cookieCsrfProtection(req);
} catch (err) {
return next(err);
}
if (!req.session || !req.session.user_id) {
req.user = null;
return next();
}
models.User.findOne({id: req.session.user_id})
.then((user) => {
req.user = user;
next();
})
.catch(() => {
req.user = null;
next();
});
});
};
// @TODO: this interface exposes private functions
module.exports = exports = {
getSession,
cookieCsrfProtection,
safeGetSession: [getSession, cookieCsrfProtection],
createSession,
destroySession,
getUser
cookieCsrfProtection,
authenticate
};

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,4 @@
const sessionService = require('../../../../../server/services/auth/session');
const SessionStore = require('../../../../../server/services/auth/session/store');
const config = require('../../../../../server/config');
const sessionMiddleware = require('../../../../../server/services/auth/session/middleware');
const models = require('../../../../../server/models');
const sinon = require('sinon');
const should = require('should');
@ -44,7 +42,7 @@ describe('Session Service', function () {
.withArgs('origin').returns('')
.withArgs('referrer').returns('');
sessionService.createSession(req, fakeRes(), function next(err) {
sessionMiddleware.createSession(req, fakeRes(), function next(err) {
should.equal(err instanceof BadRequestError, true);
done();
});
@ -68,7 +66,7 @@ describe('Session Service', function () {
done();
});
sessionService.createSession(req, res);
sessionMiddleware.createSession(req, res);
});
it('sets req.session.user_id,origin,user_agent,ip and calls sendStatus with 201 if the check succeeds', function (done) {
@ -92,7 +90,7 @@ describe('Session Service', function () {
done();
});
sessionService.createSession(req, res);
sessionMiddleware.createSession(req, res);
});
});
@ -102,7 +100,7 @@ describe('Session Service', function () {
const res = fakeRes();
const destroyStub = sandbox.stub(req.session, 'destroy');
sessionService.destroySession(req, res);
sessionMiddleware.destroySession(req, res);
should.equal(destroyStub.callCount, 1);
});
@ -115,7 +113,7 @@ describe('Session Service', function () {
fn(new Error('oops'));
});
sessionService.destroySession(req, res, function next(err) {
sessionMiddleware.destroySession(req, res, function next(err) {
should.equal(err instanceof InternalServerError, true);
done();
});
@ -134,71 +132,7 @@ describe('Session Service', function () {
done();
});
sessionService.destroySession(req, res);
});
});
describe('getUser', function () {
it('sets req.user to null and calls next if there is no session', function (done) {
const req = fakeReq();
const res = fakeRes();
delete req.session;
sessionService.getUser(req, res, function next() {
should.equal(req.user, null);
done();
});
});
it('sets req.user to null and calls next if there is no session', function (done) {
const req = fakeReq();
const res = fakeRes();
sessionService.getUser(req, res, function next() {
should.equal(req.user, null);
done();
});
});
it('calls User.findOne with id set to req.session.user_id', function (done) {
const req = fakeReq();
const res = fakeRes();
sandbox.stub(models.User, 'findOne')
.callsFake(function (opts) {
should.equal(opts.id, 23);
done();
});
req.session.user_id = 23;
sessionService.getUser(req, res);
});
it('sets req.user to null and calls next if the user is not found', function (done) {
const req = fakeReq();
const res = fakeRes();
sandbox.stub(models.User, 'findOne')
.rejects();
req.session.user_id = 23;
sessionService.getUser(req, res, function next() {
should.equal(req.user, null);
done();
});
});
it('calls next after settign req.user to the found user', function (done) {
const req = fakeReq();
const res = fakeRes();
const user = models.User.forge({id: 23});
sandbox.stub(models.User, 'findOne')
.resolves(user);
req.session.user_id = 23;
sessionService.getUser(req, res, function next() {
should.equal(req.user, user);
done();
});
sessionMiddleware.destroySession(req, res);
});
});
@ -207,10 +141,8 @@ describe('Session Service', function () {
const req = fakeReq();
const res = fakeRes();
sessionService.cookieCsrfProtection(req, res, function next(err) {
should.not.exist(err);
done();
});
sessionMiddleware.cookieCsrfProtection(req);
done();
});
it('calls next if req origin matches the session origin', function (done) {
@ -220,10 +152,8 @@ describe('Session Service', function () {
.withArgs('origin').returns('http://host.tld');
req.session.origin = 'http://host.tld';
sessionService.cookieCsrfProtection(req, res, function next(err) {
should.not.exist(err);
done();
});
sessionMiddleware.cookieCsrfProtection(req);
done();
});
it('calls next with BadRequestError if the origin of req does not match the session', function (done) {
@ -233,19 +163,12 @@ describe('Session Service', function () {
.withArgs('origin').returns('http://host.tld');
req.session.origin = 'http://different-host.tld';
sessionService.cookieCsrfProtection(req, res, function next(err) {
try {
sessionMiddleware.cookieCsrfProtection(req);
} catch (err) {
should.equal(err instanceof BadRequestError, true);
done();
});
});
});
describe('safeGetSession', function () {
it('is an array of getSession and cookieCsrfProtection', function () {
should.deepEqual(sessionService.safeGetSession, [
sessionService.getSession,
sessionService.cookieCsrfProtection
]);
}
});
});
});