From 0d047f4eb9514efc30b050ac4f6d2a67dcd40e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 22 Jul 2019 18:16:18 -0400 Subject: [PATCH] feat: #7743, webserver --- src/flags.js | 2 + src/plugins/index.js | 4 +- src/routes/authentication.js | 143 ++++++++++++++++------------------- src/routes/index.js | 24 ++---- src/webserver.js | 100 +++++++++--------------- 5 files changed, 114 insertions(+), 159 deletions(-) diff --git a/src/flags.js b/src/flags.js index d2603ce3e9..eacfacf672 100644 --- a/src/flags.js +++ b/src/flags.js @@ -761,3 +761,5 @@ Flags.notify = function (flagObj, uid, callback) { break; } }; + +require('./promisify')(Flags); diff --git a/src/plugins/index.js b/src/plugins/index.js index 1774e84052..a1d1fcac1e 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -125,9 +125,9 @@ Plugins.reload = async function () { }); }; -Plugins.reloadRoutes = async function (router) { +Plugins.reloadRoutes = async function (params) { var controllers = require('../controllers'); - await Plugins.fireHook('static:app.load', { app: app, router: router, middleware: middleware, controllers: controllers }); + await Plugins.fireHook('static:app.load', { app: app, router: params.router, middleware: middleware, controllers: controllers }); winston.verbose('[plugins] All plugins reloaded and rerouted'); }; diff --git a/src/routes/authentication.js b/src/routes/authentication.js index 88d797a5a3..209c268a5c 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -46,98 +46,89 @@ Auth.getLoginStrategies = function () { return loginStrategies; }; -Auth.reloadRoutes = function (router, callback) { +Auth.reloadRoutes = async function (params) { loginStrategies.length = 0; - + const router = params.router; if (plugins.hasListeners('action:auth.overrideLogin')) { winston.warn('[authentication] Login override detected, skipping local login strategy.'); plugins.fireHook('action:auth.overrideLogin'); } else { passport.use(new passportLocal({ passReqToCallback: true }, controllers.authentication.localLogin)); } + try { + loginStrategies = await plugins.fireHook('filter:auth.init', loginStrategies); + } catch (err) { + winston.error('[authentication] ' + err.stack); + } - async.waterfall([ - function (next) { - plugins.fireHook('filter:auth.init', loginStrategies, function (err) { + loginStrategies = loginStrategies || []; + loginStrategies.forEach(function (strategy) { + if (strategy.url) { + router.get(strategy.url, Auth.middleware.applyCSRF, function (req, res, next) { + req.session.ssoState = req.csrfToken(); + passport.authenticate(strategy.name, { + scope: strategy.scope, + prompt: strategy.prompt || undefined, + state: req.session.ssoState, + })(req, res, next); + }); + } + + router[strategy.callbackMethod || 'get'](strategy.callbackURL, function (req, res, next) { + // Ensure the passed-back state value is identical to the saved ssoState (unless explicitly skipped) + if (strategy.checkState === false) { + return next(); + } + + next(req.query.state !== req.session.ssoState ? new Error('[[error:csrf-invalid]]') : null); + }, function (req, res, next) { + // Trigger registration interstitial checks + req.session.registration = req.session.registration || {}; + // save returnTo for later usage in /register/complete + // passport seems to remove `req.session.returnTo` after it redirects + req.session.registration.returnTo = req.session.returnTo; + next(); + }, function (req, res, next) { + passport.authenticate(strategy.name, function (err, user) { if (err) { - winston.error('[authentication] ' + err.stack); + delete req.session.registration; + return next(err); } - next(null, loginStrategies); - }); + + if (!user) { + delete req.session.registration; + return helpers.redirect(res, strategy.failureUrl !== undefined ? strategy.failureUrl : '/login'); + } + + res.locals.user = user; + res.locals.strategy = strategy; + next(); + })(req, res, next); }, - function (loginStrategies, next) { - loginStrategies = loginStrategies || []; - loginStrategies.forEach(function (strategy) { - if (strategy.url) { - router.get(strategy.url, Auth.middleware.applyCSRF, function (req, res, next) { - req.session.ssoState = req.csrfToken(); - passport.authenticate(strategy.name, { - scope: strategy.scope, - prompt: strategy.prompt || undefined, - state: req.session.ssoState, - })(req, res, next); - }); + Auth.middleware.validateAuth, + (req, res, next) => { + async.waterfall([ + async.apply(req.login.bind(req), res.locals.user), + async.apply(controllers.authentication.onSuccessfulLogin, req, req.uid), + ], function (err) { + if (err) { + return next(err); } - router[strategy.callbackMethod || 'get'](strategy.callbackURL, function (req, res, next) { - // Ensure the passed-back state value is identical to the saved ssoState (unless explicitly skipped) - if (strategy.checkState === false) { - return next(); - } - - next(req.query.state !== req.session.ssoState ? new Error('[[error:csrf-invalid]]') : null); - }, function (req, res, next) { - // Trigger registration interstitial checks - req.session.registration = req.session.registration || {}; - // save returnTo for later usage in /register/complete - // passport seems to remove `req.session.returnTo` after it redirects - req.session.registration.returnTo = req.session.returnTo; - next(); - }, function (req, res, next) { - passport.authenticate(strategy.name, function (err, user) { - if (err) { - delete req.session.registration; - return next(err); - } - - if (!user) { - delete req.session.registration; - return helpers.redirect(res, strategy.failureUrl !== undefined ? strategy.failureUrl : '/login'); - } - - res.locals.user = user; - res.locals.strategy = strategy; - next(); - })(req, res, next); - }, - Auth.middleware.validateAuth, - (req, res, next) => { - async.waterfall([ - async.apply(req.login.bind(req), res.locals.user), - async.apply(controllers.authentication.onSuccessfulLogin, req, req.uid), - ], function (err) { - if (err) { - return next(err); - } - - helpers.redirect(res, strategy.successUrl !== undefined ? strategy.successUrl : '/'); - }); - }); + helpers.redirect(res, strategy.successUrl !== undefined ? strategy.successUrl : '/'); }); + }); + }); - var multipart = require('connect-multiparty'); - var multipartMiddleware = multipart(); - var middlewares = [multipartMiddleware, Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist]; - - router.post('/register', middlewares, controllers.authentication.register); - router.post('/register/complete', middlewares, controllers.authentication.registerComplete); - router.post('/register/abort', controllers.authentication.registerAbort); - router.post('/login', Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist, controllers.authentication.login); - router.post('/logout', Auth.middleware.applyCSRF, controllers.authentication.logout); + var multipart = require('connect-multiparty'); + var multipartMiddleware = multipart(); + var middlewares = [multipartMiddleware, Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist]; - next(); - }, - ], callback); + router.post('/register', middlewares, controllers.authentication.register); + router.post('/register/complete', middlewares, controllers.authentication.registerComplete); + router.post('/register/abort', controllers.authentication.registerAbort); + router.post('/login', Auth.middleware.applyCSRF, Auth.middleware.applyBlacklist, controllers.authentication.login); + router.post('/logout', Auth.middleware.applyCSRF, controllers.authentication.logout); }; passport.serializeUser(function (user, done) { diff --git a/src/routes/index.js b/src/routes/index.js index b6809d1e54..3a9bc4db56 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -3,7 +3,6 @@ var nconf = require('nconf'); var winston = require('winston'); var path = require('path'); -var async = require('async'); var express = require('express'); var meta = require('../meta'); @@ -90,7 +89,7 @@ function groupRoutes(app, middleware, controllers) { setupPageRoute(app, '/groups/:slug/members', middleware, middlewares, controllers.groups.members); } -module.exports = function (app, middleware, callback) { +module.exports = async function (app, middleware) { const router = express.Router(); router.render = function () { app.render.apply(app, arguments); @@ -109,21 +108,15 @@ module.exports = function (app, middleware, callback) { // homepage handled by `action:homepage.get:[route]` setupPageRoute(router, '/', middleware, [], controllers.home.pluginHook); - async.series([ - async.apply(plugins.reloadRoutes, router), - async.apply(authRoutes.reloadRoutes, router), - async.apply(addCoreRoutes, app, router, middleware), - async.apply(user.addInterstitials), - function (next) { - winston.info('Routes added'); - next(); - }, - ], function (err) { - callback(err); - }); + await plugins.reloadRoutes({ router: router }); + await authRoutes.reloadRoutes({ router: router }); + addCoreRoutes(app, router, middleware); + await user.addInterstitials(); + + winston.info('Routes added'); }; -function addCoreRoutes(app, router, middleware, callback) { +function addCoreRoutes(app, router, middleware) { adminRoutes(router, middleware, controllers); metaRoutes(router, middleware, controllers); apiRoutes(router, middleware, controllers); @@ -190,5 +183,4 @@ function addCoreRoutes(app, router, middleware, callback) { app.use(controllers['404'].handle404); app.use(controllers.errors.handleURIErrors); app.use(controllers.errors.handleErrors); - setImmediate(callback); } diff --git a/src/webserver.js b/src/webserver.js index a1d0c2d1c0..b880fc3590 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -2,6 +2,7 @@ 'use strict'; var fs = require('fs'); +const util = require('util'); var path = require('path'); var os = require('os'); var nconf = require('nconf'); @@ -65,7 +66,7 @@ server.on('connection', function (conn) { }); }); -module.exports.destroy = function (callback) { +exports.destroy = function (callback) { server.close(callback); for (var key in connections) { if (connections.hasOwnProperty(key)) { @@ -74,76 +75,45 @@ module.exports.destroy = function (callback) { } }; -module.exports.listen = function (callback) { - callback = callback || function () { }; +exports.listen = async function () { emailer.registerApp(app); + setupExpressApp(app); + helpers.register(); + logger.init(app); + await initializeNodeBB(); + winston.info('NodeBB Ready'); + + require('./socket.io').server.emit('event:nodebb.ready', { + 'cache-buster': meta.config['cache-buster'], + hostname: os.hostname(), + }); - async.waterfall([ - function (next) { - setupExpressApp(app, next); - }, - function (next) { - helpers.register(); - - logger.init(app); - - initializeNodeBB(next); - }, - function (next) { - winston.info('NodeBB Ready'); - - require('./socket.io').server.emit('event:nodebb.ready', { - 'cache-buster': meta.config['cache-buster'], - hostname: os.hostname(), - }); - - plugins.fireHook('action:nodebb.ready'); + plugins.fireHook('action:nodebb.ready'); - listen(next); - }, - ], callback); + await util.promisify(listen)(); }; -function initializeNodeBB(callback) { - var middleware = require('./middleware'); - - async.waterfall([ - function (next) { - meta.themes.setupPaths(next); - }, - function (next) { - plugins.init(app, middleware, next); - }, - async.apply(plugins.fireHook, 'static:assets.prepare', {}), - function (next) { - plugins.fireHook('static:app.preload', { - app: app, - middleware: middleware, - }, next); - }, - function (next) { - routes(app, middleware, next); - }, - function (next) { - meta.sounds.addUploads(next); - }, - function (next) { - meta.blacklist.load(next); - }, - function (next) { - flags.init(next); - }, - ], function (err) { - callback(err); +async function initializeNodeBB() { + const middleware = require('./middleware'); + await meta.themes.setupPaths(); + await plugins.init(app, middleware); + await plugins.fireHook('static:assets.prepare', {}); + await plugins.fireHook('static:app.preload', { + app: app, + middleware: middleware, }); + await routes(app, middleware); + await meta.sounds.addUploads(); + await meta.blacklist.load(); + await flags.init(); } -function setupExpressApp(app, callback) { - var middleware = require('./middleware'); - var pingController = require('./controllers/ping'); +function setupExpressApp(app) { + const middleware = require('./middleware'); + const pingController = require('./controllers/ping'); - var relativePath = nconf.get('relative_path'); - var viewsDir = nconf.get('views_dir'); + const relativePath = nconf.get('relative_path'); + const viewsDir = nconf.get('views_dir'); app.engine('tpl', function (filepath, data, next) { filepath = filepath.replace(/\.tpl$/, '.js'); @@ -210,8 +180,6 @@ function setupExpressApp(app, callback) { var toobusy = require('toobusy-js'); toobusy.maxLag(meta.config.eventLoopLagThreshold); toobusy.interval(meta.config.eventLoopInterval); - - callback(); } function setupFavicon(app) { @@ -308,7 +276,7 @@ function listen(callback) { } } -module.exports.testSocket = function (socketPath, callback) { +exports.testSocket = function (socketPath, callback) { if (typeof socketPath !== 'string') { return callback(new Error('invalid socket path : ' + socketPath)); } @@ -337,3 +305,5 @@ module.exports.testSocket = function (socketPath, callback) { async.apply(fs.unlink, socketPath), // The socket was stale, kick it out of the way ], callback); }; + +require('./promisify')(exports);