Make session expiry less arsey
closes #2171 - added authentication middleware - removed authentication from routes - moved authentication before CSRF validation - moved caching rules before authentication - changed/added test
This commit is contained in:
parent
6b82bebd90
commit
d3c641ea31
@ -267,6 +267,14 @@ module.exports = function (server, dbHash) {
|
||||
cookie: cookie
|
||||
}));
|
||||
|
||||
// ### Caching
|
||||
expressServer.use(middleware.cacheControl('public'));
|
||||
expressServer.use('/api/', middleware.cacheControl('private'));
|
||||
expressServer.use('/ghost/', middleware.cacheControl('private'));
|
||||
|
||||
// enable authentication; has to be done before CSRF handling
|
||||
expressServer.use(middleware.authenticate);
|
||||
|
||||
// enable express csrf protection
|
||||
expressServer.use(middleware.conditionalCSRF);
|
||||
|
||||
@ -277,12 +285,6 @@ module.exports = function (server, dbHash) {
|
||||
// Initialise the views
|
||||
expressServer.use(initViews);
|
||||
|
||||
|
||||
// ### Caching
|
||||
expressServer.use(middleware.cacheControl('public'));
|
||||
expressServer.use('/api/', middleware.cacheControl('private'));
|
||||
expressServer.use('/ghost/', middleware.cacheControl('private'));
|
||||
|
||||
// ### Routing
|
||||
expressServer.use(subdir, expressServer.router);
|
||||
|
||||
|
@ -24,6 +24,28 @@ function cacheServer(server) {
|
||||
|
||||
var middleware = {
|
||||
|
||||
// ### Authenticate Middleware
|
||||
// authentication has to be done for /ghost/* routes with
|
||||
// exceptions for signin, signout, signup, forgotten, reset only
|
||||
// api and frontend use different authentication mechanisms atm
|
||||
authenticate: function (req, res, next) {
|
||||
if (res.isAdmin) {
|
||||
if (req.path.indexOf("/ghost/api/") === 0) {
|
||||
return middleware.authAPI(req, res, next);
|
||||
}
|
||||
|
||||
var noAuthNeeded = [
|
||||
'/ghost/signin/', '/ghost/signout/', '/ghost/signup/',
|
||||
'/ghost/forgotten/', '/ghost/reset/'
|
||||
];
|
||||
|
||||
if (noAuthNeeded.indexOf(req.path) < 0) {
|
||||
return middleware.auth(req, res, next);
|
||||
}
|
||||
}
|
||||
next();
|
||||
},
|
||||
|
||||
// ### Auth Middleware
|
||||
// Authenticate a request by redirecting to login if not logged in.
|
||||
// We strip /ghost/ out of the redirect parameter for neatness
|
||||
|
@ -35,23 +35,24 @@ module.exports = function (server) {
|
||||
server.post('/ghost/reset/:token', admin.resetPassword);
|
||||
server.post('/ghost/signin/', admin.auth);
|
||||
server.post('/ghost/signup/', admin.doRegister);
|
||||
server.post('/ghost/changepw/', middleware.auth, admin.changepw);
|
||||
server.get('/ghost/editor(/:id)/', middleware.auth, admin.editor);
|
||||
server.get('/ghost/editor/', middleware.auth, admin.editor);
|
||||
server.get('/ghost/content/', middleware.auth, admin.content);
|
||||
server.get('/ghost/settings*', middleware.auth, admin.settings);
|
||||
server.get('/ghost/debug/', middleware.auth, admin.debug.index);
|
||||
|
||||
server.post('/ghost/upload/', middleware.auth, middleware.busboy, admin.uploader);
|
||||
server.post('/ghost/changepw/', admin.changepw);
|
||||
server.get('/ghost/editor(/:id)/', admin.editor);
|
||||
server.get('/ghost/editor/', admin.editor);
|
||||
server.get('/ghost/content/', admin.content);
|
||||
server.get('/ghost/settings*', admin.settings);
|
||||
server.get('/ghost/debug/', admin.debug.index);
|
||||
|
||||
server.post('/ghost/upload/', middleware.busboy, admin.uploader);
|
||||
|
||||
// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
|
||||
server.get(/\/((ghost-admin|admin|wp-admin|dashboard|signin)\/?)$/, function (req, res) {
|
||||
/*jslint unparam:true*/
|
||||
res.redirect(subdir + '/ghost/');
|
||||
});
|
||||
server.get(/\/(ghost$\/?)/, middleware.auth, function (req, res) {
|
||||
server.get(/\/(ghost$\/?)/, function (req, res) {
|
||||
/*jslint unparam:true*/
|
||||
res.redirect(subdir + '/ghost/');
|
||||
});
|
||||
server.get('/ghost/', middleware.redirectToSignup, middleware.auth, admin.index);
|
||||
server.get('/ghost/', admin.index);
|
||||
};
|
@ -4,27 +4,27 @@ var middleware = require('../middleware').middleware,
|
||||
module.exports = function (server) {
|
||||
// ### API routes
|
||||
// #### Posts
|
||||
server.get('/ghost/api/v0.1/posts', middleware.authAPI, api.requestHandler(api.posts.browse));
|
||||
server.post('/ghost/api/v0.1/posts', middleware.authAPI, api.requestHandler(api.posts.add));
|
||||
server.get('/ghost/api/v0.1/posts/:id', middleware.authAPI, api.requestHandler(api.posts.read));
|
||||
server.put('/ghost/api/v0.1/posts/:id', middleware.authAPI, api.requestHandler(api.posts.edit));
|
||||
server.del('/ghost/api/v0.1/posts/:id', middleware.authAPI, api.requestHandler(api.posts.destroy));
|
||||
server.get('/ghost/api/v0.1/posts', api.requestHandler(api.posts.browse));
|
||||
server.post('/ghost/api/v0.1/posts', api.requestHandler(api.posts.add));
|
||||
server.get('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.read));
|
||||
server.put('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.edit));
|
||||
server.del('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.destroy));
|
||||
server.get('/ghost/api/v0.1/posts/getSlug/:title', middleware.authAPI, api.requestHandler(api.posts.getSlug));
|
||||
// #### Settings
|
||||
server.get('/ghost/api/v0.1/settings/', middleware.authAPI, api.requestHandler(api.settings.browse));
|
||||
server.get('/ghost/api/v0.1/settings/:key/', middleware.authAPI, api.requestHandler(api.settings.read));
|
||||
server.put('/ghost/api/v0.1/settings/', middleware.authAPI, api.requestHandler(api.settings.edit));
|
||||
server.get('/ghost/api/v0.1/settings/', api.requestHandler(api.settings.browse));
|
||||
server.get('/ghost/api/v0.1/settings/:key/', api.requestHandler(api.settings.read));
|
||||
server.put('/ghost/api/v0.1/settings/', api.requestHandler(api.settings.edit));
|
||||
// #### Users
|
||||
server.get('/ghost/api/v0.1/users/', middleware.authAPI, api.requestHandler(api.users.browse));
|
||||
server.get('/ghost/api/v0.1/users/:id/', middleware.authAPI, api.requestHandler(api.users.read));
|
||||
server.put('/ghost/api/v0.1/users/:id/', middleware.authAPI, api.requestHandler(api.users.edit));
|
||||
server.get('/ghost/api/v0.1/users/', api.requestHandler(api.users.browse));
|
||||
server.get('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.read));
|
||||
server.put('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.edit));
|
||||
// #### Tags
|
||||
server.get('/ghost/api/v0.1/tags/', middleware.authAPI, api.requestHandler(api.tags.all));
|
||||
server.get('/ghost/api/v0.1/tags/', api.requestHandler(api.tags.all));
|
||||
// #### Notifications
|
||||
server.del('/ghost/api/v0.1/notifications/:id', middleware.authAPI, api.requestHandler(api.notifications.destroy));
|
||||
server.post('/ghost/api/v0.1/notifications/', middleware.authAPI, api.requestHandler(api.notifications.add));
|
||||
server.del('/ghost/api/v0.1/notifications/:id', api.requestHandler(api.notifications.destroy));
|
||||
server.post('/ghost/api/v0.1/notifications/', api.requestHandler(api.notifications.add));
|
||||
// #### Import/Export
|
||||
server.get('/ghost/api/v0.1/db/', middleware.auth, api.db.exportContent);
|
||||
server.post('/ghost/api/v0.1/db/', middleware.authAPI, middleware.busboy, api.requestHandler(api.db.importContent));
|
||||
server.del('/ghost/api/v0.1/db/', middleware.authAPI, api.requestHandler(api.db.deleteAllContent));
|
||||
server.get('/ghost/api/v0.1/db/', api.db.exportContent);
|
||||
server.post('/ghost/api/v0.1/db/', middleware.busboy, api.requestHandler(api.db.importContent));
|
||||
server.del('/ghost/api/v0.1/db/', api.requestHandler(api.db.deleteAllContent));
|
||||
};
|
@ -41,7 +41,7 @@ CasperTest.begin("Ghost admin will load login page", 2, function suite(test) {
|
||||
CasperTest.begin('Redirects login to signin', 2, function suite(test) {
|
||||
casper.start(url + 'ghost/login/', function testRedirect(response) {
|
||||
test.assertEqual(response.status, 200, 'Response status should be 200.');
|
||||
test.assertUrlMatch(/ghost\/signin\/$/, 'Should be redirected to /signin/.');
|
||||
test.assertUrlMatch(/ghost\/signin\//, 'Should be redirected to /signin/.');
|
||||
});
|
||||
}, true);
|
||||
|
||||
|
20
core/test/functional/api/error_test.js
Normal file
20
core/test/functional/api/error_test.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*globals describe, before, after, beforeEach, afterEach, it */
|
||||
var testUtils = require('../../utils'),
|
||||
should = require('should'),
|
||||
_ = require('lodash'),
|
||||
request = require('request');
|
||||
|
||||
describe('Unauthorized', function () {
|
||||
|
||||
it('can\'t retrieve posts', function (done) {
|
||||
request.get(testUtils.API.getApiURL('posts/'), function (error, response, body) {
|
||||
response.should.have.status(401);
|
||||
should.not.exist(response.headers['x-cache-invalidate']);
|
||||
response.should.be.json;
|
||||
var jsonResponse = JSON.parse(body);
|
||||
jsonResponse.should.exist;
|
||||
testUtils.API.checkResponseValue(jsonResponse, ['error']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -247,6 +247,22 @@ describe('Post API', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit a post with invalid CSRF token', function (done) {
|
||||
request.get(testUtils.API.getApiURL('posts/1/'), function (error, response, body) {
|
||||
var jsonResponse = JSON.parse(body),
|
||||
changedValue = 'My new Title';
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.title = changedValue;
|
||||
|
||||
request.put({uri: testUtils.API.getApiURL('posts/1/'),
|
||||
headers: {'X-CSRF-Token': 'invalid-token'},
|
||||
json: jsonResponse}, function (error, response, putBody) {
|
||||
response.should.have.status(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ## delete
|
||||
|
@ -100,6 +100,22 @@ describe('Settings API', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit settings with invalid CSRF token', function (done) {
|
||||
request.get(testUtils.API.getApiURL('settings'), function (error, response, body) {
|
||||
var jsonResponse = JSON.parse(body),
|
||||
changedValue = 'Ghost changed';
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.title = changedValue;
|
||||
|
||||
request.put({uri: testUtils.API.getApiURL('settings/'),
|
||||
headers: {'X-CSRF-Token': 'invalid-token'},
|
||||
json: jsonResponse}, function (error, response, putBody) {
|
||||
response.should.have.status(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit non existent setting', function (done) {
|
||||
request.get(testUtils.API.getApiURL('settings'), function (error, response, body) {
|
||||
var jsonResponse = JSON.parse(body),
|
||||
|
@ -100,4 +100,20 @@ describe('User API', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit a user with invalid CSRF token', function (done) {
|
||||
request.get(testUtils.API.getApiURL('users/me/'), function (error, response, body) {
|
||||
var jsonResponse = JSON.parse(body),
|
||||
changedValue = 'joe-bloggs.ghost.org';
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.website = changedValue;
|
||||
|
||||
request.put({uri: testUtils.API.getApiURL('users/me/'),
|
||||
headers: {'X-CSRF-Token': 'invalid-token'},
|
||||
json: jsonResponse}, function (error, response, putBody) {
|
||||
response.should.have.status(403);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -73,9 +73,9 @@ describe('Admin Routing', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect from /ghost to /ghost/signup when no user', function (done) {
|
||||
it('should redirect from /ghost to /ghost/signin when no user', function (done) {
|
||||
request.get('/ghost/')
|
||||
.expect('Location', /ghost\/signup/)
|
||||
.expect('Location', /ghost\/signin/)
|
||||
.expect('Cache-Control', cacheRules['private'])
|
||||
.expect(302)
|
||||
.end(doEnd(done));
|
||||
|
Loading…
Reference in New Issue
Block a user