From c1697949863a6c1379759085c70094d5b1324bc0 Mon Sep 17 00:00:00 2001 From: scottinet Date: Thu, 21 Mar 2019 11:58:19 +0100 Subject: [PATCH 1/2] [auth] add support for the new refreshToken API route --- features/features | 2 +- features/steps/auth.js | 42 +++++++- features/support/world.js | 2 + package-lock.json | 180 ++++++++++++++++++---------------- package.json | 1 + src/controllers/auth.js | 24 ++++- test/controllers/auth.test.js | 41 ++++++++ 7 files changed, 200 insertions(+), 92 deletions(-) diff --git a/features/features b/features/features index 1512d35db..2fb7bfe97 160000 --- a/features/features +++ b/features/features @@ -1 +1 @@ -Subproject commit 1512d35dbc13a9b043a8f3346847496d413082ca +Subproject commit 2fb7bfe97b7d344a4a396261ecaa55f3b24c612b diff --git a/features/steps/auth.js b/features/steps/auth.js index 8a691e3d1..1e0b2aff6 100644 --- a/features/steps/auth.js +++ b/features/steps/auth.js @@ -1,5 +1,7 @@ -const {Given, When, Then} = require('cucumber'); -const should = require('should'); +const + {Given, When, Then} = require('cucumber'), + should = require('should'), + retry = require('retry'); Given('I log in as {string}:{string}', async function (username, password) { try { @@ -15,11 +17,44 @@ Given('I log in as {string}:{string}', async function (username, password) { } }); - When('I logout', async function () { await this.kuzzle.auth.logout(); }); +When('I refresh the JWT', function (cb) { + this.previousJwt = this.jwt; + + // we have to wait for at least 1s: if we ask Kuzzle to refresh a JWT that + // has been generated during the same second, then the new JWT will be + // identical to the one being refreshed + setTimeout(async () => { + const token = await this.kuzzle.auth.refreshToken(); + this.jwt = token.jwt; + cb(); + }, 1000); +}); + +Then('the previous JWT is now invalid', function (cb) { + // prevent false positives, just in case + should(this.previousJwt).be.a.String().and.not.empty(); + should(this.previousJwt).not.eql(this.jwt); + + const op = retry.operation({retries: 10, minTimeout: 500, factor: 1}); + + op.attempt(() => { + this.kuzzle.auth.checkToken(this.previousJwt) + .then(response => { + const err = response.valid ? new Error('Unexpected valid token') : null; + + if (op.retry(err)) { + return; + } + + cb(err); + }) + .catch(err => cb(err)); + }); +}); Then(/^the JWT is (in)?valid$/, async function (not) { const response = await this.kuzzle.auth.checkToken(this.jwt); @@ -31,4 +66,3 @@ Then(/^the JWT is (in)?valid$/, async function (not) { should(response.valid).be.true(); } }); - diff --git a/features/support/world.js b/features/support/world.js index 665ad77c7..e6526ed20 100644 --- a/features/support/world.js +++ b/features/support/world.js @@ -14,6 +14,8 @@ class World { this.ids = []; this.user = null; this.jwt = null; + this.rights = null; + this.previousJwt = null; this.content = null; this.error = null; diff --git a/package-lock.json b/package-lock.json index ea5bb171a..b9bb42b6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1546,7 +1546,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "requires": { "buffer-xor": "^1.0.3", @@ -1580,7 +1580,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { "bn.js": "^4.1.0", @@ -1621,7 +1621,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { "base64-js": "^1.0.2", @@ -1733,7 +1733,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -1943,7 +1943,7 @@ }, "commander": { "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -2034,7 +2034,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "requires": { "cipher-base": "^1.0.1", @@ -2046,7 +2046,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "requires": { "cipher-base": "^1.0.3", @@ -2283,7 +2283,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "requires": { "bn.js": "^4.1.0", @@ -2622,7 +2622,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -3079,7 +3079,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3091,8 +3092,8 @@ "bundled": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { @@ -3105,7 +3106,7 @@ "bundled": true, "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -3162,7 +3163,7 @@ "bundled": true, "optional": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -3175,14 +3176,14 @@ "bundled": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -3190,12 +3191,12 @@ "bundled": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -3224,8 +3225,8 @@ "bundled": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -3269,8 +3270,8 @@ "bundled": true, "optional": true, "requires": { - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } }, "minizlib": { @@ -3278,7 +3279,7 @@ "bundled": true, "optional": true, "requires": { - "minipass": "2.3.5" + "minipass": "^2.2.1" } }, "mkdirp": { @@ -3309,16 +3310,16 @@ "bundled": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.4", - "nopt": "4.0.1", - "npm-packlist": "1.2.0", - "npmlog": "4.1.2", - "rc": "1.2.8", - "rimraf": "2.6.3", - "semver": "5.6.0", - "tar": "4.4.8" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -3349,10 +3350,10 @@ "bundled": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -3370,7 +3371,7 @@ "bundled": true, "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -3407,10 +3408,10 @@ "bundled": true, "optional": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -3425,13 +3426,13 @@ "bundled": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -3439,7 +3440,7 @@ "bundled": true, "optional": true, "requires": { - "glob": "7.1.3" + "glob": "^7.1.3" } }, "safe-buffer": { @@ -3477,9 +3478,9 @@ "bundled": true, "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -3487,14 +3488,15 @@ "bundled": true, "optional": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -3507,13 +3509,13 @@ "bundled": true, "optional": true, "requires": { - "chownr": "1.1.1", - "fs-minipass": "1.2.5", - "minipass": "2.3.5", - "minizlib": "1.2.1", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.2", - "yallist": "3.0.3" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -3526,16 +3528,18 @@ "bundled": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -4210,7 +4214,7 @@ }, "json5": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, "just-extend": { @@ -4515,7 +4519,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mississippi": { @@ -4556,7 +4560,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -6382,7 +6386,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -6557,6 +6561,12 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, "rewire": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/rewire/-/rewire-4.0.1.tgz", @@ -6568,7 +6578,7 @@ "dependencies": { "acorn-jsx": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { @@ -6577,7 +6587,7 @@ "dependencies": { "acorn": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true } @@ -6637,7 +6647,7 @@ }, "eslint": { "version": "4.19.1", - "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { @@ -6962,7 +6972,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { "inherits": "^2.0.1", @@ -7565,7 +7575,7 @@ }, "text-encoding": { "version": "0.6.4", - "resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", "dev": true }, @@ -7595,7 +7605,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, diff --git a/package.json b/package.json index b6d3ff2b9..130f0cf5a 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "mock-require": "^3.0.3", "nyc": "^13.2.0", "proxyquire": "^2.1.0", + "retry": "^0.12.0", "rewire": "^4.0.1", "should": "13.2.3", "should-sinon": "0.0.6", diff --git a/src/controllers/auth.js b/src/controllers/auth.js index d728e8832..1c93a2746 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -149,7 +149,7 @@ class AuthController { * @param expiresIn * @returns {Promise|*|PromiseLike|Promise} */ - login (strategy, credentials, expiresIn) { + login (strategy, credentials = {}, expiresIn = null) { if (typeof strategy !== 'string' || strategy === '') { throw new Error('Kuzzle.auth.login: strategy is required'); } @@ -158,7 +158,7 @@ class AuthController { request = { strategy, expiresIn, - body: credentials || {}, + body: credentials, controller: 'auth', action: 'login' }; @@ -247,6 +247,26 @@ class AuthController { .then(response => response.result); } + /** + * Refresh an authentication token + * + * @param {Object} options + * @returns {Promise.} + */ + refreshToken(options = {}) { + const query = { + controller: 'auth', + action: 'refreshToken', + expiresIn: options.expiresIn + }; + + return this.kuzzle.query(query, options) + .then(response => { + this.kuzzle.jwt = response.result.jwt; + + return response.result; + }); + } } module.exports = AuthController; diff --git a/test/controllers/auth.test.js b/test/controllers/auth.test.js index 8cbcb1018..707036ff4 100644 --- a/test/controllers/auth.test.js +++ b/test/controllers/auth.test.js @@ -365,4 +365,45 @@ describe('Auth Controller', () => { }); }); }); + + describe('refreshToken', () => { + const tokenResponse = { _id: 'foo', jwt: 'newToken' }; + + beforeEach(() => { + kuzzle.jwt = 'jwt'; + kuzzle.query.resolves({result: tokenResponse}); + }); + + it('should call auth/refreshToken query', () => { + return kuzzle.auth.refreshToken() + .then(res => { + should(kuzzle.query) + .be.calledOnce() + .be.calledWith({ + controller: 'auth', + action: 'refreshToken', + expiresIn: undefined + }); + + should(res).be.eql(tokenResponse); + should(kuzzle.jwt).be.eql('newToken'); + }); + }); + + it('should set the expiresIn option if one is provided', () => { + return kuzzle.auth.refreshToken({expiresIn: 'foobar'}) + .then(res => { + should(kuzzle.query) + .be.calledOnce() + .be.calledWith({ + controller: 'auth', + action: 'refreshToken', + expiresIn: 'foobar' + }); + + should(res).be.eql(tokenResponse); + should(kuzzle.jwt).be.eql('newToken'); + }); + }); + }); }); From dc7ef8cb87c05f316c490ed2aa7cef941b865530 Mon Sep 17 00:00:00 2001 From: scottinet Date: Thu, 21 Mar 2019 12:03:06 +0100 Subject: [PATCH 2/2] [features] add support for the auth:getMyRights feature --- features/steps/auth.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/features/steps/auth.js b/features/steps/auth.js index 1e0b2aff6..b61abb20a 100644 --- a/features/steps/auth.js +++ b/features/steps/auth.js @@ -66,3 +66,11 @@ Then(/^the JWT is (in)?valid$/, async function (not) { should(response.valid).be.true(); } }); + +Given('I get my rights', async function () { + this.rights = await this.kuzzle.auth.getMyRights(); +}); + +Then('I have a vector with {int} rights', function (nbRights) { + should(this.rights).have.length(nbRights); +});