From ac498f0bca5795bb8f9b8a370ee7b869dae6dc4e Mon Sep 17 00:00:00 2001 From: Sasha Sorokin Date: Tue, 17 Dec 2019 18:27:16 +0400 Subject: [PATCH 1/4] fix: delete lock for user ip after password reset --- schemas/verify.json | 2 +- src/actions/updatePassword.js | 92 +++++++++---------- .../redis/lua/slidingWindowReserve.lua | 4 + test/suites/actions/update-password.js | 40 ++++++-- .../redis/lua/sliding-window-reserve.js | 6 ++ yarn.lock | 85 ++--------------- 6 files changed, 94 insertions(+), 135 deletions(-) diff --git a/schemas/verify.json b/schemas/verify.json index f6c8bb623..cd065ad73 100644 --- a/schemas/verify.json +++ b/schemas/verify.json @@ -33,4 +33,4 @@ "type": "boolean" } } -} \ No newline at end of file +} diff --git a/src/actions/updatePassword.js b/src/actions/updatePassword.js index 8c3d0082c..5d370a271 100644 --- a/src/actions/updatePassword.js +++ b/src/actions/updatePassword.js @@ -1,6 +1,5 @@ -const Promise = require('bluebird'); -const Errors = require('common-errors'); -const partialRight = require('lodash/partialRight'); +const { HttpStatusError } = require('common-errors'); + const scrypt = require('../utils/scrypt'); const redisKey = require('../utils/key'); const jwt = require('../utils/jwt'); @@ -15,44 +14,40 @@ const { USERS_PASSWORD_FIELD, USERS_ID_FIELD, } = require('../constants'); +const UserLoginRateLimiter = require('../utils/rate-limiters/user-login-rate-limiter'); // cache error -const Forbidden = new Errors.HttpStatusError(403, 'invalid token'); +const Forbidden = new HttpStatusError(403, 'invalid token'); /** * Verify that username and password match + * @param {Object} service * @param {String} username * @param {String} password */ -function usernamePasswordReset(username, password) { - return Promise - .bind(this, username) - .then(getInternalData) - .tap(isActive) - .tap(isBanned) - .tap(hasPassword) - .tap((data) => scrypt.verify(data.password, password)) - .then((data) => data[USERS_ID_FIELD]); +async function usernamePasswordReset(service, username, password) { + const internalData = await getInternalData.call(service, username); + + await isActive(internalData); + await isBanned(internalData); + await hasPassword(internalData); + + await scrypt.verify(internalData.password, password); + + return internalData[USERS_ID_FIELD]; } /** * Sets new password for a given username + * @param {Object} service * @param {String} username * @param {String} password */ -function setPassword(_username, password) { - const { redis } = this; - - return Promise - .bind(this, _username) - .then(getUserId) - .then((userId) => Promise.props({ - userId, - hash: scrypt.hash(password), - })) - .then(({ userId, hash }) => redis - .hset(redisKey(userId, USERS_DATA), USERS_PASSWORD_FIELD, hash) - .return(userId)); +async function setPassword(service, userId, password) { + const { redis } = service; + const hash = scrypt.hash(password); + + return redis.hset(redisKey(userId, USERS_DATA), USERS_PASSWORD_FIELD, hash); } /** @@ -71,48 +66,45 @@ function setPassword(_username, password) { * @apiParam (Payload) {Boolean} [invalidateTokens=false] - if set to `true` will invalidate issued tokens * @apiParam (Payload) {String} [remoteip] - will be used for rate limiting if supplied */ -function updatePassword(request) { - const { redis } = this; - const { newPassword: password, remoteip } = request.params; +async function updatePassword(request) { + const { config, redis, tokenManager } = this; + const { newPassword: password, remoteip = false } = request.params; const invalidateTokens = !!request.params.invalidateTokens; + const loginRateLimiter = new UserLoginRateLimiter(redis, config.rateLimiters.userLogin); + + let userId; // 2 cases - token reset and current password reset - let promise; if (request.params.resetToken) { - // Refactor Me If You Can - promise = Promise - .resolve(this.tokenManager.verify(request.params.resetToken, { + try { + const tokenData = await tokenManager.verify(request.params.resetToken, { erase: true, control: { action: USERS_ACTION_RESET }, - })) - .catchThrow(Forbidden) - .get('id') - .bind(this); + }); + + // get real user id + userId = await getUserId.call(this, tokenData.id); + } catch (e) { + throw Forbidden; + } } else { - promise = Promise - .bind(this, [request.params.username, request.params.currentPassword]) - .spread(usernamePasswordReset); + userId = await usernamePasswordReset(this, request.params.username, request.params.currentPassword); } // update password - promise = promise.then(partialRight(setPassword, password)); + await setPassword(this, userId, password); if (invalidateTokens) { - promise = promise.tap(jwt.reset); + await jwt.reset(userId); } - if (remoteip) { - promise = promise.tap(function resetLock(username) { - return redis.del(redisKey(username, 'ip', remoteip)); - }); + if (remoteip !== false && loginRateLimiter.isEnabled()) { + await loginRateLimiter.cleanupForUserIp(userId, remoteip); } - return promise.return({ success: true }); + return { success: true }; } -/** - * Public API - */ module.exports = updatePassword; module.exports.updatePassword = setPassword; module.exports.transports = [require('@microfleet/core').ActionTransport.amqp]; diff --git a/src/utils/sliding-window-limiter/redis/lua/slidingWindowReserve.lua b/src/utils/sliding-window-limiter/redis/lua/slidingWindowReserve.lua index 09dc3ba38..f8f5f93c3 100644 --- a/src/utils/sliding-window-limiter/redis/lua/slidingWindowReserve.lua +++ b/src/utils/sliding-window-limiter/redis/lua/slidingWindowReserve.lua @@ -115,6 +115,10 @@ if windowInterval == false then end blockInterval = false +else + if blockInterval == 0 then + return redis.error_reply('`blockInterval` must be greater than 0 if `windowInterval` is greater than 0') + end end if reserveToken == 1 then diff --git a/test/suites/actions/update-password.js b/test/suites/actions/update-password.js index a8b73550c..d559f92e9 100644 --- a/test/suites/actions/update-password.js +++ b/test/suites/actions/update-password.js @@ -1,5 +1,7 @@ -const { inspectPromise } = require('@makeomatic/deploy'); +const { deepStrictEqual, strictEqual } = require('assert'); const { expect } = require('chai'); +const { inspectPromise } = require('@makeomatic/deploy'); + const redisKey = require('../../../src/utils/key.js'); const simpleDispatcher = require('../../helpers/simple-dispatcher'); @@ -108,13 +110,35 @@ describe('#updatePassword', function updatePasswordSuite() { }); }); - it('must update password passed with a valid challenge token', function test() { - return simpleDispatcher(this.users.router)('users.updatePassword', { resetToken: this.token, newPassword: 'vvv' }) - .reflect() - .then(inspectPromise()) - .then((updatePassword) => { - expect(updatePassword).to.be.deep.eq({ success: true }); - }); + it('must update password passed with a valid challenge token', async function test() { + const result = await this.users.amqp.publishAndWait( + 'users.updatePassword', + { resetToken: this.token, newPassword: 'vvv' } + ); + + deepStrictEqual(result, { success: true }); + }); + + it('must delete lock for ip after success update', async function test() { + const { amqp, redis } = this.users; + + await redis.zadd(`${this.userId}!ip!10.0.0.1`, 1576335000001, 'token1'); + await redis.zadd(`${this.userId}!ip!10.0.0.1`, 1576335000002, 'token2'); + await redis.zadd('gl!ip!ctr!10.0.0.1', 1576335000001, 'token1'); + await redis.zadd('gl!ip!ctr!10.0.0.1', 1576335000002, 'token2'); + + strictEqual(await redis.zrange(`${this.userId}!ip!10.0.0.1`, 0, -1).get('length'), 2); + strictEqual(await redis.zrange('gl!ip!ctr!10.0.0.1', 0, -1).get('length'), 2); + + const result = await amqp.publishAndWait( + 'users.updatePassword', + { resetToken: this.token, newPassword: 'vvv', remoteip: '10.0.0.1' } + ); + + deepStrictEqual(result, { success: true }); + + strictEqual(await redis.zrange(`${this.userId}!ip!10.0.0.1`, 0, -1).get('length'), 0); + strictEqual(await redis.zrange('gl!ip!ctr!10.0.0.1', 0, -1).get('length'), 0); }); }); }); diff --git a/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js b/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js index d1c052e2b..877264219 100644 --- a/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js +++ b/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js @@ -61,6 +61,12 @@ describe('redis.slidingWindowReserve script', function suite() { async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 10, 0), /^ReplyError: `blockInterval` has no sense if `windowInterval` is gt 0$/ ); + + // if window interval gt 0 then block interval must be gt 0 + await rejects( + async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 1000, 1, 0, 0), + /^ReplyError: `blockInterval` must be greater than 0 if `windowInterval` is greater than 0$/ + ); }); describe('should be able to works well if window interval equals 0', () => { diff --git a/yarn.lock b/yarn.lock index fc625884f..ad7817501 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2497,7 +2497,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@3.2.6, debug@^3.1.0, debug@^3.2.6: +debug@3.2.6, debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -2511,7 +2511,7 @@ debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -2619,11 +2619,6 @@ detect-indent@~5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -4120,7 +4115,7 @@ hyperid@^2.0.1: uuid "^3.3.2" uuid-parse "^1.0.0" -iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4187,7 +4182,7 @@ import-modules@^2.0.0: resolved "https://registry.yarnpkg.com/import-modules/-/import-modules-2.0.0.tgz#9c1e13b4e7a15682f70a6e3fa29534e4540cfc5d" integrity sha512-iczM/v9drffdNnABOKwj0f9G3cFDon99VcG1mxeBsdqnbd+vnQ5c2uAiCHNQITqFTOPaEvwg3VjoWCur0uHLEw== -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -5160,11 +5155,6 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -5173,33 +5163,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -5335,11 +5303,6 @@ lodash.reduce@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -5976,15 +5939,6 @@ ndjson@^1.3.0: split2 "^2.1.0" through2 "^2.0.3" -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - neo-async@^2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" @@ -6072,22 +6026,6 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - 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.4.2" - nopt@^4.0.1, nopt@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -6184,7 +6122,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.12, npm-packlist@^1.1.6, npm-packlist@^1.4.7: +npm-packlist@^1.1.12, npm-packlist@^1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== @@ -6370,7 +6308,7 @@ npm@^6.10.3: worker-farm "^1.7.0" write-file-atomic "^2.4.3" -npmlog@^4.0.2, npmlog@^4.1.2, npmlog@~4.1.2: +npmlog@^4.1.2, npmlog@~4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -7212,7 +7150,7 @@ ramda@^0.26.1: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== -rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: +rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7743,11 +7681,6 @@ safe-regex@^2.0.2, safe-regex@^2.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - scrypt-kdf@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/scrypt-kdf/-/scrypt-kdf-2.0.1.tgz#3355224c52d398331b2cbf2b70a7be26b52c53e6" @@ -7807,7 +7740,7 @@ semver-regex@^2.0.0: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -8422,7 +8355,7 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tar@^4.4.10, tar@^4.4.12, tar@^4.4.13, tar@^4.4.2: +tar@^4.4.10, tar@^4.4.12, tar@^4.4.13: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== From 338e328d8f4b9d5173ca1372d5de29ed223ad7d3 Mon Sep 17 00:00:00 2001 From: Sasha Sorokin Date: Tue, 17 Dec 2019 20:00:13 +0400 Subject: [PATCH 2/4] fix: remove unnecessary code --- .../redis/lua/sliding-window-reserve.js | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js b/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js index 877264219..4ec4b355e 100644 --- a/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js +++ b/test/suites/utils/sliding-window-limiter/redis/lua/sliding-window-reserve.js @@ -10,61 +10,61 @@ describe('redis.slidingWindowReserve script', function suite() { it('should be able to throw error if params are invalid', async () => { const { redis } = this.ctx.users; - await rejects(async () => redis.slidingWindowReserve(), /^ReplyError: ERR wrong number of arguments for 'evalsha' command$/); + await rejects(redis.slidingWindowReserve(), /^ReplyError: ERR wrong number of arguments for 'evalsha' command$/); // invalid number of keys - await rejects(async () => redis.slidingWindowReserve(''), /^ReplyError: ERR value is not an integer or out of range$/); - await rejects(async () => redis.slidingWindowReserve(null), /^ReplyError: ERR value is not an integer or out of range$/); - await rejects(async () => redis.slidingWindowReserve('perchik'), /^ReplyError: ERR value is not an integer or out of range$/); - await rejects(async () => redis.slidingWindowReserve(true), /^ReplyError: ERR value is not an integer or out of range$/); + await rejects(redis.slidingWindowReserve(''), /^ReplyError: ERR value is not an integer or out of range$/); + await rejects(redis.slidingWindowReserve(null), /^ReplyError: ERR value is not an integer or out of range$/); + await rejects(redis.slidingWindowReserve('perchik'), /^ReplyError: ERR value is not an integer or out of range$/); + await rejects(redis.slidingWindowReserve(true), /^ReplyError: ERR value is not an integer or out of range$/); // invalid redis key argument - await rejects(async () => redis.slidingWindowReserve(1), /^ReplyError: invalid `tokenDbKey` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, ''), /^ReplyError: invalid `tokenDbKey` argument$/); + await rejects(redis.slidingWindowReserve(1), /^ReplyError: invalid `tokenDbKey` argument$/); + await rejects(redis.slidingWindowReserve(1, ''), /^ReplyError: invalid `tokenDbKey` argument$/); // invalid current time argument - await rejects(async () => redis.slidingWindowReserve(1, 'perchik'), /^ReplyError: invalid `currentTime` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 0), /^ReplyError: invalid `currentTime` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', -1), /^ReplyError: invalid `currentTime` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 'fat'), /^ReplyError: invalid `currentTime` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik'), /^ReplyError: invalid `currentTime` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 0), /^ReplyError: invalid `currentTime` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', -1), /^ReplyError: invalid `currentTime` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 'fat'), /^ReplyError: invalid `currentTime` argument$/); // invalid window interval argument - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000), /^ReplyError: invalid `windowInterval` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, -1), /^ReplyError: invalid `windowInterval` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 'fat'), /^ReplyError: invalid `windowInterval` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000), /^ReplyError: invalid `windowInterval` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, -1), /^ReplyError: invalid `windowInterval` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 'fat'), /^ReplyError: invalid `windowInterval` argument$/); // invalid window limit argument - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0), /^ReplyError: invalid `windowLimit` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 0), /^ReplyError: invalid `windowLimit` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, -1), /^ReplyError: invalid `windowLimit` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 'fat'), /^ReplyError: invalid `windowLimit` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0), /^ReplyError: invalid `windowLimit` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 0), /^ReplyError: invalid `windowLimit` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, -1), /^ReplyError: invalid `windowLimit` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 'fat'), /^ReplyError: invalid `windowLimit` argument$/); // invalid block interval argument - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1), /^ReplyError: invalid `blockInterval` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, -1), /^ReplyError: invalid `blockInterval` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 'fat'), /^ReplyError: invalid `blockInterval` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1), /^ReplyError: invalid `blockInterval` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, -1), /^ReplyError: invalid `blockInterval` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 'fat'), /^ReplyError: invalid `blockInterval` argument$/); // invalid reserve token argument - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0), /^ReplyError: invalid `reserveToken` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, -1), /^ReplyError: invalid `reserveToken` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0), /^ReplyError: invalid `reserveToken` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, -1), /^ReplyError: invalid `reserveToken` argument$/); await rejects( - async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, 'fat'), + redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, 'fat'), /^ReplyError: invalid `reserveToken` argument$/ ); // invalid token argument - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, 1), /^ReplyError: invalid `token` argument$/); - await rejects(async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, 1, ''), /^ReplyError: invalid `token` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, 1), /^ReplyError: invalid `token` argument$/); + await rejects(redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 0, 1, ''), /^ReplyError: invalid `token` argument$/); // if window interval equals 0 then block interval has no sense await rejects( - async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 10, 0), + redis.slidingWindowReserve(1, 'perchik', 1576335000000, 0, 1, 10, 0), /^ReplyError: `blockInterval` has no sense if `windowInterval` is gt 0$/ ); // if window interval gt 0 then block interval must be gt 0 await rejects( - async () => redis.slidingWindowReserve(1, 'perchik', 1576335000000, 1000, 1, 0, 0), + redis.slidingWindowReserve(1, 'perchik', 1576335000000, 1000, 1, 0, 0), /^ReplyError: `blockInterval` must be greater than 0 if `windowInterval` is greater than 0$/ ); }); From 82efd00e769bbb9e88dd2fd70ca11ae78641eafd Mon Sep 17 00:00:00 2001 From: Sasha Sorokin Date: Tue, 17 Dec 2019 20:18:24 +0400 Subject: [PATCH 3/4] fix: error --- src/actions/updatePassword.js | 4 ++-- test/suites/{ => accounts}/admins.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename test/suites/{ => accounts}/admins.js (94%) diff --git a/src/actions/updatePassword.js b/src/actions/updatePassword.js index 5d370a271..3a97fb5c2 100644 --- a/src/actions/updatePassword.js +++ b/src/actions/updatePassword.js @@ -40,12 +40,12 @@ async function usernamePasswordReset(service, username, password) { /** * Sets new password for a given username * @param {Object} service - * @param {String} username + * @param {String} userId * @param {String} password */ async function setPassword(service, userId, password) { const { redis } = service; - const hash = scrypt.hash(password); + const hash = await scrypt.hash(password); return redis.hset(redisKey(userId, USERS_DATA), USERS_PASSWORD_FIELD, hash); } diff --git a/test/suites/admins.js b/test/suites/accounts/admins.js similarity index 94% rename from test/suites/admins.js rename to test/suites/accounts/admins.js index f1b9be81d..e29e8997b 100644 --- a/test/suites/admins.js +++ b/test/suites/accounts/admins.js @@ -1,10 +1,10 @@ /* global startService */ const assert = require('assert'); -const simpleDispatcher = require('../helpers/simple-dispatcher'); +const simpleDispatcher = require('../../helpers/simple-dispatcher'); describe('#admins', function verifySuite() { - const constants = require('../../src/constants'); + const constants = require('../../../src/constants'); const ctx = {}; let service; From 91166cd3cf7dd1f3cd2ac67a308b2e9ae83ec8f0 Mon Sep 17 00:00:00 2001 From: Sasha Sorokin Date: Wed, 18 Dec 2019 19:11:42 +0400 Subject: [PATCH 4/4] chore: add test for previous error --- test/suites/actions/update-password.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/suites/actions/update-password.js b/test/suites/actions/update-password.js index d559f92e9..10a544b21 100644 --- a/test/suites/actions/update-password.js +++ b/test/suites/actions/update-password.js @@ -111,12 +111,17 @@ describe('#updatePassword', function updatePasswordSuite() { }); it('must update password passed with a valid challenge token', async function test() { - const result = await this.users.amqp.publishAndWait( + const { amqp, redis } = this.users; + + const result = await amqp.publishAndWait( 'users.updatePassword', { resetToken: this.token, newPassword: 'vvv' } ); + const hashedPassword = await redis.hget(`${this.userId}!data`, 'password'); deepStrictEqual(result, { success: true }); + strictEqual(hashedPassword.startsWith('scrypt'), true); + strictEqual(hashedPassword.length > 50, true); }); it('must delete lock for ip after success update', async function test() {