From 14f2b0ae46d7a89a148de37c3893a72774fa22de Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 9 Jan 2023 11:03:51 +0200 Subject: [PATCH 1/8] Add user token map by email --- workers/loc.api/sync/authenticator/index.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index f5f943d07..34dd04320 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -52,6 +52,7 @@ class Authenticator { * It may only work for one grenache worker instance */ this.userSessions = new Map() + this.userTokenMapByEmail = new Map() } async signUp (args, opts) { @@ -508,6 +509,7 @@ class Authenticator { return returnedUser } + // TODO: const existedToken = this.getUserSessionByEmail( { email, isSubAccount } ).token @@ -528,6 +530,7 @@ class Authenticator { } } + // TODO: async verifyUser (args, opts) { const { email, @@ -836,9 +839,13 @@ class Authenticator { } setUserSession (user) { - const { token } = user ?? {} + const { + token + } = user ?? {} + const tokenKey = this._getTokenKeyByEmailField(user) this.userSessions.set(token, { ...user }) + this.userTokenMapByEmail.set(tokenKey, token) } getUserSessionByToken (token, isReturnedPassword) { @@ -905,6 +912,18 @@ class Authenticator { return isArray ? res : res[0] } + + _getTokenKeyByEmailField (user) { + const { + email, + isSubAccount + } = user ?? {} + const suffix = isSubAccount + ? ':sub-account' + : '' + + return `${email}${suffix}` + } } decorateInjectable(Authenticator, depsTypes) From 6ce85bd4f93de759da17dc86d77da40e24faefa3 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 9 Jan 2023 12:21:51 +0200 Subject: [PATCH 2/8] Add ability to remove user session by email --- workers/loc.api/sync/authenticator/index.js | 23 +++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index 34dd04320..c609f6ac9 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -362,7 +362,11 @@ class Authenticator { throw new AuthError() } - this.removeUserSessionByToken(token) + this.removeUserSession({ + email, + isSubAccount, + token + }) if (isSchedulerEnabled) { await this.dao.updateRecordOf( @@ -833,15 +837,17 @@ class Authenticator { throw new UserRemovingError() } - this.removeUserSessionByToken(token) + this.removeUserSession({ + email, + isSubAccount, + token + }) return true } setUserSession (user) { - const { - token - } = user ?? {} + const { token } = user ?? {} const tokenKey = this._getTokenKeyByEmailField(user) this.userSessions.set(token, { ...user }) @@ -880,7 +886,12 @@ class Authenticator { return new Map(sessionsMap) } - removeUserSessionByToken (token) { + removeUserSession (user) { + const { token } = user ?? {} + const tokenKey = this._getTokenKeyByEmailField(user) + + this.userTokenMapByEmail.delete(tokenKey) + return this.userSessions.delete(token) } From 0ccccc8aa232008e59c1cc22b4edec28e2416b71 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 9 Jan 2023 14:13:58 +0200 Subject: [PATCH 3/8] Improve performance for getting user session by email --- workers/loc.api/sync/authenticator/index.js | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index c609f6ac9..83a6a6d74 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -513,7 +513,6 @@ class Authenticator { return returnedUser } - // TODO: const existedToken = this.getUserSessionByEmail( { email, isSubAccount } ).token @@ -534,7 +533,6 @@ class Authenticator { } } - // TODO: async verifyUser (args, opts) { const { email, @@ -860,18 +858,9 @@ class Authenticator { return pickSessionProps(session, isReturnedPassword) } - getUserSessionByEmail (args, isReturnedPassword) { - const { - email, - isSubAccount = false - } = args ?? {} - const keyVal = [...this.userSessions].find(([, session]) => { - return ( - email === session.email && - isSubAccount === session.isSubAccount - ) - }) - const session = Array.isArray(keyVal) ? keyVal[1] : {} + getUserSessionByEmail (user, isReturnedPassword) { + const tokenKey = this._getTokenKeyByEmailField(user) + const session = this.userTokenMapByEmail.get(tokenKey) return pickSessionProps(session, isReturnedPassword) } From 69b61deddca8a87ee0ee5ace4bb6ac62547d2d44 Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Mon, 9 Jan 2023 14:32:17 +0200 Subject: [PATCH 4/8] Fix getting session by email --- workers/loc.api/sync/authenticator/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index 83a6a6d74..be7a1a14a 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -303,7 +303,7 @@ class Authenticator { typeof token === 'string' ) ? token - : this.getUserSessionByEmail({ email, isSubAccount }).token + : this.getUserSessionByEmail({ email, isSubAccount })?.token const createdToken = ( existedToken && typeof existedToken === 'string' @@ -515,7 +515,7 @@ class Authenticator { const existedToken = this.getUserSessionByEmail( { email, isSubAccount } - ).token + )?.token const createdToken = ( existedToken && typeof existedToken === 'string' @@ -607,7 +607,7 @@ class Authenticator { token, isReturnedPassword ) - const { apiKey, apiSecret } = { ...session } + const { apiKey, apiSecret } = session ?? {} if ( !apiKey || @@ -860,7 +860,8 @@ class Authenticator { getUserSessionByEmail (user, isReturnedPassword) { const tokenKey = this._getTokenKeyByEmailField(user) - const session = this.userTokenMapByEmail.get(tokenKey) + const token = this.userTokenMapByEmail.get(tokenKey) + const session = this.userSessions.get(token) return pickSessionProps(session, isReturnedPassword) } From 68ded5d78fcdc0f0e5a6abe567b6320f4d252acd Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 10 Jan 2023 13:22:36 +0200 Subject: [PATCH 5/8] Add test cases for removing users --- test/test-cases/index.js | 4 +- test/test-cases/remove-user-test-cases.js | 91 +++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/test-cases/remove-user-test-cases.js diff --git a/test/test-cases/index.js b/test/test-cases/index.js index 4470bccb9..dbbdce10f 100644 --- a/test/test-cases/index.js +++ b/test/test-cases/index.js @@ -8,10 +8,12 @@ const additionalApiSyncModeSqliteTestCases = require( ) const signUpTestCase = require('./sign-up-test-case') const getSyncProgressTestCase = require('./get-sync-progress-test-case') +const removeUserTestCases = require('./remove-user-test-cases') module.exports = { apiSyncModeSqliteTestCases, additionalApiSyncModeSqliteTestCases, signUpTestCase, - getSyncProgressTestCase + getSyncProgressTestCase, + removeUserTestCases } diff --git a/test/test-cases/remove-user-test-cases.js b/test/test-cases/remove-user-test-cases.js new file mode 100644 index 000000000..dea3107e0 --- /dev/null +++ b/test/test-cases/remove-user-test-cases.js @@ -0,0 +1,91 @@ +'use strict' + +const { assert } = require('chai') + +module.exports = ( + agent, + params = {} +) => { + const { + basePath, + auth: { + email, + password, + isSubAccount + } + } = params + const auth = { token: '' } + + it('it should be successfully performed by the signIn method', async function () { + this.timeout(5000) + + const res = await agent + .post(`${basePath}/json-rpc`) + .type('json') + .send({ + auth: { + email, + password, + isSubAccount + }, + method: 'signIn', + id: 5 + }) + .expect('Content-Type', /json/) + .expect(200) + + assert.isObject(res.body) + assert.propertyVal(res.body, 'id', 5) + assert.isObject(res.body.result) + assert.strictEqual(res.body.result.email, email) + assert.strictEqual(res.body.result.isSubAccount, isSubAccount) + assert.isString(res.body.result.token) + + auth.token = res.body.result.token + }) + + it('it should be successfully performed by the removeUser method with email', async function () { + this.timeout(5000) + + const res = await agent + .post(`${basePath}/json-rpc`) + .type('json') + .send({ + auth: { + email, + password, + isSubAccount + }, + method: 'removeUser', + id: 5 + }) + .expect('Content-Type', /json/) + .expect(200) + + assert.isObject(res.body) + assert.propertyVal(res.body, 'id', 5) + assert.isBoolean(res.body.result) + assert.isOk(res.body.result) + }) + + it('it should not be successfully performed by the verifyUser method', async function () { + this.timeout(5000) + + const res = await agent + .post(`${basePath}/json-rpc`) + .type('json') + .send({ + auth, + method: 'verifyUser', + id: 5 + }) + .expect('Content-Type', /json/) + .expect(401) + + assert.isObject(res.body) + assert.isObject(res.body.error) + assert.propertyVal(res.body.error, 'code', 401) + assert.propertyVal(res.body.error, 'message', 'Unauthorized') + assert.propertyVal(res.body, 'id', 5) + }) +} From 17185471c69110e0376ba2c6afd40a7b861b5d7b Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 10 Jan 2023 13:25:38 +0200 Subject: [PATCH 6/8] Add test cases for removing users to base coverage --- test/1-api-sync-mode-sqlite.spec.js | 5 ++++- test/test-cases/api-sync-mode-sqlite-test-cases.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/1-api-sync-mode-sqlite.spec.js b/test/1-api-sync-mode-sqlite.spec.js index 4cbbaea43..5b43858d2 100644 --- a/test/1-api-sync-mode-sqlite.spec.js +++ b/test/1-api-sync-mode-sqlite.spec.js @@ -27,7 +27,8 @@ const agent = request.agent(app) const { apiSyncModeSqliteTestCases, - signUpTestCase + signUpTestCase, + removeUserTestCases } = require('./test-cases') let wrkReportServiceApi = null @@ -94,4 +95,6 @@ describe('Sync mode API with SQLite', () => { signUpTestCase(agent, params) apiSyncModeSqliteTestCases(agent, params) + signUpTestCase(agent, params) + removeUserTestCases(agent, params) }) diff --git a/test/test-cases/api-sync-mode-sqlite-test-cases.js b/test/test-cases/api-sync-mode-sqlite-test-cases.js index 4e08bf703..e90ce6f35 100644 --- a/test/test-cases/api-sync-mode-sqlite-test-cases.js +++ b/test/test-cases/api-sync-mode-sqlite-test-cases.js @@ -3354,7 +3354,7 @@ module.exports = ( assert.isNotOk(res.body.result) }) - it('it should be successfully performed by the removeUser method', async function () { + it('it should be successfully performed by the removeUser method with token', async function () { this.timeout(5000) const res = await agent From fe63e28edc249c40574c998978de51bb8a29a25b Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 10 Jan 2023 13:26:03 +0200 Subject: [PATCH 7/8] Add test cases for removing users to sub-account coverage --- test/4-sub-account.spec.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/4-sub-account.spec.js b/test/4-sub-account.spec.js index 379be2189..cc9508b7c 100644 --- a/test/4-sub-account.spec.js +++ b/test/4-sub-account.spec.js @@ -32,7 +32,8 @@ const agent = request.agent(app) const { apiSyncModeSqliteTestCases, additionalApiSyncModeSqliteTestCases, - signUpTestCase + signUpTestCase, + removeUserTestCases } = require('./test-cases') let wrkReportServiceApi = null @@ -429,5 +430,10 @@ describe('Sub-account', () => { additionalApiSyncModeSqliteTestCases(agent, params) }) + describe('Removing sub-account API', () => { + before(beforeFn) + + removeUserTestCases(agent, params) + }) }) }) From 8654ba6694df265992b59c038c7b4e029a518a3f Mon Sep 17 00:00:00 2001 From: Vladimir Voronkov Date: Tue, 10 Jan 2023 13:28:03 +0200 Subject: [PATCH 8/8] Fix removing user session when token is not passed --- workers/loc.api/sync/authenticator/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/workers/loc.api/sync/authenticator/index.js b/workers/loc.api/sync/authenticator/index.js index be7a1a14a..67e6f0dd0 100644 --- a/workers/loc.api/sync/authenticator/index.js +++ b/workers/loc.api/sync/authenticator/index.js @@ -879,10 +879,16 @@ class Authenticator { removeUserSession (user) { const { token } = user ?? {} const tokenKey = this._getTokenKeyByEmailField(user) + const _token = ( + token && + typeof token === 'string' + ) + ? token + : this.userTokenMapByEmail.get(tokenKey) this.userTokenMapByEmail.delete(tokenKey) - return this.userSessions.delete(token) + return this.userSessions.delete(_token) } async decryptApiKeys (password, users) {