Ghost/core/server/api/app.js
Katharina Irrgang a68592a6b9 🔥 remove forceAdminSSL and urlSSL, add admin url (#7937)
* 🔥  kill apiUrl helper, use urlFor helper instead

More consistency of creating urls.
Creates an easier ability to add config changes.

Attention: urlFor function is getting a little nesty, BUT that is for now wanted to make easier and centralised changes to the configs.
The url util need's refactoring anyway.

* 🔥  urlSSL

Remove all urlSSL usages.
Add TODO's for the next commit to re-add logic for deleted logic.

e.g.

- cors helper generated an array of url's to allow requests from the defined config url's -> will be replaced by the admin url if available
- theme handler prefered the urlSSL in case it was defined -> will be replaced by using the urlFor helper to get the blog url (based on the request secure flag)

The changes in this commit doesn't have to be right, but it helped going step by step.
The next commit is the more interesting one.

* 🔥    remove forceAdminSSL, add new admin url and adapt logic

I wanted to remove the forceAdminSSL as separate commit, but was hard to realise.
That's why both changes are in one commit:

1. remove forceAdminSSL
2. add admin.url option

- fix TODO's from last commits
- rewrite the ssl middleware!
- create some private helper functions in the url helper to realise the changes
- rename some wordings and functions e.g. base === blog (we have so much different wordings)
- i would like to do more, but this would end in a non readable PR
- this commit contains the most important changes to offer admin.url option

* 🤖  adapt tests

IMPORTANT
- all changes in the routing tests were needed, because each routing test did not start the ghost server
- they just required the ghost application, which resulted in a random server port
- having a random server port results in a redirect, caused by the ssl/redirect middleware

* 😎  rename check-ssl middleware

* 🎨  fix theme-handler because of master rebase
2017-02-03 18:13:22 +00:00

262 lines
10 KiB
JavaScript

// # API routes
var debug = require('debug')('ghost:api'),
express = require('express'),
tmpdir = require('os').tmpdir,
// This essentially provides the controllers for the routes
api = require('../api'),
// Include the middleware
// API specific
auth = require('../auth'),
cors = require('../middleware/api/cors'), // routes only?!
brute = require('../middleware/brute'), // routes only
versionMatch = require('../middleware/api/version-match'), // global
// Handling uploads & imports
upload = require('multer')({dest: tmpdir()}), // routes only
validation = require('../middleware/validation'), // routes only
// Shared
bodyParser = require('body-parser'), // global, shared
cacheControl = require('../middleware/cache-control'), // global, shared
urlRedirects = require('../middleware/url-redirects'),
prettyURLs = require('../middleware/pretty-urls'),
maintenance = require('../middleware/maintenance'), // global, shared
errorHandler = require('../middleware/error-handler'), // global, shared
// Temporary
// @TODO find a more appy way to do this!
labs = require('../middleware/labs'),
// @TODO find a better way to bundle these authentication packages
// Authentication for public endpoints
authenticatePublic = [
auth.authenticate.authenticateClient,
auth.authenticate.authenticateUser,
auth.authorize.requiresAuthorizedUserPublicAPI
],
// Require user for private endpoints
authenticatePrivate = [
auth.authenticate.authenticateClient,
auth.authenticate.authenticateUser,
auth.authorize.requiresAuthorizedUser
];
// @TODO refactor/clean this up - how do we want the routing to work long term?
function apiRoutes() {
var apiRouter = express.Router();
// alias delete with del
apiRouter.del = apiRouter.delete;
// ## CORS pre-flight check
apiRouter.options('*', cors);
// ## Configuration
apiRouter.get('/configuration', api.http(api.configuration.read));
apiRouter.get('/configuration/:key', authenticatePrivate, api.http(api.configuration.read));
apiRouter.get('/configuration/timezones', authenticatePrivate, api.http(api.configuration.read));
// ## Posts
apiRouter.get('/posts', authenticatePublic, api.http(api.posts.browse));
apiRouter.post('/posts', authenticatePrivate, api.http(api.posts.add));
apiRouter.get('/posts/:id', authenticatePublic, api.http(api.posts.read));
apiRouter.get('/posts/slug/:slug', authenticatePublic, api.http(api.posts.read));
apiRouter.put('/posts/:id', authenticatePrivate, api.http(api.posts.edit));
apiRouter.del('/posts/:id', authenticatePrivate, api.http(api.posts.destroy));
// ## Schedules
apiRouter.put('/schedules/posts/:id', [
auth.authenticate.authenticateClient,
auth.authenticate.authenticateUser
], api.http(api.schedules.publishPost));
// ## Settings
apiRouter.get('/settings', authenticatePrivate, api.http(api.settings.browse));
apiRouter.get('/settings/:key', authenticatePrivate, api.http(api.settings.read));
apiRouter.put('/settings', authenticatePrivate, api.http(api.settings.edit));
// ## Users
apiRouter.get('/users', authenticatePublic, api.http(api.users.browse));
apiRouter.get('/users/:id', authenticatePublic, api.http(api.users.read));
apiRouter.get('/users/slug/:slug', authenticatePublic, api.http(api.users.read));
apiRouter.get('/users/email/:email', authenticatePublic, api.http(api.users.read));
apiRouter.put('/users/password', authenticatePrivate, api.http(api.users.changePassword));
apiRouter.put('/users/owner', authenticatePrivate, api.http(api.users.transferOwnership));
apiRouter.put('/users/:id', authenticatePrivate, api.http(api.users.edit));
apiRouter.post('/users', authenticatePrivate, api.http(api.users.add));
apiRouter.del('/users/:id', authenticatePrivate, api.http(api.users.destroy));
// ## Tags
apiRouter.get('/tags', authenticatePublic, api.http(api.tags.browse));
apiRouter.get('/tags/:id', authenticatePublic, api.http(api.tags.read));
apiRouter.get('/tags/slug/:slug', authenticatePublic, api.http(api.tags.read));
apiRouter.post('/tags', authenticatePrivate, api.http(api.tags.add));
apiRouter.put('/tags/:id', authenticatePrivate, api.http(api.tags.edit));
apiRouter.del('/tags/:id', authenticatePrivate, api.http(api.tags.destroy));
// ## Subscribers
apiRouter.get('/subscribers', labs.subscribers, authenticatePrivate, api.http(api.subscribers.browse));
apiRouter.get('/subscribers/csv', labs.subscribers, authenticatePrivate, api.http(api.subscribers.exportCSV));
apiRouter.post('/subscribers/csv',
labs.subscribers,
authenticatePrivate,
upload.single('subscribersfile'),
validation.upload({type: 'subscribers'}),
api.http(api.subscribers.importCSV)
);
apiRouter.get('/subscribers/:id', labs.subscribers, authenticatePrivate, api.http(api.subscribers.read));
apiRouter.post('/subscribers', labs.subscribers, authenticatePublic, api.http(api.subscribers.add));
apiRouter.put('/subscribers/:id', labs.subscribers, authenticatePrivate, api.http(api.subscribers.edit));
apiRouter.del('/subscribers/:id', labs.subscribers, authenticatePrivate, api.http(api.subscribers.destroy));
// ## Roles
apiRouter.get('/roles/', authenticatePrivate, api.http(api.roles.browse));
// ## Clients
apiRouter.get('/clients/slug/:slug', api.http(api.clients.read));
// ## Slugs
apiRouter.get('/slugs/:type/:name', authenticatePrivate, api.http(api.slugs.generate));
// ## Themes
apiRouter.get('/themes/:name/download',
authenticatePrivate,
api.http(api.themes.download)
);
apiRouter.post('/themes/upload',
authenticatePrivate,
upload.single('theme'),
validation.upload({type: 'themes'}),
api.http(api.themes.upload)
);
apiRouter.del('/themes/:name',
authenticatePrivate,
api.http(api.themes.destroy)
);
// ## Notifications
apiRouter.get('/notifications', authenticatePrivate, api.http(api.notifications.browse));
apiRouter.post('/notifications', authenticatePrivate, api.http(api.notifications.add));
apiRouter.del('/notifications/:id', authenticatePrivate, api.http(api.notifications.destroy));
// ## DB
apiRouter.get('/db', authenticatePrivate, api.http(api.db.exportContent));
apiRouter.post('/db',
authenticatePrivate,
upload.single('importfile'),
validation.upload({type: 'db'}),
api.http(api.db.importContent)
);
apiRouter.del('/db', authenticatePrivate, api.http(api.db.deleteAllContent));
// ## Mail
apiRouter.post('/mail', authenticatePrivate, api.http(api.mail.send));
apiRouter.post('/mail/test', authenticatePrivate, api.http(api.mail.sendTest));
// ## Slack
apiRouter.post('/slack/test', authenticatePrivate, api.http(api.slack.sendTest));
// ## Authentication
apiRouter.post('/authentication/passwordreset',
brute.globalReset,
brute.userReset,
api.http(api.authentication.generateResetToken)
);
apiRouter.put('/authentication/passwordreset', brute.globalBlock, api.http(api.authentication.resetPassword));
apiRouter.post('/authentication/invitation', api.http(api.authentication.acceptInvitation));
apiRouter.get('/authentication/invitation', api.http(api.authentication.isInvitation));
apiRouter.post('/authentication/setup', api.http(api.authentication.setup));
apiRouter.put('/authentication/setup', authenticatePrivate, api.http(api.authentication.updateSetup));
apiRouter.get('/authentication/setup', api.http(api.authentication.isSetup));
apiRouter.post('/authentication/token',
brute.globalBlock,
brute.userLogin,
auth.authenticate.authenticateClient,
auth.oauth.generateAccessToken
);
apiRouter.post('/authentication/revoke', authenticatePrivate, api.http(api.authentication.revoke));
// ## Uploads
// @TODO: rename endpoint to /images/upload (or similar)
apiRouter.post('/uploads',
authenticatePrivate,
upload.single('uploadimage'),
validation.upload({type: 'images'}),
api.http(api.uploads.add)
);
apiRouter.post('/uploads/icon',
authenticatePrivate,
upload.single('uploadimage'),
validation.upload({type: 'icons'}),
validation.blogIcon(),
api.http(api.uploads.add)
);
// ## Invites
apiRouter.get('/invites', authenticatePrivate, api.http(api.invites.browse));
apiRouter.get('/invites/:id', authenticatePrivate, api.http(api.invites.read));
apiRouter.post('/invites', authenticatePrivate, api.http(api.invites.add));
apiRouter.del('/invites/:id', authenticatePrivate, api.http(api.invites.destroy));
return apiRouter;
}
module.exports = function setupApiApp() {
debug('API setup start');
var apiApp = express();
// @TODO finish refactoring this away.
apiApp.use(function setIsAdmin(req, res, next) {
// api === isAdmin
res.isAdmin = true;
next();
});
// API middleware
// Body parsing
apiApp.use(bodyParser.json({limit: '1mb'}));
apiApp.use(bodyParser.urlencoded({extended: true, limit: '1mb'}));
apiApp.use(cors);
// send 503 json response in case of maintenance
apiApp.use(maintenance);
// Force SSL if required
// must happen AFTER asset loading and BEFORE routing
apiApp.use(urlRedirects);
// Add in all trailing slashes & remove uppercase
// must happen AFTER asset loading and BEFORE routing
apiApp.use(prettyURLs);
// Check version matches for API requests, depends on res.locals.safeVersion being set
// Therefore must come after themeHandler.ghostLocals, for now
apiApp.use(versionMatch);
// API shouldn't be cached
apiApp.use(cacheControl('private'));
// Routing
apiApp.use(apiRoutes());
// API error handling
apiApp.use(errorHandler.resourceNotFound);
apiApp.use(errorHandler.handleJSONResponse);
debug('API setup end');
return apiApp;
};