0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-06 22:40:14 -05:00

🔒 Added admin:redirects config option for disabling admin redirects

no issue

- adds `config:redirects` config option that defaults to `true`
- when set to `false`
  - `/ghost/` will 404 on the front-end when a separate admin url is configured
  - all `{resource}/edit/` URLs on the front-end will 404
This commit is contained in:
Kevin Ansfield 2019-09-12 12:40:12 +01:00
parent 95ea5265d5
commit 7e92b07233
9 changed files with 273 additions and 30 deletions

View file

@ -1,4 +1,5 @@
const debug = require('ghost-ignition').debug('services:routing:taxonomy-router');
const config = require('../../../server/config');
const common = require('../../../server/lib/common');
const ParentRouter = require('./ParentRouter');
const RSSRouter = require('./RSSRouter');
@ -53,7 +54,9 @@ class TaxonomyRouter extends ParentRouter {
this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'page', ':page(\\d+)'), controllers.channel);
// REGISTER: edit redirect to admin client e.g. /tag/:slug/edit
this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'edit'), this._redirectEditOption.bind(this));
if (config.get('admin:redirects')) {
this.mountRoute(urlUtils.urlJoin(this.permalinks.value, 'edit'), this._redirectEditOption.bind(this));
}
common.events.emit('router.created', this);
}

View file

