From f8695e615edd8dc830bb8b51a7219091e97a0574 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Tue, 28 Sep 2021 11:43:49 +0200 Subject: [PATCH] Add validation & required check --- packages/core/strapi/lib/middlewares/index.js | 117 ----------------- .../core/strapi/lib/services/server/index.js | 10 +- .../services/server/register-middlewares.js | 123 ++++++++++++++++-- 3 files changed, 116 insertions(+), 134 deletions(-) diff --git a/packages/core/strapi/lib/middlewares/index.js b/packages/core/strapi/lib/middlewares/index.js index 1c2439c432..89eb7819f7 100644 --- a/packages/core/strapi/lib/middlewares/index.js +++ b/packages/core/strapi/lib/middlewares/index.js @@ -28,120 +28,3 @@ module.exports = { favicon, public: publicStatic, }; - -// const requiredMiddlewares = [ -// 'auth', -// 'responses', -// 'router', -// 'logger', -// 'error', -// 'cors', -// 'cron', -// 'xframe', -// 'xss', -// 'public', -// 'favicon', -// ]; - -// module.exports = async function(strapi) { -// /** Utils */ -// const middlewareConfig = strapi.config.middleware; - -// // check if a middleware exists -// const middlewareExists = key => { -// return !isUndefined(strapi.middleware[key]); -// }; - -// // check if a middleware is enabled -// const middlewareEnabled = key => { -// return ( -// requiredMiddlewares.includes(key) || -// get(middlewareConfig, ['settings', key, 'enabled'], false) === true -// ); -// }; - -// // list of enabled middlewares -// const enabledMiddlewares = Object.keys(strapi.middleware).filter(middlewareEnabled); - -// // Method to initialize middlewares and emit an event. -// const initialize = middlewareKey => { -// if (strapi.middleware[middlewareKey].loaded === true) return; - -// const module = strapi.middleware[middlewareKey].load; - -// return new Promise((resolve, reject) => { -// const timeout = setTimeout( -// () => reject(`(middleware: ${middlewareKey}) is taking too long to load.`), -// middlewareConfig.timeout || 1000 -// ); -// strapi.middleware[middlewareKey] = merge(strapi.middleware[middlewareKey], module); - -// Promise.resolve() -// .then(() => module.initialize(strapi)) -// .then(() => { -// clearTimeout(timeout); -// strapi.middleware[middlewareKey].loaded = true; -// resolve(); -// }) -// .catch(err => { -// clearTimeout(timeout); - -// if (err) { -// return reject(err); -// } -// }); -// }); -// }; - -// /** -// * Run init functions -// */ - -// // Run beforeInitialize of every middleware -// await Promise.all( -// enabledMiddlewares.map(key => { -// const { beforeInitialize } = strapi.middleware[key].load; -// if (typeof beforeInitialize === 'function') { -// return beforeInitialize(strapi); -// } -// }) -// ); - -// // run the initialization of an array of middlewares sequentially -// const initMiddlewaresSeq = async middlewareArr => { -// for (let key of uniq(middlewareArr)) { -// await initialize(key); -// } -// }; - -// const middlewaresBefore = get(middlewareConfig, 'load.before', []) -// .filter(middlewareExists) -// .filter(middlewareEnabled); - -// const middlewaresAfter = get(middlewareConfig, 'load.after', []) -// .filter(middlewareExists) -// .filter(middlewareEnabled); - -// const middlewaresOrder = get(middlewareConfig, 'load.order', []) -// .filter(middlewareExists) -// .filter(middlewareEnabled); - -// const unspecifiedMiddlewares = difference( -// enabledMiddlewares, -// middlewaresBefore, -// middlewaresOrder, -// middlewaresAfter -// ); - -// // before -// await initMiddlewaresSeq(middlewaresBefore); - -// // ordered // rest of middlewares -// await Promise.all([ -// initMiddlewaresSeq(middlewaresOrder), -// Promise.all(unspecifiedMiddlewares.map(initialize)), -// ]); - -// // after -// await initMiddlewaresSeq(middlewaresAfter); -// }; diff --git a/packages/core/strapi/lib/services/server/index.js b/packages/core/strapi/lib/services/server/index.js index 0d9f0fbcbd..e412fe79ac 100644 --- a/packages/core/strapi/lib/services/server/index.js +++ b/packages/core/strapi/lib/services/server/index.js @@ -91,13 +91,15 @@ const createServer = strapi => { return this; }, - initRouting() { - registerAllRoutes(strapi); + async initRouting() { + await registerAllRoutes(strapi); + return this; }, - initMiddlewares() { - registerMiddlewares(strapi); + async initMiddlewares() { + await registerMiddlewares(strapi); + return this; }, diff --git a/packages/core/strapi/lib/services/server/register-middlewares.js b/packages/core/strapi/lib/services/server/register-middlewares.js index 9392811ca5..f376e2acee 100644 --- a/packages/core/strapi/lib/services/server/register-middlewares.js +++ b/packages/core/strapi/lib/services/server/register-middlewares.js @@ -1,5 +1,13 @@ 'use strict'; +const { yup } = require('@strapi/utils'); + +/** + * @type {import('../../').Strapi} Strapi + * @type {Array} MiddlewaresConfig + * @type {Array<{name: string, hanlder: Function}>} Middlewares + */ + const defaultConfig = [ 'strapi::errors', 'strapi::security', @@ -11,19 +19,98 @@ const defaultConfig = [ 'strapi::public', ]; +const requiredMiddlewares = [ + 'strapi::errors', + 'strapi::security', + 'strapi::cors', + 'strapi::request', + 'strapi::public', + 'strapi::favicon', +]; + +const middlewareConfigSchema = yup.array().of( + yup.lazy(value => { + if (typeof value === 'string') { + return yup.string().required(); + } + + if (typeof value === 'object') { + return yup + .object({ + name: yup.string(), + resolve: yup.string(), + config: yup.mixed(), + }) + .required() + .noUnknown(); + } + + return yup.test(() => false); + }) +); + /** * Register middlewares in router - * @param {import('../../').Strapi} strapi + * @param {Strapi} strapi */ -module.exports = strapi => { +module.exports = async strapi => { const middlewareConfig = strapi.config.get('middlewares', defaultConfig); - // must be an array - // verify required middlewares are register + await validateMiddlewareConfig(); + const middlewares = await initMiddlewares(middlewareConfig, strapi); + + checkRequiredMiddlewares(middlewares); + + for (const middleware of middlewares) { + strapi.server.use(middleware.handler); + } +}; + +/** + * + * @param {MiddlewaresConfig} config + */ +const validateMiddlewareConfig = async config => { + try { + await middlewareConfigSchema.validate(config, { strict: true, abortEarly: false }); + } catch (error) { + throw new Error( + 'Invalid middleware configuration. Expected Array { + const missingMiddlewares = requiredMiddlewares.filter(name => { + return middlewares.findIndex(mdl => mdl.name === name) === -1; + }); + + if (missingMiddlewares.length > 0) { + throw new Error( + `Missing required middlewares in configuration. Add the following middlewares: "${missingMiddlewares.join( + ', ' + )}".` + ); + } + + return; +}; + +/** + * Initialize every configured middlewares + * @param {MiddlewaresConfig} config + * @param {Strapi} strapi + * @returns {Middlewares} + */ +const initMiddlewares = async (config, strapi) => { const middlewares = []; - for (const item of middlewareConfig) { + for (const item of config) { if (typeof item === 'string') { const middlewareFactory = strapi.middleware(item); @@ -31,7 +118,11 @@ module.exports = strapi => { throw new Error(`Middleware ${item} not found.`); } - middlewares.push(middlewareFactory()); + middlewares.push({ + name: item, + handler: await middlewareFactory(), + }); + continue; } @@ -40,24 +131,30 @@ module.exports = strapi => { if (name) { const middlewareFactory = strapi.middleware(name); - middlewares.push(middlewareFactory(config)); + middlewares.push({ + name, + handler: await middlewareFactory(config), + }); + continue; } if (resolve) { - middlewares.push(require(resolve)(config)); + middlewares.push({ + name: resolve, + handler: await require(resolve)(config), + }); + continue; } - throw new Error('Missing name or resolve'); + throw new Error('Invalid middleware configuration. Missing name or resolve properties.'); } throw new Error( - 'Middlware config must either be a string or an object (name?: string, resolve?: string, config: any)' + 'Middlware config must either be a string or an object {name?: string, resolve?: string, config: any}.' ); } - for (const middleware of middlewares) { - strapi.server.use(middleware); - } + return middlewares; };