diff --git a/.gitignore b/.gitignore index 342dfe828..2753fda9a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ example/keys/*.pem **/.DS_Store coverage .nyc_output +coverage.lcov diff --git a/lib/policies/basic-auth/auth.js b/lib/policies/basic-auth/auth.js index 6f87bc692..c863f0b5a 100644 --- a/lib/policies/basic-auth/auth.js +++ b/lib/policies/basic-auth/auth.js @@ -27,7 +27,6 @@ function authenticateBasic (req, clientId, clientSecret, done) { if (!consumer) { return done(null, false); } - return authService.authorizeCredential(clientId, credentialType, endpointScopes || requestedScopes) .then(authorized => { if (!authorized) { diff --git a/lib/services/auth.js b/lib/services/auth.js index 9619a733b..75d4f596c 100644 --- a/lib/services/auth.js +++ b/lib/services/auth.js @@ -23,7 +23,6 @@ s.authenticateCredential = function (id, password, type) { return this.validateConsumer(credential.consumerId, {checkUsername: true}); }); } - return this.validateConsumer(id, { checkUsername: true }) .then((consumer) => { if (!consumer) { diff --git a/package-lock.json b/package-lock.json index 0dca00435..d6b2c1645 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "acorn": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", - "integrity": "sha1-U/4WERH5EquZnuiHqQoLxSgi/XU=", + "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==", "dev": true }, "acorn-jsx": { @@ -100,7 +100,7 @@ "aproba": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", - "integrity": "sha1-RcZikJTeTpb2k+9+q3SuB5wkD8E=" + "integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw==" }, "are-we-there-yet": { "version": "1.1.4", @@ -136,7 +136,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=" + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, "array-differ": { "version": "1.0.0", @@ -833,7 +833,7 @@ "cpr": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cpr/-/cpr-2.2.0.tgz", - "integrity": "sha1-LcbIfft4ASSJzdmUInYowyDpoXs=", + "integrity": "sha512-q8UoWzIT9rslJKb3Y5CcByzR2zX7GBkVcoU6jJx02d/BgbE7zJ8Aix74i7bw3iYk58TrgXhmB2XB0aGaBd7oZA==", "dev": true, "requires": { "graceful-fs": "4.1.11", @@ -1104,7 +1104,7 @@ "diff": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.0.tgz", - "integrity": "sha1-BWaVFQ16qTI3yn43isOxaCt5Y7k=" + "integrity": "sha512-w0XZubFWn0Adlsapj9EAWX0FqWdO4tz8kc3RiYdWLh4k/V8PTb6i0SMgXt0vRM3zyKnT8tKO7mUlieRQHIjMNg==" }, "doctrine": { "version": "2.0.0", @@ -1517,7 +1517,7 @@ "eslint-module-utils": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", - "integrity": "sha1-q67IJBd2E7ipWymWOeG2+s9HNEk=", + "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", "dev": true, "requires": { "debug": "2.6.8", @@ -1815,7 +1815,7 @@ "iconv-lite": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", - "integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI=" + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" } } }, @@ -2100,7 +2100,7 @@ "fsevents": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", - "integrity": "sha1-MoK3E/s62A7eDp/PRhG1qm/AM/Q=", + "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", "optional": true, "requires": { "nan": "2.6.2", @@ -2993,7 +2993,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -3006,7 +3006,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "1.1.8" } @@ -3033,7 +3033,7 @@ "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, "globby": { @@ -3206,7 +3206,7 @@ "hosted-git-info": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha1-bWDjSzq7yDEwYsO3mO+NkBoHrzw=" + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==" }, "html-wiring": { "version": "1.2.0", @@ -4139,7 +4139,7 @@ "lru-cache": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", "requires": { "pseudomap": "1.0.2", "yallist": "2.1.2" @@ -4578,7 +4578,7 @@ "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "requires": { "hosted-git-info": "2.5.0", "is-builtin-module": "1.0.0", @@ -4605,7 +4605,7 @@ "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "1.1.4", "console-control-strings": "1.1.0", @@ -6942,7 +6942,7 @@ "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "requires": { "is-number": "3.0.0", "kind-of": "4.0.0" @@ -7049,7 +7049,7 @@ "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -7229,7 +7229,7 @@ "uuid": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=" + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" } } }, @@ -7337,7 +7337,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "samsam": { "version": "1.2.1", @@ -7663,7 +7663,7 @@ "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "requires": { "safe-buffer": "5.1.1" } @@ -8290,7 +8290,7 @@ "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "requires": { "define-properties": "1.1.2", "object.getownpropertydescriptors": "2.0.3" @@ -8411,7 +8411,7 @@ "wide-align": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha1-Vx4PGwYEY268DfwhsDObvjE0FxA=", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "requires": { "string-width": "1.0.2" } @@ -8648,7 +8648,7 @@ "async": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", "requires": { "lodash": "4.17.4" } @@ -8829,7 +8829,7 @@ "async": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha1-hDGQ/WtzV6C54clW7d3V7IRitU0=", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", "dev": true, "requires": { "lodash": "4.17.4" @@ -9056,7 +9056,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "1.1.8" @@ -9280,7 +9280,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "1.1.8" diff --git a/package.json b/package.json index 65a02ffd3..e0a3c058f 100644 --- a/package.json +++ b/package.json @@ -31,14 +31,14 @@ }, "scripts": { "start": "node lib/index.js", - "getting-started": "node lib/index.js emulate", "start-dev": "LOG_LEVEL=debug node lib/index.js", "lint": "eslint --fix .", "clean": "rimraf dist/", "pretest": "eslint .", "test": "npm run mocha-istanbul", - "test-all": "cross-env EG_CONFIG_DIR=test/config EG_DISABLE_CONFIG_WATCH=true mocha --recursive test --timeout 20000", - "dev-test": "cross-env EG_CONFIG_DIR=test/config mocha EG_DISABLE_CONFIG_WATCH=true --recursive test", + "test-all": "cross-env EG_CONFIG_DIR=test/config EG_DISABLE_CONFIG_WATCH=true mocha --recursive test --timeout 60000", + "test:unit": "cross-env EG_CONFIG_DIR=test/config EG_DISABLE_CONFIG_WATCH=true mocha --recursive \"./test/{,!(e2e)/**/}*.test.js\" --timeout 5000", + "test:e2e": "mocha --recursive test/e2e --timeout 60000", "mocha-istanbul": "nyc --reporter=lcov npm run test-all && nyc report --report=lcov > coverage.lcov && codecov" }, "bin": { diff --git a/test/common/cli.helper.js b/test/common/cli.helper.js new file mode 100644 index 000000000..be0f5f70c --- /dev/null +++ b/test/common/cli.helper.js @@ -0,0 +1,57 @@ +const path = require('path'); +const { exec } = require('child_process'); +const os = require('os'); +const fs = require('fs'); +const uuid = require('node-uuid'); + +const modulePath = path.resolve(__dirname, '..', '..', 'bin', 'index.js'); + +module.exports.bootstrapFolder = function (options) { + let tempDir = path.join(os.tmpdir(), uuid.v4()); + fs.mkdirSync(tempDir); + let execOptions = { + env: Object.assign({}, process.env) + }; + let cmd = modulePath + ' gateway create -t getting-started -n test -d ' + tempDir; + return new Promise((resolve, reject) => { + exec(cmd, execOptions, function (error, stdout, stderr) { + if (error !== null) { + reject(error); + } + resolve({ + basePath: tempDir, + configDirectoryPath: path.join(tempDir, 'config'), + gatewayConfigPath: path.join(tempDir, 'config', 'gateway.config.yml'), + systemConfigPath: path.join(tempDir, 'config', 'system.config.yml') + }); + }); + }); +}; + +module.exports.runCLICommand = function ({adminPort, adminUrl, configDirectoryPath, cliArgs}) { + // TODO: it should not depend on configFolder, API only, now the last dependency is models + let cliExecOptions = Object.assign({}, { + env: process.env + }); + cliExecOptions.env.EG_CONFIG_DIR = configDirectoryPath; + cliExecOptions.env.EG_ADMIN_URL = adminUrl || `http://localhost:${adminPort}`; + const command = [modulePath].concat(cliArgs).join(' '); + return new Promise((resolve, reject) => { + exec(command, cliExecOptions, (err, stdout, stderr) => { + if (err) { + reject(err); + return; + } + try { + const obj = JSON.parse(stdout); + resolve(obj); + } catch (err) { + if (err instanceof SyntaxError) { + resolve(stdout); + } else { + reject(err); + } + } + }); + }); +}; diff --git a/test/common/gateway.helper.js b/test/common/gateway.helper.js new file mode 100644 index 000000000..d0909ec78 --- /dev/null +++ b/test/common/gateway.helper.js @@ -0,0 +1,81 @@ +const yaml = require('js-yaml'); +const fs = require('fs'); +const { fork } = require('child_process'); +const path = require('path'); +const request = require('superagent'); +const { generateBackendServer, findOpenPortNumbers } = + require('../common/server-helper'); +let gatewayPort = null; +let adminPort = null; +let backendPort = null; + +// Set gateway.config or system.config yml files +module.exports.setYmlConfig = function ({ymlConfigPath, newConfig}) { + fs.writeFileSync(ymlConfigPath, yaml.dump(newConfig)); +}; + +// Get config by path (gateway.config.yml or system.config.yml) +module.exports.getYmlConfig = function ({ymlConfigPath}) { + let content = fs.readFileSync(); + return yaml.load(content); +}; + +module.exports.startGatewayInstance = function ({dirInfo, gatewayConfig}) { + return findOpenPortNumbers(4) + .then(ports => { + gatewayPort = ports[0]; + backendPort = ports[1]; + adminPort = ports[2]; + + gatewayConfig.http = {port: gatewayPort}; + gatewayConfig.admin = {port: adminPort}; + gatewayConfig.serviceEndpoints = gatewayConfig.serviceEndpoints || {}; + gatewayConfig.serviceEndpoints.backend = {url: `http://localhost:${backendPort}`}; + return this.setYmlConfig({ + ymlConfigPath: dirInfo.gatewayConfigPath, + newConfig: gatewayConfig + }); + }) + .then(() => { + return generateBackendServer(backendPort); + }) + .then(() => { + return new Promise((resolve, reject) => { + const childEnv = Object.assign({}, process.env); + childEnv.EG_CONFIG_DIR = dirInfo.configDirectoryPath; + + // Tests, by default have config watch disabled. + // Need to remove this paramter in the child process. + delete childEnv.EG_DISABLE_CONFIG_WATCH; + + const modulePath = path.join(__dirname, '..', '..', + 'lib', 'index.js'); + let gatewayProcess = fork(modulePath, [], { + cwd: dirInfo.basePath, + env: childEnv + }); + + gatewayProcess.on('error', err => { + reject(err); + }); + let count = 0; + let interval = setInterval(() => { + count++; // Waiting for process to start, ignoring conn refused errors + request + .get(`http://localhost:${gatewayPort}/not-found`) + .end((err, res) => { + if (err && res && res.statusCode === 404) { + clearInterval(interval); + resolve({gatewayProcess, gatewayPort, adminPort, backendPort, dirInfo}); + } else { + if (count >= 25) { + gatewayProcess.kill(); + clearInterval(interval); + reject(new Error('Failed to start Express Gateway')); + } + } + }); + }, 300); + }); + }); +}; diff --git a/test/common/server-helper.js b/test/common/server-helper.js index 4d24e4944..2cf1b18c6 100644 --- a/test/common/server-helper.js +++ b/test/common/server-helper.js @@ -19,32 +19,33 @@ const generateBackendServer = port => { }); }; -const findOpenPortNumbers = (count, cb) => { +const findOpenPortNumbers = function (count = 1) { let completeCount = 0; const ports = []; + return new Promise((resolve, reject) => { + for (let i = 0; i < count; i++) { + const server = net.createServer(); - for (let i = 0; i < count; i++) { - const server = net.createServer(); + server.listen(0); - server.listen(0); + server.on('listening', () => { + ports.push(server.address().port); - server.on('listening', () => { - ports.push(server.address().port); + server.once('close', () => { + completeCount++; - server.once('close', () => { - completeCount++; - - if (completeCount === count) { - cb(null, ports); - } + if (completeCount === count) { + resolve(ports); + } + }); + server.close(); }); - server.close(); - }); - server.on('error', (err) => { - cb(err); - }); - } + server.on('error', (err) => { + reject(err); + }); + } + }); }; module.exports = { diff --git a/test/e2e/basic-auth.e2e.test.js b/test/e2e/basic-auth.e2e.test.js new file mode 100644 index 000000000..fc729d90c --- /dev/null +++ b/test/e2e/basic-auth.e2e.test.js @@ -0,0 +1,108 @@ +const cliHelper = require('../common/cli.helper'); +const gwHelper = require('../common/gateway.helper'); +const request = require('supertest'); +let gatewayProcess = null; +let gatewayPort, adminPort, configDirectoryPath; +let username = 'test'; +let proxyPolicy = { + proxy: { action: { serviceEndpoint: 'backend' } } +}; +describe('E2E: basic-auth Policy', () => { + before('setup', () => { + let gatewayConfig = { + apiEndpoints: { + authorizedEndpoint: { + host: '*', + paths: ['/authorizedPath'], + scopes: [ 'authorizedScope' ] + }, + unauthorizedEndpoint: { + host: '*', + paths: ['/unauthorizedPath'], + scopes: ['unauthorizedScope'] + } + }, + policies: ['basic-auth', 'proxy'], + pipelines: { + pipeline1: { + apiEndpoint: 'authorizedEndpoint', + policies: [ + { 'basic-auth': {} }, proxyPolicy + ] + }, + pipeline2: { + apiEndpoint: 'unauthorizedEndpoint', + policies: [ + { 'basic-auth': {} }, proxyPolicy + ] + } + } + }; + return cliHelper.bootstrapFolder().then(dirInfo => { + return gwHelper.startGatewayInstance({dirInfo, gatewayConfig}); + }).then(gwInfo => { + gatewayProcess = gwInfo.gatewayProcess; + gatewayPort = gwInfo.gatewayPort; + adminPort = gwInfo.adminPort; + configDirectoryPath = gwInfo.dirInfo.configDirectoryPath; + + return cliHelper.runCLICommand({ + cliArgs: ['scopes create', 'authorizedScope', 'unauthorizedScope'], + adminPort, + configDirectoryPath}); + }).then((scopes) => { + const args = [ + '-p', `username=${username}`, + '-p', 'firstname=Kate', + '-p', 'lastname=Smith' + ]; + return cliHelper.runCLICommand({ + cliArgs: ['users create '].concat(args), + adminPort, + configDirectoryPath}); + }).then(newUser => { + return cliHelper.runCLICommand({ + cliArgs: ['credentials create -t basic-auth -p "scopes=authorizedScope" -p "password=pass" -c ', username], + adminPort, + configDirectoryPath}); + }); + }); + + after('cleanup', (done) => { + gatewayProcess.kill(); + done(); + }); + + it('should not authenticate token for requests without token header', function () { + return request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .expect(401); + }); + + it('should not authenticate token for requests if requester doesn\'t have authorized scopes', function () { + let credentials = Buffer.from(username.concat(':pass')).toString('base64'); + + return request(`http://localhost:${gatewayPort}`) + .get('/unauthorizedPath') + .set('Authorization', 'basic ' + credentials) + .expect(401); + }); + + it('should authenticate token for requests with scopes if requester is authorized', function () { + let credentials = Buffer.from(username.concat(':pass')).toString('base64'); + + return request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .set('Authorization', 'basic ' + credentials) + .expect(200); + }); + + it('should not authenticate invalid token', function () { + let credentials = Buffer.from(username.concat(':wrongPassword')).toString('base64'); + + request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .set('Authorization', 'basic ' + credentials) + .expect(401); + }); +}); diff --git a/test/hot-reload.test.js b/test/e2e/hot-reload.test.js similarity index 93% rename from test/hot-reload.test.js rename to test/e2e/hot-reload.test.js index 8f5a48ddc..71cccbc82 100644 --- a/test/hot-reload.test.js +++ b/test/e2e/hot-reload.test.js @@ -10,7 +10,7 @@ const rimraf = require('rimraf'); const tmp = require('tmp'); const yaml = require('js-yaml'); -const { findOpenPortNumbers } = require('./common/server-helper'); +const { findOpenPortNumbers } = require('../common/server-helper'); /* 1) Copy config to a temp directory. @@ -22,7 +22,7 @@ const { findOpenPortNumbers } = require('./common/server-helper'); 7) Clean up the temp directory. */ -const baseConfigDirectory = path.join(__dirname, 'fixtures', 'hot-reload'); +const baseConfigDirectory = path.join(__dirname, '..', 'fixtures', 'hot-reload'); describe('hot-reload', () => { describe('gateway config', () => { @@ -45,7 +45,7 @@ describe('hot-reload', () => { testGatewayConfigPath = path.join(tempPath, 'gateway.config.yml'); - findOpenPortNumbers(3, (err, ports) => { + findOpenPortNumbers(3).then(ports => { if (err) { throw err; } @@ -77,7 +77,7 @@ describe('hot-reload', () => { // Need to remove this paramter in the child process. delete childEnv.EG_DISABLE_CONFIG_WATCH; - const modulePath = path.join(__dirname, '..', 'lib', 'index.js'); + const modulePath = path.join(__dirname, '../..', 'lib', 'index.js'); childProcess = fork(modulePath, [], { cwd: tempPath, env: childEnv @@ -99,7 +99,7 @@ describe('hot-reload', () => { }, 5000); }); }); - }); + }).catch(err => done(err)); }); }); }); diff --git a/test/e2e/key-auth.e2e.test.js b/test/e2e/key-auth.e2e.test.js new file mode 100644 index 000000000..7c0395dc5 --- /dev/null +++ b/test/e2e/key-auth.e2e.test.js @@ -0,0 +1,180 @@ +const cliHelper = require('../common/cli.helper'); +const gwHelper = require('../common/gateway.helper'); +const request = require('supertest'); +let gatewayProcess = null; +let gatewayPort, adminPort, configDirectoryPath; +let username = 'test'; +let keyCred; +const headerName = 'Authorization'; +let proxyPolicy = { + proxy: { action: { serviceEndpoint: 'backend' } } +}; +describe('E2E: key-auth Policy', () => { + before('setup', () => { + let gatewayConfig = { + apiEndpoints: { + authorizedEndpoint: { + host: '*', + paths: ['/authorizedPath'], + scopes: ['authorizedScope'] + }, + onlyQueryParamEndpoint: { + host: '*', + paths: ['/by_query'] + }, + unauthorizedEndpoint: { + host: '*', + paths: ['/unauthorizedPath'], + scopes: ['unauthorizedScope'] + } + }, + policies: ['key-auth', 'proxy'], + pipelines: { + pipeline1: { + apiEndpoints: ['authorizedEndpoint'], + policies: [{ + 'key-auth': { + action: { + apiKeyHeader: 'TEST_HEADER', + apiKeyHeaderScheme: 'SCHEME1' + } + } + }, + proxyPolicy + ] + }, + pipeline2: { + apiEndpoints: ['unauthorizedEndpoint'], + policies: [{ + 'key-auth': {} + }, + proxyPolicy + ] + }, + pipeline_by_query: { + apiEndpoints: ['onlyQueryParamEndpoint'], + policies: [{ + 'key-auth': [{ + action: { + apiKeyField: 'customApiKeyParam', + disableHeaders: true + } + }] + }, + proxyPolicy + ] + } + } + }; + return cliHelper.bootstrapFolder().then(dirInfo => { + return gwHelper.startGatewayInstance({dirInfo, gatewayConfig}); + }).then(gwInfo => { + gatewayProcess = gwInfo.gatewayProcess; + gatewayPort = gwInfo.gatewayPort; + adminPort = gwInfo.adminPort; + configDirectoryPath = gwInfo.dirInfo.configDirectoryPath; + + return cliHelper.runCLICommand({ + cliArgs: ['scopes create', 'authorizedScope', 'unauthorizedScope'], + adminPort, + configDirectoryPath}); + }).then((scopes) => { + const args = [ + '-p', `username=${username}`, + '-p', 'firstname=Kate', + '-p', 'lastname=Smith' + ]; + return cliHelper.runCLICommand({ + cliArgs: ['users create '].concat(args), + adminPort, + configDirectoryPath}); + }).then(newUser => { + return cliHelper.runCLICommand({ + cliArgs: ['credentials create -t key-auth -p "scopes=authorizedScope" -c ', newUser.id], + adminPort, + configDirectoryPath}); + }).then(cred => { + keyCred = cred; + }); + }); + + after('cleanup', (done) => { + gatewayProcess.kill(); + done(); + }); + + it('should not authenticate key for requests without authorization header', function () { + return request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .expect(401); + }); + + it('should not authorise key for requests if requester doesn\'t have authorized scopes', function (done) { + let apikey = 'apiKey ' + keyCred.keyId + ':' + keyCred.keySecret; + + request(`http://localhost:${gatewayPort}`) + .get('/unauthorizedPath') + .set(headerName, apikey) + .expect(403) + .end(function (err) { + done(err); + }); + }); + + it('should authenticate key with scheme in headers for requests with scopes if requester is authorized', function (done) { + let apikey = 'SCHEME1 ' + keyCred.keyId + ':' + keyCred.keySecret; + + request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .set('TEST_HEADER', apikey) + .expect(200) + .end(done); + }); + it('should authenticate key with scheme ignoring case in headers for requests with scopes if requester is authorized', function (done) { + let apikey = 'scheME1 ' + keyCred.keyId + ':' + keyCred.keySecret; + + request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .set('TEST_HEADER', apikey) + .expect(200) + .end(done); + }); + it('should authenticate key in query for requests with scopes if requester is authorized ', function (done) { + let apikey = keyCred.keyId + ':' + keyCred.keySecret; + + request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath?apiKey=' + apikey) + .expect(200) + .end(done); + }); + + it('should not authorize invalid key', function (done) { + let apikey = 'apiKey test:wrong'; + + request(`http://localhost:${gatewayPort}`) + .get('/authorizedPath') + .set(headerName, apikey) + .expect(401) + .end(done); + }); + + it('should authenticate key in query if endpoint allows only query ', function (done) { + let apikey = keyCred.keyId + ':' + keyCred.keySecret; + + request(`http://localhost:${gatewayPort}`) + .get('/by_query?customApiKeyParam=' + apikey) + .expect(200) + .end(done); + }); + it('should not authenticate with header of EP allows only query', function (done) { + let apikey = 'apiKey ' + keyCred.keyId + ':' + keyCred.keySecret; + + request(`http://localhost:${gatewayPort}`) + .get('/by_query') + .set(headerName, apikey) + .expect(401) + .end(function (err) { + done(err); + }); + }); +}); diff --git a/test/e2e/oauth2-authorization-code.js b/test/e2e/oauth2-authorization-code.js index 269be3f95..2d47fdc08 100644 --- a/test/e2e/oauth2-authorization-code.js +++ b/test/e2e/oauth2-authorization-code.js @@ -1,4 +1,5 @@ -const { exec, fork } = require('child_process'); +const { fork } = require('child_process'); +const {runCLICommand} = require('../common/cli.helper'); const fs = require('fs'); const path = require('path'); const url = require('url'); @@ -13,6 +14,7 @@ const rimraf = require('rimraf'); const tmp = require('tmp'); const webdriver = require('selenium-webdriver'); const yaml = require('js-yaml'); +let tempPath = null; require('util.promisify/shim')(); @@ -171,8 +173,6 @@ describe('oauth2 authorization code grant type', () => { }); function startGatewayInstance (done) { - let tempPath = null; - return util.promisify(tmp.dir)() .then(temp => { tempPath = temp; @@ -180,7 +180,7 @@ describe('oauth2 authorization code grant type', () => { }) .then(files => { testGatewayConfigPath = path.join(tempPath, 'gateway.config.yml'); - return util.promisify(findOpenPortNumbers)(4); + return findOpenPortNumbers(4); }) .then(ports => { gatewayPort = ports[0]; @@ -235,49 +235,33 @@ describe('oauth2 authorization code grant type', () => { assert(res.statusCode, 404); resolve(); }); - }, 2000); + }, 4000); }); }); } - function runCLICommand (args) { - return new Promise((resolve, reject) => { - const childEnv = Object.assign({}, process.env); - childEnv.EG_CONFIG_DIR = path.join(testGatewayConfigPath, '..'); - - const modulePath = path.join(__dirname, '..', '..', 'bin', 'index.js'); - - const command = [process.argv[0], modulePath].concat(args).join(' '); - exec(command, { env: childEnv }, (err, stdout) => { - if (err) { - reject(err); - return; - } - - try { - const obj = JSON.parse(stdout); - resolve(obj); - } catch (err) { - if (err instanceof SyntaxError) { - resolve(stdout); - } else { - reject(err); - } - } - }); - }); - } - function createUser (args, done) { - return runCLICommand(['users', 'create'].concat(args)); + return runCLICommand({ + cliArgs: ['users', 'create'].concat(args), + adminPort, + configDirectoryPath: tempPath + }); } function createCredential (args, done) { - return runCLICommand(['credentials', 'create'].concat(args)); + return runCLICommand({ + cliArgs: ['credentials', 'create'].concat(args), + adminPort, + configDirectoryPath: tempPath + }); } function createApp (args, done) { - return runCLICommand(['apps', 'create'].concat(args)); + return runCLICommand({ + cliArgs: ['apps', 'create'].concat(args), + adminPort, + configDirectoryPath: tempPath + }); } function generateRedirectServer (port) { diff --git a/test/e2e/policy-seq-oauth2-expression-log-ratelimit-proxy.js b/test/e2e/policy-seq-oauth2-expression-log-ratelimit-proxy.js index 78238ba77..c1fe35b2b 100644 --- a/test/e2e/policy-seq-oauth2-expression-log-ratelimit-proxy.js +++ b/test/e2e/policy-seq-oauth2-expression-log-ratelimit-proxy.js @@ -23,7 +23,7 @@ let testHelper = require('../common/routing.helper'); let config = require('../../lib/config'); let originalGatewayConfig = config.gatewayConfig; -describe('End to End tests with oauth2, proxy, log, expression, rate-limit policies', () => { +describe('E2E: oauth2, proxy, log, expression, rate-limit policies', () => { let helper = testHelper(); let spy = sinon.spy(); let originalAppConfig, originalCredentialConfig, originalUserConfig; diff --git a/test/e2e/round-robin.test.js b/test/e2e/round-robin.test.js index 605b5ceb7..8e75710bb 100644 --- a/test/e2e/round-robin.test.js +++ b/test/e2e/round-robin.test.js @@ -35,11 +35,7 @@ describe('round-robin load balancing', () => { testGatewayConfigPath = path.join(tempPath, 'gateway.config.yml'); - findOpenPortNumbers(4, (err, ports) => { - if (err) { - throw err; - } - + findOpenPortNumbers(4).then(ports => { fs.readFile(testGatewayConfigPath, (err, configData) => { if (err) { throw err; @@ -100,7 +96,7 @@ describe('round-robin load balancing', () => { }); }); }); - }); + }).catch(err => done(err)); }); }); }); diff --git a/test/policies/basic-auth-policy.test.js b/test/policies/basic-auth-policy.test.js index d71e02499..5c55d9b27 100644 --- a/test/policies/basic-auth-policy.test.js +++ b/test/policies/basic-auth-policy.test.js @@ -86,10 +86,7 @@ describe('Functional Tests basic auth Policy', () => { }; db.flushdbAsync() - .then(function (didSucceed) { - if (!didSucceed) { - console.log('Failed to flush the database'); - } + .then(function () { let user1 = { username: 'irfanbaqui', firstname: 'irfan', @@ -135,52 +132,36 @@ describe('Functional Tests basic auth Policy', () => { done(); }); - it('should not authenticate token for requests without token header', function (done) { - request(app) + it('should not authenticate token for requests without token header', function () { + return request(app) .get('/authorizedPath') - .expect(401) - .end(function (err) { - should.not.exist(err); - done(); - }); + .expect(401); }); - it('should not authenticate token for requests if requester doesn\'t have authorized scopes', function (done) { + it('should not authenticate token for requests if requester doesn\'t have authorized scopes', function () { let credentials = Buffer.from(user.id.concat(':user-secret')).toString('base64'); - request(app) + return request(app) .get('/unauthorizedPath') .set('Authorization', 'basic ' + credentials) - .expect(401) - .end(function (err) { - should.not.exist(err); - done(); - }); + .expect(401); }); - it('should authenticate token for requests with scopes if requester is authorized', function (done) { + it('should authenticate token for requests with scopes if requester is authorized', function () { let credentials = Buffer.from(user.username.concat(':user-secret')).toString('base64'); - request(app) + return request(app) .get('/authorizedPath') .set('Authorization', 'basic ' + credentials) - .expect(200) - .end(function (err) { - should.not.exist(err); - done(); - }); + .expect(200); }); - it('should not authenticate invalid token', function (done) { + it('should not authenticate invalid token', function () { let credentials = Buffer.from(user.id.concat(':wrongPassword')).toString('base64'); - request(app) + return request(app) .get('/authorizedPath') .set('Authorization', 'basic ' + credentials) - .expect(401) - .end(function (err) { - should.not.exist(err); - done(); - }); + .expect(401); }); });