@ -1,8 +1,9 @@
const debug = require('ghost-ignition').debug('services:routing:controllers:entry'),
url = require('url'),
urlService = require('../../../services/url'),
urlUtils = require('../../../../server/lib/url-utils'),
helpers = require('../helpers');
const debug = require('ghost-ignition').debug('services:routing:controllers:entry');
const url = require('url');
const config = require('../../../../server/config');
const urlService = require('../../../services/url');
const urlUtils = require('../../../../server/lib/url-utils');
const helpers = require('../helpers');
/**
* @description Entry controller.
@ -32,6 +33,11 @@ module.exports = function entryController(req, res, next) {
// CASE: last param is of url is /edit, redirect to admin
if (lookup.isEditURL) {
if (!config.get('admin:redirects')) {
debug('is edit url but admin redirects are disabled');
return next();
}
debug('redirect. is edit url');
const resourceType = entry.page ? 'page' : 'post';

View file

@ -1,7 +1,8 @@
const debug = require('ghost-ignition').debug('services:routing:controllers:preview'),
urlService = require('../../url'),
urlUtils = require('../../../../server/lib/url-utils'),
helpers = require('../helpers');
const debug = require('ghost-ignition').debug('services:routing:controllers:preview');
const config = require('../../../../server/config');
const urlService = require('../../url');
const urlUtils = require('../../../../server/lib/url-utils');
const helpers = require('../helpers');
/**
* @description Preview Controller.
@ -31,6 +32,11 @@ module.exports = function previewController(req, res, next) {
}
if (req.params.options && req.params.options.toLowerCase() === 'edit') {
// CASE: last param of the url is /edit but admin redirects are disabled
if (!config.get('admin:redirects')) {
return next();
}
// @TODO: we don't know which resource type it is, because it's a generic preview handler and the
// preview API returns {previews: []}
// @TODO: figure out how to solve better

View file

@ -4,6 +4,9 @@
"host": "127.0.0.1",
"port": 2368
},
"admin": {
"redirects": true
},
"updateCheck": {
"url": "https://updates.ghost.org",
"forceUpdate": false

View file

@ -1,4 +1,5 @@
const express = require('express');
const config = require('../../../config');
const urlUtils = require('../../../lib/url-utils');
const adminRedirect = (path) => {
@ -10,6 +11,10 @@ const adminRedirect = (path) => {
// redirect to /ghost to the admin
module.exports = function adminRedirects() {
const router = express.Router();
router.get(/^\/ghost\/?$/, adminRedirect('/'));
if (config.get('admin:redirects')) {
router.get(/^\/ghost\/?$/, adminRedirect('/'));
}
return router;
};

View file

@ -2,17 +2,18 @@
// As it stands, these tests depend on the database, and as such are integration tests.
// These tests are here to cover the headers sent with requests and high-level redirects that can't be
// tested with the unit tests
const should = require('should'),
supertest = require('supertest'),
sinon = require('sinon'),
moment = require('moment'),
path = require('path'),
testUtils = require('../../utils'),
cheerio = require('cheerio'),
config = require('../../../server/config'),
api = require('../../../server/api'),
settingsCache = require('../../../server/services/settings/cache'),
ghost = testUtils.startGhost;
const should = require('should');
const supertest = require('supertest');
const sinon = require('sinon');
const moment = require('moment');
const path = require('path');
const testUtils = require('../../utils');
const configUtils = require('../../utils/configUtils');
const cheerio = require('cheerio');
const config = require('../../../server/config');
const api = require('../../../server/api');
const settingsCache = require('../../../server/services/settings/cache');
const ghost = testUtils.startGhost;
let request;
@ -284,6 +285,43 @@ describe('Dynamic Routing', function () {
});
});
describe('Edit with admin redirects disabled', function () {
before(function () {
configUtils.set('admin:redirects', false);
return ghost({forceStart: true})
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
});
});
after(function () {
configUtils.restore();
return ghost({forceStart: true})
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
});
});
it('should redirect without slash', function (done) {
request.get('/tag/getting-started/edit')
.expect('Location', '/tag/getting-started/edit/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEnd(done));
});
it('should not redirect to admin', function (done) {
request.get('/tag/getting-started/edit/')
.expect(404)
.expect('Cache-Control', testUtils.cacheRules.private)
.end(doEnd(done));
});
});
describe.skip('Paged', function () {
// Inserting more posts takes a bit longer
this.timeout(20000);
@ -529,6 +567,43 @@ describe('Dynamic Routing', function () {
});
});
describe('Edit with admin redirects disabled', function () {
before(function () {
configUtils.set('admin:redirects', false);
return ghost({forceStart: true})
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
});
});
after(function () {
configUtils.restore();
return ghost({forceStart: true})
.then(function (_ghostServer) {
ghostServer = _ghostServer;
request = supertest.agent(config.get('url'));
});
});
it('should redirect without slash', function (done) {
request.get('/author/ghost-owner/edit')
.expect('Location', '/author/ghost-owner/edit/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEnd(done));
});
it('should not redirect to admin', function (done) {
request.get('/author/ghost-owner/edit/')
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(404)
.end(doEnd(done));
});
});
describe('Paged', function () {
// Add enough posts to trigger pages
before(function (done) {

View file

@ -193,6 +193,41 @@ describe('Frontend Routing', function () {
});
});
describe('Post edit with admin redirects disabled', function () {
before(function () {
configUtils.set('admin:redirects', false);
return ghost({forceStart: true})
.then(function () {
request = supertest.agent(config.get('url'));
});
});
after(function () {
configUtils.restore();
return ghost({forceStart: true})
.then(function () {
request = supertest.agent(config.get('url'));
});
});
it('should redirect without slash', function (done) {
request.get('/welcome/edit')
.expect('Location', '/welcome/edit/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEnd(done));
});
it('should not redirect to editor', function (done) {
request.get('/welcome/edit/')
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(404)
.end(doEnd(done));
});
});
describe('AMP post', function () {
it('should redirect without slash', function (done) {
request.get('/welcome/amp')
@ -357,6 +392,43 @@ describe('Frontend Routing', function () {
});
});
describe('edit with admin redirects disabled', function () {
before(function (done) {
configUtils.set('admin:redirects', false);
ghost({forceStart: true})
.then(function () {
request = supertest.agent(config.get('url'));
addPosts(done);
});
});
after(function (done) {
configUtils.restore();
ghost({forceStart: true})
.then(function () {
request = supertest.agent(config.get('url'));
addPosts(done);
});
});
it('should redirect without slash', function (done) {
request.get('/static-page-test/edit')
.expect('Location', '/static-page-test/edit/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEnd(done));
});
it('should not redirect to editor', function (done) {
request.get('/static-page-test/edit/')
.expect(404)
.expect('Cache-Control', testUtils.cacheRules.private)
.end(doEnd(done));
});
});
describe('amp', function () {
it('should 404 for amp parameter', function (done) {
// NOTE: only post pages are supported so the router doesn't have a way to distinguish if

View file

@ -5397,6 +5397,54 @@ describe('Integration - Web - Site', function () {
});
});
describe('separate admin host w/ admin redirects disabled', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();
testUtils.integrationTesting.defaultMocks(sinon, {amp: true, apps: true});
testUtils.integrationTesting.overrideGhostConfig(configUtils);
configUtils.set('url', 'http://example.com');
configUtils.set('admin:url', 'https://admin.example.com');
configUtils.set('admin:redirects', false);
return testUtils.integrationTesting.initGhost()
.then(function () {
sinon.stub(themeService.getActive(), 'engine').withArgs('ghost-api').returns('v2');
sinon.stub(themeService.getActive(), 'config').withArgs('posts_per_page').returns(2);
app = siteApp({start: true});
return testUtils.integrationTesting.urlService.waitTillFinished();
})
.then(() => {
return appsService.init();
});
});
before(function () {
urlUtils.stubUrlUtilsFromConfig();
});
after(function () {
configUtils.restore();
urlUtils.restore();
sinon.restore();
});
it('does not redirect /ghost/ on configured url', function () {
const req = {
secure: false,
method: 'GET',
url: '/ghost/',
host: 'example.com'
};
return testUtils.mocks.express.invoke(app, req)
.then(function (response) {
response.statusCode.should.eql(404);
});
});
});
describe('same host separate protocol', function () {
before(function () {
testUtils.integrationTesting.urlService.resetGenerators();

View file

@ -1,11 +1,12 @@
const should = require('should'),
sinon = require('sinon'),
testUtils = require('../../../../utils'),
urlService = require('../../../../../frontend/services/url'),
urlUtils = require('../../../../../server/lib/url-utils'),
controllers = require('../../../../../frontend/services/routing/controllers'),
helpers = require('../../../../../frontend/services/routing/helpers'),
EDITOR_URL = `/editor/post/`;
const should = require('should');
const sinon = require('sinon');
const testUtils = require('../../../../utils');
const configUtils = require('../../../../utils/configUtils');
const urlService = require('../../../../../frontend/services/url');
const urlUtils = require('../../../../../server/lib/url-utils');
const controllers = require('../../../../../frontend/services/routing/controllers');
const helpers = require('../../../../../frontend/services/routing/helpers');
const EDITOR_URL = `/editor/post/`;
describe('Unit - services/routing/controllers/entry', function () {
let req, res, entryLookUpStub, secureStub, renderStub, post, page;
@ -126,6 +127,30 @@ describe('Unit - services/routing/controllers/entry', function () {
});
});
it('isEditURL: true with admin redirects disabled', function (done) {
configUtils.set('admin:redirects', false);
req.path = post.url;
entryLookUpStub.withArgs(req.path, res.routerOptions)
.resolves({
isEditURL: true,
entry: post
});
urlUtils.redirectToAdmin.callsFake(function (statusCode, res, editorUrl) {
configUtils.restore();
done(new Error('redirectToAdmin was called'));
});
controllers.entry(req, res, (err) => {
configUtils.restore();
urlUtils.redirectToAdmin.called.should.eql(false);
should.not.exist(err);
done(err);
});
});
it('type of router !== type of resource', function (done) {
req.path = post.url;
res.routerOptions.resourceType = 'posts';