0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-27 22:49:56 -05:00
ghost/test/unit/api/canary/session_spec.js
Fabien O'Carroll a701ee7023
Added support for token session to /ghost (#11709)
no-issue

* Added default for getting origin of request

This function is used to attach the origin of the request to the
session, and later check that requests using the session are coming from
the same origin. This protects us against CSRF attacks as requests in
the browser MUST originate from the same origin on which the user
logged in.

Previously, when we could not determine the origin we would return
null, as a "safety" net.

This updates the function to use a secure and sensible default - which
is the origin of the Ghost-Admin application, and if that's not set -
the origin of the Ghost application.

This will make dealing with magic links simpler as you can not always
guaruntee the existence of these headers when visiting via a hyperlink

* Removed init fns and getters from session service

This simplifies the code here, making it easier to read and maintain

* Moved express-session initialisation to own file

This is complex enough that it deserves its own module

* Added createSessionFromToken to session service

* Wired up the createSessionFromToken middleware
2020-04-06 11:49:14 +02:00

144 lines
5.4 KiB
JavaScript

const should = require('should');
const sinon = require('sinon');
const models = require('../../../../core/server/models');
const {UnauthorizedError} = require('../../../../core/server/lib/common/errors');
const sessionController = require('../../../../core/server/api/canary/session');
const sessionServiceMiddleware = require('../../../../core/server/services/auth/session');
describe('Session controller', function () {
before(function () {
models.init();
});
afterEach(function () {
sinon.restore();
});
it('exports an add method', function () {
should.equal(typeof sessionController.add, 'function');
});
it('exports an delete method', function () {
should.equal(typeof sessionController.delete, 'function');
});
describe('#add', function () {
it('throws an UnauthorizedError if the object is missing a username and password', function () {
return sessionController.add({}).then(() => {
should.fail('session.add did not throw');
},(err) => {
should.equal(err instanceof UnauthorizedError, true);
});
});
it('it checks the username and password and throws UnauthorizedError if it fails', function () {
const userCheckStub = sinon.stub(models.User, 'check')
.rejects(new Error());
return sessionController.add({
username: 'freddy@vodafone.com',
password: 'qu33nRul35'
}, {}).then(() => {
should.fail('session.add did not throw');
},(err) => {
should.equal(err instanceof UnauthorizedError, true);
});
});
it('it returns a function that calls req.brute.reset, sets req.user and calls createSession if the check works', function () {
const fakeReq = {
brute: {
reset: sinon.stub().callsArg(0)
}
};
const fakeRes = {};
const fakeNext = () => {};
const fakeUser = models.User.forge({});
sinon.stub(models.User, 'check')
.resolves(fakeUser);
const createSessionStub = sinon.stub(sessionServiceMiddleware, 'createSession');
return sessionController.add({data: {
username: 'freddy@vodafone.com',
password: 'qu33nRul35'
}}).then((fn) => {
fn(fakeReq, fakeRes, fakeNext);
}).then(function () {
should.equal(fakeReq.brute.reset.callCount, 1);
const createSessionStubCall = createSessionStub.getCall(0);
should.equal(fakeReq.user, fakeUser);
should.equal(createSessionStubCall.args[0], fakeReq);
should.equal(createSessionStubCall.args[1], fakeRes);
should.equal(createSessionStubCall.args[2], fakeNext);
});
});
it('it returns a function that calls req.brute.reset and calls next if reset errors', function () {
const resetError = new Error();
const fakeReq = {
brute: {
reset: sinon.stub().callsArgWith(0, resetError)
}
};
const fakeRes = {};
const fakeNext = sinon.stub();
const fakeUser = models.User.forge({});
sinon.stub(models.User, 'check')
.resolves(fakeUser);
const createSessionStub = sinon.stub(sessionServiceMiddleware, 'createSession');
return sessionController.add({data: {
username: 'freddy@vodafone.com',
password: 'qu33nRul35'
}}).then((fn) => {
fn(fakeReq, fakeRes, fakeNext);
}).then(function () {
should.equal(fakeReq.brute.reset.callCount, 1);
should.equal(fakeNext.callCount, 1);
should.equal(fakeNext.args[0][0], resetError);
});
});
});
describe('#delete', function () {
it('returns a function that calls destroySession', function () {
const fakeReq = {};
const fakeRes = {};
const fakeNext = () => {};
const destroySessionStub = sinon.stub(sessionServiceMiddleware, 'destroySession');
return sessionController.delete().then((fn) => {
fn(fakeReq, fakeRes, fakeNext);
}).then(function () {
const destroySessionStubCall = destroySessionStub.getCall(0);
should.equal(destroySessionStubCall.args[0], fakeReq);
should.equal(destroySessionStubCall.args[1], fakeRes);
should.equal(destroySessionStubCall.args[2], fakeNext);
});
});
});
describe('#get', function () {
it('returns the result of User.findOne', function () {
const findOneReturnVal = new Promise(() => {});
const findOneStub = sinon.stub(models.User, 'findOne')
.returns(findOneReturnVal);
const result = sessionController.read({
options: {
context: {
user: 108
}
}
});
should.equal(result, findOneReturnVal);
should.deepEqual(findOneStub.args[0][0], {
id: 108
});
});
});
});