From 3c17f731ec7016cbae5dd15e93c9ee9887a701fc Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 13:52:35 +0100 Subject: [PATCH 01/20] unify code style with conditions --- lib/policies/index.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/policies/index.js b/lib/policies/index.js index f2ba78eb7..9e8717007 100644 --- a/lib/policies/index.js +++ b/lib/policies/index.js @@ -4,23 +4,22 @@ const path = require('path'); const logger = require('../logger').policy; const schemas = require('../schemas'); -const register = (policy) => { - const { name, schema } = policy; - const action = policy.policy; +const register = (policyOptions) => { + const { name, schema, policy: action } = policyOptions; const validate = schemas.register('policy', name, schema); - policy.policy = (params, ...args) => { + policyOptions.policy = (params, ...args) => { const validationResult = validate(params); if (validationResult.isValid) { return action(params, ...args); - } else { - logger.error(`Policy ${chalk.default.red.bold(name)} params validation failed: ${validationResult.error}`); - throw new Error(`POLICY_PARAMS_VALIDATION_FAILED`); } + + logger.error(`Policy ${chalk.default.red.bold(name)} params validation failed: ${validationResult.error}`); + throw new Error(`POLICY_PARAMS_VALIDATION_FAILED`); }; - policies[name] = policy; + policies[name] = policyOptions; }; const resolve = (policyName) => { From 488287464a6f0b4f65a2122287b05bcbef022fda Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 14:16:27 +0100 Subject: [PATCH 02/20] show condition name correctly --- lib/conditions/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/conditions/index.js b/lib/conditions/index.js index 338613a3b..b6e183506 100644 --- a/lib/conditions/index.js +++ b/lib/conditions/index.js @@ -24,7 +24,7 @@ function init () { // extending express.request express.request.matchEGCondition = function (conditionConfig) { - logger.debug(`matchEGCondition for ${conditionConfig}`); + logger.debug(`matchEGCondition for ${conditionConfig.name}`); const func = conditions[conditionConfig.name]; if (!func) { From 494ab86fe0e29bf997c56b967d2f1f714acea512 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 14:22:24 +0100 Subject: [PATCH 03/20] refactor pipeline construction --- lib/gateway/pipelines.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 86f434662..ef968aa7a 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -125,14 +125,14 @@ function configurePipeline (pipelinePoliciesConfig, config) { policySteps.push({}); } - for (const policyStep of policySteps) { + const middlewares = policySteps.map(policyStep => { const conditionConfig = policyStep.condition; // parameters that we pass to the policy at time of execution - const action = policyStep.action || {}; - Object.assign(action, ActionParams.prototype); + const action = Object.assign({}, policyStep.action, ActionParams.prototype); const policyMiddleware = policy(action, config); - router.use((req, res, next) => { + + return (req, res, next) => { if (!conditionConfig || req.matchEGCondition(conditionConfig)) { log.debug('request matched condition for action', policyStep.action, 'in policy', policyName); policyMiddleware(req, res, next); @@ -140,8 +140,10 @@ function configurePipeline (pipelinePoliciesConfig, config) { log.debug(`request did not matched condition for action`, policyStep.action, 'in policy', policyName); next(); } - }); - } + }; + }); + + router.use(middlewares); }); return router; From f9068fb6a5cee9f55216b9e06cbebde7c08eb8da Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 14:38:02 +0100 Subject: [PATCH 04/20] put default condition in the schema --- lib/config/schemas/gateway.config.json | 5 ++++- lib/gateway/pipelines.js | 11 +++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/config/schemas/gateway.config.json b/lib/config/schemas/gateway.config.json index 640bbbf93..c8f582236 100644 --- a/lib/config/schemas/gateway.config.json +++ b/lib/config/schemas/gateway.config.json @@ -57,7 +57,10 @@ }, "required": [ "name" - ] + ], + "default": { + "name": "always" + } } } } diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index ef968aa7a..65bdf9365 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -73,7 +73,7 @@ module.exports.bootstrap = function ({ app, config }) { return app; }; -function processApiEndpoints (apiEndpoints) { +const processApiEndpoints = (apiEndpoints) => { const cfg = {}; log.debug('loading apiEndpoints %j', apiEndpoints); for (const el in apiEndpoints) { @@ -98,7 +98,7 @@ function processApiEndpoints (apiEndpoints) { }); } return cfg; -} +}; function configurePipeline (pipelinePoliciesConfig, config) { const router = express.Router({ mergeParams: true }); @@ -128,12 +128,11 @@ function configurePipeline (pipelinePoliciesConfig, config) { const middlewares = policySteps.map(policyStep => { const conditionConfig = policyStep.condition; - // parameters that we pass to the policy at time of execution const action = Object.assign({}, policyStep.action, ActionParams.prototype); const policyMiddleware = policy(action, config); return (req, res, next) => { - if (!conditionConfig || req.matchEGCondition(conditionConfig)) { + if (req.matchEGCondition(conditionConfig)) { log.debug('request matched condition for action', policyStep.action, 'in policy', policyName); policyMiddleware(req, res, next); } else { @@ -194,7 +193,7 @@ function normalizeGatewayConfig (config) { return true; } -function validatePipelinePolicies (policies, avaiablePolicies) { +const validatePipelinePolicies = (policies, avaiablePolicies) => { policies.forEach(policyObj => { const policyNames = Object.keys(policyObj); @@ -203,7 +202,7 @@ function validatePipelinePolicies (policies, avaiablePolicies) { throw new Error('POLICY_NOT_DECLARED'); } }); -} +}; const generatePipelineHandler = ({ path, pipeline, route }) => { if (!pipeline) { From 12dd0c4c6cf2d0baac60d52a5e3327a62b408684 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 15:15:08 +0100 Subject: [PATCH 05/20] provide more data and logger --- lib/schemas/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/schemas/index.js b/lib/schemas/index.js index 9f2e491ec..d0e7a35fb 100644 --- a/lib/schemas/index.js +++ b/lib/schemas/index.js @@ -3,8 +3,11 @@ const logger = require('../../lib/logger').gateway; const ajv = new Ajv({ useDefaults: true, - coerceTypes: true + coerceTypes: true, + verbose: true, + logger }); + require('ajv-keywords')(ajv, 'instanceof'); const registeredKeys = []; From 67b26335fbb0197136654a4faff057f2e874f84c Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 15:15:47 +0100 Subject: [PATCH 06/20] restore conditionConfig check --- lib/gateway/pipelines.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 65bdf9365..984f839bd 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -132,7 +132,7 @@ function configurePipeline (pipelinePoliciesConfig, config) { const policyMiddleware = policy(action, config); return (req, res, next) => { - if (req.matchEGCondition(conditionConfig)) { + if (conditionConfig && req.matchEGCondition(conditionConfig)) { log.debug('request matched condition for action', policyStep.action, 'in policy', policyName); policyMiddleware(req, res, next); } else { From 5be5221d8532f025365550541d8582e9f88f4968 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 15:30:36 +0100 Subject: [PATCH 07/20] fix typo --- .eslintrc | 1 + lib/gateway/pipelines.js | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.eslintrc b/.eslintrc index db3b17001..28e30f1d6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,6 +15,7 @@ "node" ], "rules": { + "space-before-function-paren": 0, "comma-dangle": [ 2, "never" diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 984f839bd..89196425c 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -100,7 +100,7 @@ const processApiEndpoints = (apiEndpoints) => { return cfg; }; -function configurePipeline (pipelinePoliciesConfig, config) { +function configurePipeline(pipelinePoliciesConfig, config) { const router = express.Router({ mergeParams: true }); if (!Array.isArray(pipelinePoliciesConfig)) { @@ -132,11 +132,11 @@ function configurePipeline (pipelinePoliciesConfig, config) { const policyMiddleware = policy(action, config); return (req, res, next) => { - if (conditionConfig && req.matchEGCondition(conditionConfig)) { + if (!conditionConfig || req.matchEGCondition(conditionConfig)) { log.debug('request matched condition for action', policyStep.action, 'in policy', policyName); policyMiddleware(req, res, next); } else { - log.debug(`request did not matched condition for action`, policyStep.action, 'in policy', policyName); + log.debug(`request did not match condition for action`, policyStep.action, 'in policy', policyName); next(); } }; @@ -148,7 +148,7 @@ function configurePipeline (pipelinePoliciesConfig, config) { return router; } -function normalizeGatewayConfig (config) { +function normalizeGatewayConfig(config) { if (!config || !config.gatewayConfig) { throw new Error('No config provided'); } From 4f39c5de24e7f3b9b5e59b56158cce29bae52b2c Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 16:02:43 +0100 Subject: [PATCH 08/20] validate conditions ahead of the time --- lib/conditions/index.js | 12 ++++++------ lib/conditions/predefined.js | 6 +++--- lib/gateway/pipelines.js | 4 +++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/conditions/index.js b/lib/conditions/index.js index b6e183506..e3d1dd483 100644 --- a/lib/conditions/index.js +++ b/lib/conditions/index.js @@ -5,13 +5,13 @@ const schemas = require('../schemas'); const predefined = require('./predefined'); const conditions = {}; -function register ({ name, handler, schema }) { +function register({ name, handler, schema }) { const validate = schemas.register('condition', name, schema); - conditions[name] = (req, config) => { + conditions[name] = config => { const validationResult = validate(config); if (validationResult.isValid) { - return handler(req, config); + return req => handler(req, config); } logger.error(`Condition ${chalk.default.red.bold(name)} config validation failed: ${validationResult.error}`); @@ -19,7 +19,7 @@ function register ({ name, handler, schema }) { }; } -function init () { +function init() { predefined.forEach(register); // extending express.request @@ -32,10 +32,10 @@ function init () { return null; } - return func(this, conditionConfig); + return func(this); }; return { register }; } -module.exports = { init }; +module.exports = { init, conditions }; diff --git a/lib/conditions/predefined.js b/lib/conditions/predefined.js index 61c287814..18d8fe6e1 100644 --- a/lib/conditions/predefined.js +++ b/lib/conditions/predefined.js @@ -162,21 +162,21 @@ module.exports = [ }, { name: 'authenticated', - handler: (req, conditionConfig) => req.isAuthenticated(), + handler: req => req.isAuthenticated(), schema: { $id: 'http://express-gateway.io/schemas/conditions/authenticated.json' } }, { name: 'anonymous', - handler: (req, conditionConfig) => req.isUnauthenticated(), + handler: req => req.isUnauthenticated(), schema: { $id: 'http://express-gateway.io/schemas/conditions/anonymous.json' } }, { name: 'tlsClientAuthenticated', - handler: (req, conditionConfig) => req.client.authorized, + handler: req => req.client.authorized, schema: { $id: 'http://express-gateway.io/schemas/conditions/tlsClientAuthenticated.json' } diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 89196425c..024d11562 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -4,6 +4,7 @@ const log = require('../logger').gateway; const policies = require('../policies'); const EgContextBase = require('./context'); const ActionParams = require('./actionParams'); +const { conditions } = require('../conditions'); module.exports.bootstrap = function ({ app, config }) { if (!normalizeGatewayConfig(config)) { @@ -128,11 +129,12 @@ function configurePipeline(pipelinePoliciesConfig, config) { const middlewares = policySteps.map(policyStep => { const conditionConfig = policyStep.condition; + const condition = conditions[conditionConfig.name](conditionConfig); const action = Object.assign({}, policyStep.action, ActionParams.prototype); const policyMiddleware = policy(action, config); return (req, res, next) => { - if (!conditionConfig || req.matchEGCondition(conditionConfig)) { + if (!conditionConfig || condition(req)) { log.debug('request matched condition for action', policyStep.action, 'in policy', policyName); policyMiddleware(req, res, next); } else { From 56d3262641e91fbc6930e7630f9aff66d6b364fc Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 16:05:36 +0100 Subject: [PATCH 09/20] temporany compatibility --- lib/conditions/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/conditions/index.js b/lib/conditions/index.js index e3d1dd483..bee759458 100644 --- a/lib/conditions/index.js +++ b/lib/conditions/index.js @@ -32,7 +32,7 @@ function init() { return null; } - return func(this); + return func(conditionConfig)(this); }; return { register }; From 2c67b7283464bf16f6226cb67d76ec471a4075cc Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 16:09:23 +0100 Subject: [PATCH 10/20] check condition presence --- lib/gateway/pipelines.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 024d11562..441e679ef 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -129,7 +129,12 @@ function configurePipeline(pipelinePoliciesConfig, config) { const middlewares = policySteps.map(policyStep => { const conditionConfig = policyStep.condition; - const condition = conditions[conditionConfig.name](conditionConfig); + let condition; + + if (conditionConfig) { + condition = conditions[conditionConfig.name](conditionConfig); + } + const action = Object.assign({}, policyStep.action, ActionParams.prototype); const policyMiddleware = policy(action, config); From 832bddeca733fabe03f42f866fe76fcc99dc0e2b Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 16:22:01 +0100 Subject: [PATCH 11/20] fix test text --- test/conditions.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/conditions.test.js b/test/conditions.test.js index 6025d1015..0bc0b89dd 100644 --- a/test/conditions.test.js +++ b/test/conditions.test.js @@ -156,7 +156,7 @@ describe('tlsClientAuthenticated', function () { })).be.true(); }); - it('should return true if request is client authenticated', function () { + it('should return false if request is client authenticated', function () { req.client.authorized = false; should(req.matchEGCondition({ name: 'tlsClientAuthenticated' From 0944f65bfd61920c9ffc71cd9ba4dab7edce008e Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 16:22:14 +0100 Subject: [PATCH 12/20] add condition indirection layer --- lib/conditions/index.js | 2 +- lib/conditions/predefined.js | 34 +++++++++++++++++----------------- test/plugins/condition.test.js | 4 ++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/conditions/index.js b/lib/conditions/index.js index bee759458..ed9ea75ea 100644 --- a/lib/conditions/index.js +++ b/lib/conditions/index.js @@ -11,7 +11,7 @@ function register({ name, handler, schema }) { conditions[name] = config => { const validationResult = validate(config); if (validationResult.isValid) { - return req => handler(req, config); + return handler(config); } logger.error(`Condition ${chalk.default.red.bold(name)} config validation failed: ${validationResult.error}`); diff --git a/lib/conditions/predefined.js b/lib/conditions/predefined.js index 18d8fe6e1..7d33e3c3c 100644 --- a/lib/conditions/predefined.js +++ b/lib/conditions/predefined.js @@ -17,7 +17,7 @@ module.exports = [ }, { name: 'always', - handler: () => true, + handler: () => () => true, schema: { $id: 'http://express-gateway.io/schemas/conditions/always.json' } @@ -25,13 +25,13 @@ module.exports = [ // Not sure if anyone would ever use this in real life, but it is a // "legitimate" condition, and is useful during tests. name: 'never', - handler: () => false, + handler: () => () => false, schema: { $id: 'http://express-gateway.io/schemas/conditions/never.json' } }, { name: 'allOf', - handler: (req, actionConfig) => actionConfig.conditions.every(subItem => req.matchEGCondition(subItem)), + handler: config => req => config.conditions.every(subItem => req.matchEGCondition(subItem)), schema: { $id: 'http://express-gateway.io/schemas/conditions/allOf.json', type: 'object', @@ -45,7 +45,7 @@ module.exports = [ } }, { name: 'oneOf', - handler: (req, actionConfig) => actionConfig.conditions.some(subItem => req.matchEGCondition(subItem)), + handler: config => req => config.conditions.some(subItem => req.matchEGCondition(subItem)), schema: { $id: 'http://express-gateway.io/schemas/conditions/oneOf.json', type: 'object', @@ -59,7 +59,7 @@ module.exports = [ } }, { name: 'not', - handler: (req, actionConfig) => !req.matchEGCondition(actionConfig.condition), + handler: config => req => !req.matchEGCondition(config.condition), schema: { $id: 'http://express-gateway.io/schemas/conditions/not.json', type: 'object', @@ -70,7 +70,7 @@ module.exports = [ } }, { name: 'pathMatch', - handler: (req, actionConfig) => req.url.match(new RegExp(actionConfig.pattern)) !== null, + handler: config => req => req.url.match(new RegExp(config.pattern)) !== null, schema: { $id: 'http://express-gateway.io/schemas/conditions/pathMatch.json', type: 'object', @@ -85,7 +85,7 @@ module.exports = [ } }, { name: 'pathExact', - handler: (req, actionConfig) => req.url === actionConfig.path, + handler: config => req => req.url === config.path, schema: { $id: 'http://express-gateway.io/schemas/conditions/pathExact.json', type: 'object', @@ -99,11 +99,11 @@ module.exports = [ } }, { name: 'method', - handler: (req, actionConfig) => { - if (Array.isArray(actionConfig.methods)) { - return actionConfig.methods.includes(req.method); + handler: config => req => { + if (Array.isArray(config.methods)) { + return config.methods.includes(req.method); } else { - return req.method === actionConfig.methods; + return req.method === config.methods; } }, schema: { @@ -128,9 +128,9 @@ module.exports = [ } }, { name: 'hostMatch', - handler: (req, actionConfig) => { + handler: config => req => { if (req.headers.host) { - return minimatch(req.headers.host, actionConfig.pattern); + return minimatch(req.headers.host, config.pattern); } return false; }, @@ -147,7 +147,7 @@ module.exports = [ } }, { name: 'expression', - handler: (req, conditionConfig) => req.egContext.match(conditionConfig.expression), + handler: config => req => req.egContext.match(config.expression), schema: { $id: 'http://express-gateway.io/schemas/conditions/expression.json', type: 'object', @@ -162,21 +162,21 @@ module.exports = [ }, { name: 'authenticated', - handler: req => req.isAuthenticated(), + handler: () => req => req.isAuthenticated(), schema: { $id: 'http://express-gateway.io/schemas/conditions/authenticated.json' } }, { name: 'anonymous', - handler: req => req.isUnauthenticated(), + handler: () => req => req.isUnauthenticated(), schema: { $id: 'http://express-gateway.io/schemas/conditions/anonymous.json' } }, { name: 'tlsClientAuthenticated', - handler: req => req.client.authorized, + handler: () => req => req.client.authorized, schema: { $id: 'http://express-gateway.io/schemas/conditions/tlsClientAuthenticated.json' } diff --git a/test/plugins/condition.test.js b/test/plugins/condition.test.js index d7371609c..782a5bdcf 100644 --- a/test/plugins/condition.test.js +++ b/test/plugins/condition.test.js @@ -40,7 +40,7 @@ describe('gateway condition with plugins', () => { plugins: { conditions: [{ name: 'test-condition', - handler: function (req, conditionConfig) { + handler: conditionConfig => req => { should(conditionConfig.param1).ok(); should(req.url).be.eql('/test'); return (conditionConfig.param1 === req.url); @@ -91,7 +91,7 @@ describe('gateway condition schema with plugins', () => { }, required: ['param1'] }, - handler: function (req, conditionConfig) { + handler: conditionConfig => req => { should(conditionConfig.param1).be.ok(); should(req.url).be.eql('/test'); return (conditionConfig.param1 === req.url); From b3323bbbb1b76a7a215d21074d8452197fec8e67 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 16:44:23 +0100 Subject: [PATCH 13/20] provide validation for sub conditions --- lib/conditions/predefined.js | 16 +++++++++++++--- lib/config/gateway.config.yml | 2 +- lib/schemas/index.js | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/conditions/predefined.js b/lib/conditions/predefined.js index 7d33e3c3c..1e3fc3003 100644 --- a/lib/conditions/predefined.js +++ b/lib/conditions/predefined.js @@ -1,5 +1,15 @@ const minimatch = require('minimatch'); +const grabArray = config => { + const { conditions } = require('./index'); + return config.conditions.map(config => conditions[config.name](config)); +}; + +const grabSingle = config => { + const { conditions } = require('./index'); + return conditions[config.name](config); +}; + module.exports = [ { name: 'base', @@ -31,7 +41,7 @@ module.exports = [ } }, { name: 'allOf', - handler: config => req => config.conditions.every(subItem => req.matchEGCondition(subItem)), + handler: config => req => grabArray(config).every(c => c(req)), schema: { $id: 'http://express-gateway.io/schemas/conditions/allOf.json', type: 'object', @@ -45,7 +55,7 @@ module.exports = [ } }, { name: 'oneOf', - handler: config => req => config.conditions.some(subItem => req.matchEGCondition(subItem)), + handler: config => req => grabArray(config).some(c => c(req)), schema: { $id: 'http://express-gateway.io/schemas/conditions/oneOf.json', type: 'object', @@ -59,7 +69,7 @@ module.exports = [ } }, { name: 'not', - handler: config => req => !req.matchEGCondition(config.condition), + handler: config => req => !grabSingle(config.condition)(req), schema: { $id: 'http://express-gateway.io/schemas/conditions/not.json', type: 'object', diff --git a/lib/config/gateway.config.yml b/lib/config/gateway.config.yml index 666c9785a..4e234ffbc 100644 --- a/lib/config/gateway.config.yml +++ b/lib/config/gateway.config.yml @@ -22,5 +22,5 @@ pipelines: policies: # - key-auth: # uncomment to enable key-auth - proxy: - - action: + action: serviceEndpoint: backend diff --git a/lib/schemas/index.js b/lib/schemas/index.js index d0e7a35fb..c2eac517b 100644 --- a/lib/schemas/index.js +++ b/lib/schemas/index.js @@ -12,7 +12,7 @@ require('ajv-keywords')(ajv, 'instanceof'); const registeredKeys = []; -function register (type, name, schema) { +function register(type, name, schema) { /* This piece of code is checking that the schema isn't already registered. This is not optimal and it's happening because the testing helpers aren't @@ -39,7 +39,7 @@ function register (type, name, schema) { return (data) => validate(schema.$id, data); } -function find (param = null) { +function find(param = null) { if (param) { const item = ajv.getSchema(param); if (item) { From 1720823274a7b894b24ee138652a2639a0c8ea5f Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 17:10:37 +0100 Subject: [PATCH 14/20] json schema validator --- lib/conditions/json-schema.js | 33 +++++++++++++++++++++++++++++++++ lib/conditions/predefined.js | 5 +++-- lib/gateway/pipelines.js | 7 +++++-- 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 lib/conditions/json-schema.js diff --git a/lib/conditions/json-schema.js b/lib/conditions/json-schema.js new file mode 100644 index 000000000..e872085a9 --- /dev/null +++ b/lib/conditions/json-schema.js @@ -0,0 +1,33 @@ +const express = require('express'); +const schemas = require('../schemas'); +const logger = require('../logger').gateway; + +module.exports = { + name: 'json-schema', + schema: { + $id: 'http://express-gateway.io/schemas/policies/json-schema.json', + type: 'object', + properties: { + schema: { + type: 'object', + description: 'Json schema to validate against.', + examples: ['{"type":"string"}'] + }, + extendedErrors: { + type: 'boolean', + default: false, + description: 'Value istructing the gateway to return the extended Schema Validation errors or a generic Unprocessable Entity' + } + }, + required: ['schema', 'extendedErrors'] + }, + handler: config => { + const validator = schemas.register(String(), String(), config.schema); + return req => { + const { isValid, error } = validator(req.body); + logger.warn(error); + return isValid; + }; + }, + middlewares: [express.json(), express.urlencoded({ extended: false })] +}; diff --git a/lib/conditions/predefined.js b/lib/conditions/predefined.js index 1e3fc3003..466f80611 100644 --- a/lib/conditions/predefined.js +++ b/lib/conditions/predefined.js @@ -1,4 +1,5 @@ const minimatch = require('minimatch'); +const jsonSchema = require('./json-schema'); const grabArray = config => { const { conditions } = require('./index'); @@ -190,6 +191,6 @@ module.exports = [ schema: { $id: 'http://express-gateway.io/schemas/conditions/tlsClientAuthenticated.json' } - } - + }, + jsonSchema ]; diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 441e679ef..4fd3d0ca4 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -126,7 +126,7 @@ function configurePipeline(pipelinePoliciesConfig, config) { policySteps.push({}); } - const middlewares = policySteps.map(policyStep => { + const middlewares = policySteps.flatMap(policyStep => { const conditionConfig = policyStep.condition; let condition; @@ -138,7 +138,7 @@ function configurePipeline(pipelinePoliciesConfig, config) { const action = Object.assign({}, policyStep.action, ActionParams.prototype); const policyMiddleware = policy(action, config); - return (req, res, next) => { + const fn = (req, res, next) => { if (!conditionConfig || condition(req)) { log.debug('request matched condition for action', policyStep.action, 'in policy', policyName); policyMiddleware(req, res, next); @@ -147,6 +147,9 @@ function configurePipeline(pipelinePoliciesConfig, config) { next(); } }; + + if (conditionConfig && conditionConfig.middlewares) { return [...middlewares, fn]; } + return fn; }); router.use(middlewares); From e6fb11e5a2b004ee51c127211b5cad5f1ea01062 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 17:32:43 +0100 Subject: [PATCH 15/20] add flatmap for node 8 and 10 --- lib/gateway/pipelines.js | 3 ++- package-lock.json | 7 ++++++- package.json | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/gateway/pipelines.js b/lib/gateway/pipelines.js index 4fd3d0ca4..689df27e3 100644 --- a/lib/gateway/pipelines.js +++ b/lib/gateway/pipelines.js @@ -1,4 +1,5 @@ const express = require('express'); +const flatMap = require('lodash.flatmap'); const vhost = require('vhost'); const log = require('../logger').gateway; const policies = require('../policies'); @@ -126,7 +127,7 @@ function configurePipeline(pipelinePoliciesConfig, config) { policySteps.push({}); } - const middlewares = policySteps.flatMap(policyStep => { + const middlewares = flatMap(policySteps, policyStep => { const conditionConfig = policyStep.condition; let condition; diff --git a/package-lock.json b/package-lock.json index b38b65ef7..82c74476c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4514,6 +4514,11 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" }, + "lodash.flatmap": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", + "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=" + }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", @@ -8048,7 +8053,7 @@ }, "trim-right": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "resolved": "", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, diff --git a/package.json b/package.json index 36c5d2121..e439013d7 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "json-schema-merge-allof": "^0.6.0", "json-schema-ref-parser": "6.1.0", "jsonwebtoken": "8.5.0", + "lodash.flatmap": "^4.5.0", "minimatch": "^3.0.4", "oauth2orize": "^1.11.0", "parent-require": "^1.0.0", From a4135e581ce8111cc4f18587fcb276e0c2627bb1 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sat, 16 Mar 2019 17:37:14 +0100 Subject: [PATCH 16/20] use logError to show stuff or not --- lib/conditions/json-schema.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/conditions/json-schema.js b/lib/conditions/json-schema.js index e872085a9..7989110c3 100644 --- a/lib/conditions/json-schema.js +++ b/lib/conditions/json-schema.js @@ -13,21 +13,25 @@ module.exports = { description: 'Json schema to validate against.', examples: ['{"type":"string"}'] }, - extendedErrors: { + logErrors: { type: 'boolean', default: false, - description: 'Value istructing the gateway to return the extended Schema Validation errors or a generic Unprocessable Entity' + description: 'Value istructing the gateway to report the errors on the logger or not' } }, - required: ['schema', 'extendedErrors'] + required: ['schema', 'logErrors'] }, handler: config => { const validator = schemas.register(String(), String(), config.schema); - return req => { - const { isValid, error } = validator(req.body); - logger.warn(error); - return isValid; - }; + if (config.logErrors) { + return req => { + const { isValid, error } = validator(req.body); + logger.warn(error); + return isValid; + }; + } + + return req => validator(req.body).isValid; }, middlewares: [express.json(), express.urlencoded({ extended: false })] }; From fe875dba94f3ba4b6fb1ffc0d9bb64dd36cff9d1 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sun, 17 Mar 2019 10:04:59 +0100 Subject: [PATCH 17/20] Compatibility with old plugins --- lib/gateway/index.js | 8 +++++++- package.json | 2 +- test/plugins/condition.test.js | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/gateway/index.js b/lib/gateway/index.js index ef9556b9a..f1fd75ba9 100644 --- a/lib/gateway/index.js +++ b/lib/gateway/index.js @@ -49,7 +49,7 @@ module.exports = function ({ plugins, config } = {}) { }); }; -function bootstrap ({ plugins, config } = {}) { +function bootstrap({ plugins, config } = {}) { const app = express(); app.set('x-powered-by', false); @@ -73,6 +73,12 @@ function bootstrap ({ plugins, config } = {}) { if (plugins && plugins.conditions && plugins.conditions.length) { plugins.conditions.forEach(cond => { log.debug('registering condition', cond.name); + if (cond.handler.length === 2) { + return conditionEngine.register({ + ...cond, + handler: config => req => cond.handler(req, config) + }); + } conditionEngine.register(cond); }); } diff --git a/package.json b/package.json index e439013d7..926765c9c 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "Apache-2.0", "main": "lib/index.js", "engines": { - "node": ">= 8.0.0" + "node": ">= 8.3.0" }, "scripts": { "start": "node lib/index.js", diff --git a/test/plugins/condition.test.js b/test/plugins/condition.test.js index 782a5bdcf..3e5f431da 100644 --- a/test/plugins/condition.test.js +++ b/test/plugins/condition.test.js @@ -91,7 +91,7 @@ describe('gateway condition schema with plugins', () => { }, required: ['param1'] }, - handler: conditionConfig => req => { + handler: (req, conditionConfig) => { should(conditionConfig.param1).be.ok(); should(req.url).be.eql('/test'); return (conditionConfig.param1 === req.url); From e0c4c124ee79113e71f133f5ad663216a66ddeed Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sun, 17 Mar 2019 10:09:05 +0100 Subject: [PATCH 18/20] move it in conditions --- lib/conditions/index.js | 4 ++++ lib/gateway/index.js | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/conditions/index.js b/lib/conditions/index.js index ed9ea75ea..28469bc12 100644 --- a/lib/conditions/index.js +++ b/lib/conditions/index.js @@ -11,6 +11,10 @@ function register({ name, handler, schema }) { conditions[name] = config => { const validationResult = validate(config); if (validationResult.isValid) { + if (handler.length === 2) { + return req => handler(req, config); + } + return handler(config); } diff --git a/lib/gateway/index.js b/lib/gateway/index.js index f1fd75ba9..ff83f6298 100644 --- a/lib/gateway/index.js +++ b/lib/gateway/index.js @@ -73,12 +73,6 @@ function bootstrap({ plugins, config } = {}) { if (plugins && plugins.conditions && plugins.conditions.length) { plugins.conditions.forEach(cond => { log.debug('registering condition', cond.name); - if (cond.handler.length === 2) { - return conditionEngine.register({ - ...cond, - handler: config => req => cond.handler(req, config) - }); - } conditionEngine.register(cond); }); } From cb374470b4d54a68fa390f62cd614b732b71d2ee Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sun, 17 Mar 2019 10:23:10 +0100 Subject: [PATCH 19/20] kill matchEgCondition --- lib/conditions/index.js | 15 -- lib/config/config.js | 20 ++- test/conditions.test.js | 291 +++++++++++++++------------------ test/plugins/condition.test.js | 14 +- 4 files changed, 149 insertions(+), 191 deletions(-) diff --git a/lib/conditions/index.js b/lib/conditions/index.js index 28469bc12..b5eae019f 100644 --- a/lib/conditions/index.js +++ b/lib/conditions/index.js @@ -1,4 +1,3 @@ -const express = require('express'); const chalk = require('chalk'); const logger = require('../logger').policy; const schemas = require('../schemas'); @@ -25,20 +24,6 @@ function register({ name, handler, schema }) { function init() { predefined.forEach(register); - - // extending express.request - express.request.matchEGCondition = function (conditionConfig) { - logger.debug(`matchEGCondition for ${conditionConfig.name}`); - const func = conditions[conditionConfig.name]; - - if (!func) { - logger.warn(`Condition not found for ${conditionConfig.name}`); - return null; - } - - return func(conditionConfig)(this); - }; - return { register }; } diff --git a/lib/config/config.js b/lib/config/config.js index e66d1f715..bd4d1438f 100644 --- a/lib/config/config.js +++ b/lib/config/config.js @@ -10,7 +10,7 @@ const eventBus = require('../eventBus'); const schemas = require('../schemas'); class Config { - constructor () { + constructor() { this.models = {}; this.configTypes = { @@ -29,7 +29,7 @@ class Config { }; } - loadConfig (type) { + loadConfig(type) { const configType = this.configTypes[type]; let configPath = this[configType.pathProperty] || path.join(process.env.EG_CONFIG_DIR, `${configType.baseFilename}.yml`); let config; @@ -59,9 +59,9 @@ class Config { log.debug(`ConfigPath: ${configPath}`); } - loadGatewayConfig () { this.loadConfig('gateway'); } + loadGatewayConfig() { this.loadConfig('gateway'); } - loadModels () { + loadModels() { ['users.json', 'credentials.json', 'applications.json'].forEach(model => { const module = path.resolve(process.env.EG_CONFIG_DIR, 'models', model); const name = path.basename(module, '.json'); @@ -71,7 +71,9 @@ class Config { }); } - watch () { + watch() { + if (typeof this.systemConfigPath !== 'string' || typeof this.gatewayConfigPath !== 'string') { return; } + const watchEvents = ['add', 'change']; const watchOptions = { @@ -96,15 +98,15 @@ class Config { }); } - unwatch () { + unwatch() { this.watcher && this.watcher.close(); } - updateGatewayConfig (modifier) { + updateGatewayConfig(modifier) { return this._updateConfigFile('gateway', modifier); } - _updateConfigFile (type, modifier) { + _updateConfigFile(type, modifier) { const configType = this.configTypes[type]; const path = this[configType.pathProperty]; @@ -166,7 +168,7 @@ class Config { // Kindly borrowed from https://github.com/macbre/optimist-config-file/blob/master/lib/envvar-replace.js // Thanks a lot guys 🙌 -function envReplace (str, vars) { +function envReplace(str, vars) { return str.replace(/\$?\$\{([A-Za-z0-9_]+)(:-(.*?))?\}/g, function (varStr, varName, _, defValue) { // Handle escaping: if (varStr.indexOf('$$') === 0) { diff --git a/test/conditions.test.js b/test/conditions.test.js index 0bc0b89dd..6c198c139 100644 --- a/test/conditions.test.js +++ b/test/conditions.test.js @@ -1,197 +1,164 @@ const EgContextBase = require('../lib/gateway/context'); -require('../lib/conditions').init(); +const { init, conditions } = require('../lib/conditions'); const express = require('express'); const should = require('should'); -describe('always', function () { - const req = Object.create(express.request); - it('should always return true', function () { - should(req.matchEGCondition({ name: 'always' })).be.true(); - }); -}); +describe('conditions', () => { + before(init); -describe('never', function () { - const req = Object.create(express.request); - it('should always return false', function () { - should(req.matchEGCondition({ name: 'never' })).be.false(); + describe('always', function () { + const req = Object.create(express.request); + it('should always return true', function () { + should(conditions['always']()(req)).be.true(); + }); }); -}); -describe('allOf', function () { - const req = Object.create(express.request); - it('should return true if all of the arguments is true', function () { - should(req.matchEGCondition({ - name: 'allOf', - conditions: [{ name: 'always' }, { name: 'always' }] - })).be.true(); - }); - it('should return false if one of the arguments is false', function () { - should(req.matchEGCondition({ - name: 'allOf', - conditions: [{ name: 'always' }, { name: 'never' }] - })).be.false(); + describe('never', function () { + const req = Object.create(express.request); + it('should always return false', function () { + should(conditions['never']()(req)).be.false(); + }); }); -}); -describe('oneOf', function () { - const req = Object.create(express.request); - it('should return true if one of the arguments is true', function () { - should(req.matchEGCondition({ - name: 'oneOf', - conditions: [{ name: 'never' }, { name: 'always' }] - })).be.true(); - }); - it('should return true if more than one of the arguments is true', - function () { - should(req.matchEGCondition({ - name: 'oneOf', - conditions: [{ name: 'always' }, { name: 'always' }] - })).be.true(); - }); - it('should return false if none of the arguments are true', function () { - should(req.matchEGCondition({ - name: 'oneOf', - conditions: [{ name: 'never' }, { name: 'never' }] - })).be.false(); + describe('allOf', function () { + const req = Object.create(express.request); + it('should return true if all of the arguments is true', function () { + should(conditions['allOf']({ conditions: [{ name: 'always' }, { name: 'always' }] })(req)).be.true(); + }); + it('should return false if one of the arguments is false', function () { + should(conditions['allOf']({ conditions: [{ name: 'always' }, { name: 'never' }] })(req)).be.false(); + }); }); -}); -describe('not', function () { - const req = Object.create(express.request); - it('should return true if the argument is false', function () { - should(req.matchEGCondition({ name: 'not', condition: { name: 'never' } })).be.true(); - }); - it('should return false if the argument is true', function () { - should(req.matchEGCondition({ name: 'not', condition: { name: 'always' } })).be.false(); + describe('oneOf', function () { + const req = Object.create(express.request); + it('should return true if one of the arguments is true', function () { + should(conditions['oneOf']({ conditions: [{ name: 'never' }, { name: 'always' }] })(req)).be.true(); + }); + it('should return true if more than one of the arguments is true', + function () { + should(conditions['oneOf']({ conditions: [{ name: 'always' }, { name: 'always' }] })(req)).be.true(); + }); + it('should return false if none of the arguments are true', function () { + should(conditions['oneOf']({ conditions: [{ name: 'never' }, { name: 'never' }] })(req)).be.false(); + }); }); -}); -describe('pathExact', function () { - const req = Object.create(express.request); - it('should return true if request url is the same', function () { - req.url = '/foo/bar/baz'; - should(req.matchEGCondition({ name: 'pathExact', path: '/foo/bar/baz' })).be.true(); - }); - it('should return false if request url is not the same', function () { - req.url = '/foo/bar'; - should(req.matchEGCondition({ name: 'pathExact', path: '/foo/bar/baz' })).be.false(); + describe('not', function () { + const req = Object.create(express.request); + it('should return true if the argument is false', function () { + should(conditions['not']({ condition: { name: 'never' } })(req)).be.true(); + }); + it('should return false if the argument is true', function () { + should(conditions['not']({ condition: { name: 'always' } })(req)).be.false(); + }); }); -}); -describe('pathMatch', function () { - const req = Object.create(express.request); - it('should return true if request url matches', function () { - req.url = '/foo/bar'; - should(req.matchEGCondition({ name: 'pathMatch', pattern: '(/(foo|bar|baz))+/?' })).be.true(); - }); - it('should return false if request url does not match', function () { - req.url = '/froo/brar'; - should(req.matchEGCondition({ name: 'pathMatch', pattern: '(/(foo|bar|baz))/?' })).be.false(); - }); -}); - -describe('expression', () => { - const req = Object.create(express.request); - req.egContext = Object.create(new EgContextBase()); - req.egContext.req = req; - it('should return false if expression does not match', function () { - req.url = 'test'; - should(req.matchEGCondition({ - name: 'expression', - expression: 'req.url.length>5' - })).be.false(); - }); - it('should pass if expression match', function () { - req.url = 'test_123'; - should(req.matchEGCondition({ - name: 'expression', - expression: 'req.url.length>5' - })).be.true(); + describe('pathExact', function () { + const req = Object.create(express.request); + it('should return true if request url is the same', function () { + req.url = '/foo/bar/baz'; + should(conditions['pathExact']({ path: '/foo/bar/baz' })(req)).be.true(); + }); + it('should return false if request url is not the same', function () { + req.url = '/foo/bar'; + should(conditions['pathExact']({ path: '/foo/bar/baz' })(req)).be.false(); + }); }); -}); -describe('method', function () { - const req = Object.create(express.request); - it('should return true if methods param is string and matches', function () { - req.method = 'GET'; - should(req.matchEGCondition({ - name: 'method', - methods: 'GET' - })).be.true(); + describe('pathMatch', function () { + const req = Object.create(express.request); + it('should return true if request url matches', function () { + req.url = '/foo/bar'; + should(conditions['pathMatch']({ pattern: '(/(foo|bar|baz))+/?' })(req)).be.true(); + }); + it('should return false if request url does not match', function () { + req.url = '/froo/brar'; + should(conditions['pathMatch']({ pattern: '(/(foo|bar|baz))/?' })(req)).be.false(); + }); }); - it('should return true if methods param is list and method is member', function () { - req.method = 'POST'; - should(req.matchEGCondition({ - name: 'method', - methods: ['GET', 'POST', 'PUT'] - })).be.true(); + describe('expression', () => { + const req = Object.create(express.request); + req.egContext = Object.create(new EgContextBase()); + req.egContext.req = req; + it('should return false if expression does not match', function () { + req.url = 'test'; + should(conditions['expression']({ expression: 'req.url.length>5' })(req)).be.false(); + }); + it('should pass if expression match', function () { + req.url = 'test_123'; + should(conditions['expression']({ expression: 'req.url.length>5' })(req)).be.true(); + }); }); - it('should return false if methods param is string and does not match', function () { - req.method = 'POST'; - should(req.matchEGCondition({ - name: 'method', - methods: 'GET' - })).be.false(); - }); + describe('method', function () { + const req = Object.create(express.request); + it('should return true if methods param is string and matches', function () { + req.method = 'GET'; + should(conditions['method']({ methods: 'GET' })(req)).be.true(); + }); - it('should return false if param is list and method is not member', - function () { - req.method = 'HEAD'; - should(req.matchEGCondition({ - name: 'method', - methods: ['GET', 'POST', 'PUT'] - })).be.false(); + it('should return true if methods param is list and method is member', function () { + req.method = 'POST'; + should(conditions['method']({ methods: ['GET', 'POST', 'PUT'] })(req)).be.true(); }); -}); -describe('tlsClientAuthenticated', function () { - const req = Object.create(express.request); + it('should return false if methods param is string and does not match', function () { + req.method = 'POST'; + should(conditions['method']({ methods: 'GET' })(req)).be.false(); + }); - it('should return true if request is client authenticated', function () { - req.client = { authorized: true }; - should(req.matchEGCondition({ - name: 'tlsClientAuthenticated' - })).be.true(); + it('should return false if param is list and method is not member', + function () { + req.method = 'HEAD'; + should(conditions['method']({ methods: ['GET', 'POST', 'PUT'] })(req)).be.false(); + }); }); - it('should return false if request is client authenticated', function () { - req.client.authorized = false; - should(req.matchEGCondition({ - name: 'tlsClientAuthenticated' - })).be.false(); + describe('tlsClientAuthenticated', function () { + const req = Object.create(express.request); + + it('should return true if request is client authenticated', function () { + req.client = { authorized: true }; + should(conditions['tlsClientAuthenticated']()(req)).be.true(); + }); + + it('should return false if request is client authenticated', function () { + req.client.authorized = false; + should(conditions['tlsClientAuthenticated']()(req)).be.false(); + }); }); -}); -describe('req.matchEGCondition', function () { - const req = Object.create(express.request); - it('correctly handles complex conditional rule', function () { - const control = { name: 'never' }; - const rule = { - name: 'allOf', - conditions: [{ - name: 'oneOf', - conditions: [ - { name: 'pathExact', path: '/foo/bar' }, - { name: 'not', condition: { name: 'always' } } - ] - }, - { - name: 'not', - condition: { + describe('req.matchEGCondition', function () { + const req = Object.create(express.request); + it('correctly handles complex conditional rule', function () { + const control = { name: 'never' }; + const rule = { + name: 'allOf', + conditions: [{ name: 'oneOf', conditions: [ - control, - { name: 'pathExact', path: '/path/path/path' } + { name: 'pathExact', path: '/foo/bar' }, + { name: 'not', condition: { name: 'always' } } ] + }, + { + name: 'not', + condition: { + name: 'oneOf', + conditions: [ + control, + { name: 'pathExact', path: '/path/path/path' } + ] + } } - } - ] - }; - req.url = '/foo/bar'; - should(req.matchEGCondition(rule)).be.true(); - control.name = 'always'; - should(req.matchEGCondition(rule)).be.false(); + ] + }; + req.url = '/foo/bar'; + should(conditions['allOf'](rule)(req)).be.true(); + control.name = 'always'; + should(conditions['allOf'](rule)(req)).be.false(); + }); }); }); diff --git a/test/plugins/condition.test.js b/test/plugins/condition.test.js index 3e5f431da..19d556232 100644 --- a/test/plugins/condition.test.js +++ b/test/plugins/condition.test.js @@ -1,4 +1,6 @@ const should = require('should'); + +const { conditions, init } = require('../../lib/conditions'); const gateway = require('../../lib/gateway'); const Config = require('../../lib/config/config'); const express = require('express'); @@ -34,6 +36,8 @@ config.gatewayConfig = { }; describe('gateway condition with plugins', () => { + before(init); + let gatewaySrv; before('fires up a new gateway instance', function () { return gateway({ @@ -57,13 +61,13 @@ describe('gateway condition with plugins', () => { it('should return false for param1 not matching url', function () { const req = Object.create(express.request); req.url = '/test'; - should(req.matchEGCondition({ name: 'test-condition', param1: true })).be.false(); + should(conditions['test-condition']({ param1: true })(req)).be.false(); }); it('should return true for param1 matching url', function () { const req = Object.create(express.request); req.url = '/test'; - should(req.matchEGCondition({ name: 'test-condition', param1: '/test' })).be.ok(); + should(conditions['test-condition']({ param1: '/test' })(req)).be.ok(); }); after('close gateway srv', () => { @@ -103,7 +107,7 @@ describe('gateway condition schema with plugins', () => { gatewaySrv = srv.app; const req = Object.create(express.request); req.url = '/test'; - should(req.matchEGCondition({ name: 'test-condition-1', param1: true })).be.false(); + should(conditions['test-condition-1']({ param1: true })(req)).be.false(); }); }); @@ -120,7 +124,7 @@ describe('gateway condition schema with plugins', () => { }, required: ['param2'] }, - handler: function () { + handler: function (req, config) { should.fail(); } }] @@ -130,7 +134,7 @@ describe('gateway condition schema with plugins', () => { gatewaySrv = srv.app; const req = Object.create(express.request); req.url = '/test'; - should.throws(() => req.matchEGCondition({ name: 'test-condition-2', param1: true })); + should.throws(() => conditions['test-condition-2']({ param1: true })(req)); }); }); }); From 69b39dbeb4f3cc824bf8d91e89c92465c3fb1938 Mon Sep 17 00:00:00 2001 From: Vincenzo Chianese Date: Sun, 17 Mar 2019 10:41:42 +0100 Subject: [PATCH 20/20] add test for json schema --- test/conditions.test.js | 59 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/test/conditions.test.js b/test/conditions.test.js index 6c198c139..736814bcf 100644 --- a/test/conditions.test.js +++ b/test/conditions.test.js @@ -130,7 +130,64 @@ describe('conditions', () => { }); }); - describe('req.matchEGCondition', function () { + describe('jsonSchema', function () { + const req = Object.create(express.request); + + it('should return true if the body matches the schema', function () { + req.body = { + name: 'Clark', + surname: 'Kent', + age: 30 + }; + + should(conditions['json-schema']({ + schema: { + $id: 'schema1', + type: 'object', + properties: { + name: { + type: 'string' + }, + surname: { + type: 'string' + }, + age: { + type: 'number' + } + }, + required: ['name', 'surname', 'age'] + } + })(req)).be.true(); + }); + + it('should return false if the body does not match the schema', function () { + req.body = { + name: 'Clark', + surname: 'Kent' + }; + + should(conditions['json-schema']({ + schema: { + $id: 'schema2', + type: 'object', + properties: { + name: { + type: 'string' + }, + surname: { + type: 'string' + }, + age: { + type: 'number' + } + }, + required: ['name', 'surname', 'age'] + } + })(req)).be.false(); + }); + }); + + describe('complex conditions', function () { const req = Object.create(express.request); it('correctly handles complex conditional rule', function () { const control = { name: 'never' };