From cc276bc6acc86e3983f8a8bb7ee842a89f58febb Mon Sep 17 00:00:00 2001 From: Renat Berezovsky Date: Mon, 16 Nov 2020 07:43:03 +0400 Subject: [PATCH 01/13] fix: edited actions for the organization members and invites (#481) * fix: edited actions for the organization members and invites * fix: not include added members in invite list * fix: update mailer templates --- schemas/organization.invites.send.json | 16 +++++++- src/actions/organization/invites/send.js | 13 ++++++- src/constants.js | 1 + src/utils/challenges/email/generate.js | 11 ++++++ .../organization/add-organization-members.js | 37 +++++++++++++------ src/utils/organization/send-invite-email.js | 12 +++--- test/configs/core.js | 1 + test/suites/actions/organization/invites.js | 3 +- yarn.lock | 23 ++++-------- 9 files changed, 80 insertions(+), 37 deletions(-) diff --git a/schemas/organization.invites.send.json b/schemas/organization.invites.send.json index ba8802c3e..26ea898fe 100644 --- a/schemas/organization.invites.send.json +++ b/schemas/organization.invites.send.json @@ -14,7 +14,21 @@ "permissions", "email" ], - "$ref": "common.json#/definitions/organizationMember" + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "permissions": { + "type": "array" + } + } } } } diff --git a/src/actions/organization/invites/send.js b/src/actions/organization/invites/send.js index d5fbd5a39..a15cab487 100644 --- a/src/actions/organization/invites/send.js +++ b/src/actions/organization/invites/send.js @@ -5,7 +5,9 @@ const { checkOrganizationExists } = require('../../../utils/organization'); const { ORGANIZATIONS_NAME_FIELD, ORGANIZATIONS_ID_FIELD, + USERS_ACTION_ORGANIZATION_INVITE, } = require('../../../constants'); +const getUserId = require('../../../utils/userData/get-user-id'); /** * @api {amqp} .invites.send Send invitation @@ -26,10 +28,19 @@ const { async function sendOrganizationInvite({ params }) { const { member, organizationId } = params; const organization = await getInternalData.call(this, organizationId); + let userExist = false; + + try { + await getUserId.call(this, member.email); + userExist = true; + } catch (e) { + this.log.info('invited user not exist'); + } return sendInviteMail.call(this, { email: member.email, ctx: { + skipPassword: userExist, firstName: member.firstName, lastName: member.lastName, permissions: member.permissions, @@ -37,7 +48,7 @@ async function sendOrganizationInvite({ params }) { organizationId: organization[ORGANIZATIONS_ID_FIELD], organization: organization[ORGANIZATIONS_NAME_FIELD], }, - }); + }, USERS_ACTION_ORGANIZATION_INVITE); } sendOrganizationInvite.allowed = checkOrganizationExists; diff --git a/src/constants.js b/src/constants.js index 3f0462569..7e0a48286 100644 --- a/src/constants.js +++ b/src/constants.js @@ -93,6 +93,7 @@ module.exports = exports = { USERS_ACTION_INVITE: 'invite', USERS_ACTION_ORGANIZATION_INVITE: 'organization-user-invite', USERS_ACTION_ORGANIZATION_REGISTER: 'organization-user-register', + USERS_ACTION_ORGANIZATION_ADD: 'organization-user-add', // invitations constants INVITATIONS_INDEX: 'user-invitations', diff --git a/src/utils/challenges/email/generate.js b/src/utils/challenges/email/generate.js index ec803b8c0..8929e4dc7 100644 --- a/src/utils/challenges/email/generate.js +++ b/src/utils/challenges/email/generate.js @@ -16,6 +16,7 @@ const { USERS_ACTION_INVITE, USERS_ACTION_ORGANIZATION_INVITE, USERS_ACTION_ORGANIZATION_REGISTER, + USERS_ACTION_ORGANIZATION_ADD, } = require('../../../constants.js'); // will be replaced later @@ -40,6 +41,15 @@ function generate(email, type, ctx = {}, opts = {}, nodemailer = {}) { context.qs = `?q=${context.token.secret}`; context.link = generateLink(server, paths[type]); break; + case USERS_ACTION_ORGANIZATION_ADD: + context.qs = `?${stringify({ + login: ctx.email, + firstName: ctx.firstName, + lastName: ctx.lastName, + organizationId: ctx.organizationId, + })}`; + context.link = generateLink(server, paths[USERS_ACTION_ORGANIZATION_ADD]); + break; case USERS_ACTION_ORGANIZATION_REGISTER: context.qs = `?${stringify({ password: ctx.password, @@ -57,6 +67,7 @@ function generate(email, type, ctx = {}, opts = {}, nodemailer = {}) { username: ctx.email, firstName: ctx.firstName, lastName: ctx.lastName, + skipPassword: ctx.skipPassword, })}`; context.link = generateLink(server, paths[USERS_ACTION_ORGANIZATION_INVITE]); break; diff --git a/src/utils/organization/add-organization-members.js b/src/utils/organization/add-organization-members.js index 931819e93..370f75ba4 100644 --- a/src/utils/organization/add-organization-members.js +++ b/src/utils/organization/add-organization-members.js @@ -12,7 +12,10 @@ const { USERS_METADATA, ORGANIZATIONS_NAME_FIELD, ORGANIZATIONS_ID_FIELD, + USERS_ACTION_ORGANIZATION_REGISTER, + USERS_ACTION_ORGANIZATION_ADD, } = require('../../constants.js'); +const generateEmail = require('../challenges/email/generate.js'); const JSONStringify = (data) => JSON.stringify(data); @@ -43,19 +46,29 @@ async function addMember({ password, ...member }) { pipe.zadd(membersKey, stringifyMember.invited, memberKey); } -function sendInvite(member) { - return sendInviteMail.call(this, { +async function sendInvite(member) { + const ctx = { email: member.email, - ctx: { - email: member.email, - firstName: member.firstName, - lastName: member.lastName, - password: member.password, - permissions: member.permissions, - organizationId: this.organization[ORGANIZATIONS_ID_FIELD], - organization: this.organization[ORGANIZATIONS_NAME_FIELD], - }, - }); + firstName: member.firstName, + lastName: member.lastName, + password: member.password, + permissions: member.permissions, + organizationId: this.organization[ORGANIZATIONS_ID_FIELD], + organization: this.organization[ORGANIZATIONS_NAME_FIELD], + }; + + if (ctx.password) { + return sendInviteMail.call(this, { ctx, email: member.email }, USERS_ACTION_ORGANIZATION_REGISTER); + } + + // if user already exist, just send info email, about new org + const res = await generateEmail.call(this, member.email, USERS_ACTION_ORGANIZATION_ADD, ctx, { wait: true, send: true }); + + if (res.err) { + this.log.error(res, 'send organization add mail result'); + } + + return res; } /** diff --git a/src/utils/organization/send-invite-email.js b/src/utils/organization/send-invite-email.js index 2d4256e5a..f4708a9d3 100644 --- a/src/utils/organization/send-invite-email.js +++ b/src/utils/organization/send-invite-email.js @@ -5,19 +5,18 @@ const { TOKEN_METADATA_FIELD_CONTEXT, TOKEN_METADATA_FIELD_SENDED_AT, USERS_ACTION_ORGANIZATION_INVITE, - USERS_ACTION_ORGANIZATION_REGISTER, TOKEN_METADATA_FIELD_METADATA, } = require('../../constants.js'); -module.exports = async function sendInviteMail(params) { +module.exports = async function sendInviteMail(params, action = USERS_ACTION_ORGANIZATION_INVITE) { const { redis, tokenManager } = this; const { email, ctx } = params; const now = Date.now(); const token = await tokenManager .create({ + action, id: inviteId(ctx.organizationId, email), - action: USERS_ACTION_ORGANIZATION_INVITE, regenerate: true, metadata: { [TOKEN_METADATA_FIELD_METADATA]: { permissions: ctx.permissions }, @@ -26,12 +25,13 @@ module.exports = async function sendInviteMail(params) { }, }); - const emailType = ctx.password ? USERS_ACTION_ORGANIZATION_REGISTER : USERS_ACTION_ORGANIZATION_INVITE; - const res = await generateEmail.call(this, email, emailType, { ...ctx, token }, { wait: true, send: true }); + const res = await generateEmail.call(this, email, action, { ...ctx, token }, { wait: true, send: true }); if (res.err) { this.log.error(res, 'send invite mail result'); } - await redis.sadd(organizationInvite(ctx.organizationId), email); + if (action === USERS_ACTION_ORGANIZATION_INVITE) { + await redis.sadd(organizationInvite(ctx.organizationId), email); + } }; diff --git a/test/configs/core.js b/test/configs/core.js index 7026840d5..4b163200d 100644 --- a/test/configs/core.js +++ b/test/configs/core.js @@ -10,6 +10,7 @@ module.exports = { register: 'cpst-register', invite: 'rfx-invite', 'organization-user-invite': 'sl-accept-invite', + 'organization-user-add': 'sl-registration-notify', 'organization-user-register': 'sl-registration-notify', }, }, diff --git a/test/suites/actions/organization/invites.js b/test/suites/actions/organization/invites.js index 4b97319e1..6e5563651 100644 --- a/test/suites/actions/organization/invites.js +++ b/test/suites/actions/organization/invites.js @@ -60,10 +60,9 @@ describe('#invite organization', function registerSuite() { .then(inspectPromise()) .then(({ data }) => { // At this point we're expecting to have 3 users: - // - root, created together with the organization // - root, created at a test case above // - member, also created at a test case - assert.equal(data.length, 3); + assert.equal(data.length, 2); for (const invite of data) { assert(invite.id); assert(invite.type); diff --git a/yarn.lock b/yarn.lock index ccdb2b14c..985ec86c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2894,7 +2894,7 @@ commander@2.11.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== -commander@^2.20.0, commander@~2.20.3: +commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2904,12 +2904,7 @@ commander@^4.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -common-errors@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/common-errors/-/common-errors-1.0.5.tgz#13f8aa9727ca71595d0067895e98ef4ba7fc994e" - integrity sha1-E/iqlyfKcVldAGeJXpjvS6f8mU4= - -common-errors@^1.2.0: +common-errors@^1.0.5, common-errors@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/common-errors/-/common-errors-1.2.0.tgz#5e3903f66fe58d691c1fc1ff881ec692e4a69442" integrity sha512-HtLF1V6LRACyueMEs+MLwf++1Q7cJLC0FFVStFErYZXXCNuTyVwXp67MTn7Mf7+lAaA5HfUe4cREPTwo0LAXkg== @@ -6920,9 +6915,9 @@ ndjson@^1.3.0: through2 "^2.0.3" neo-async@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" - integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nerf-dart@^1.0.0: version "1.0.0" @@ -9744,11 +9739,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.9.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.2.tgz#012b74fb6a2e440d9ba1f79110a479d3b1f2d48d" - integrity sha512-zGVwKslUAD/EeqOrD1nQaBmXIHl1Vw371we8cvS8I6mYK9rmgX5tv8AAeJdfsQ3Kk5mGax2SVV/AizxdNGhl7Q== - dependencies: - commander "~2.20.3" + version "3.11.6" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.6.tgz#144b50d3e05eadd3ad4dd047c60ca541a8cd4e9c" + integrity sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g== uid-number@0.0.6: version "0.0.6" From 7db10d5498e25eee6ac9ca19089dcc9e7ad77544 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 16 Nov 2020 05:14:57 +0000 Subject: [PATCH 02/13] chore(release): 14.12.3-rc.1 [skip ci] ## [14.12.3-rc.1](https://github.com/makeomatic/ms-users/compare/v14.12.2...v14.12.3-rc.1) (2020-11-16) ### Bug Fixes * edited actions for the organization members and invites ([#481](https://github.com/makeomatic/ms-users/issues/481)) ([a3f2627](https://github.com/makeomatic/ms-users/commit/a3f26273633a67a2d04c79b8b027427b5bbb12c8)) --- CHANGELOG.md | 7 ++++++- package.json | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55460ef4..ecd2c62a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ -## [14.12.3](https://github.com/makeomatic/ms-users/compare/v14.12.2...v14.12.3) (2020-11-23) +## [14.12.3-rc.1](https://github.com/makeomatic/ms-users/compare/v14.12.2...v14.12.3-rc.1) (2020-11-16) + +### Bug Fixes +* edited actions for the organization members and invites ([#481](https://github.com/makeomatic/ms-users/issues/481)) ([a3f2627](https://github.com/makeomatic/ms-users/commit/a3f26273633a67a2d04c79b8b027427b5bbb12c8)) +* +## [14.12.3](https://github.com/makeomatic/ms-users/compare/v14.12.2...v14.12.3) (2020-11-23) ### Bug Fixes diff --git a/package.json b/package.json index 94f63838a..a0cffb35b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ms-users", "description": "Core of the microservice for handling users", "main": "./lib/index.js", - "version": "14.12.3", + "version": "14.12.3-rc.1", "scripts": { "compile": "rimraf ./lib; babel -d ./lib --copy-files ./src", "pretest": "yarn compile", From c11d040414b0e8da6717663c59cee92a1a21d5f7 Mon Sep 17 00:00:00 2001 From: renat Date: Wed, 18 Nov 2020 07:14:40 +0400 Subject: [PATCH 03/13] fix: added new props form email config in validation scheme --- schemas/config.json | 36 ++++++++++++++++++++++++++ src/utils/challenges/email/generate.js | 2 ++ 2 files changed, 38 insertions(+) diff --git a/schemas/config.json b/schemas/config.json index c8b60eeef..626ca3e5d 100644 --- a/schemas/config.json +++ b/schemas/config.json @@ -191,6 +191,15 @@ }, "reset": { "type": "string" + }, + "organization-user-invite": { + "type": "string" + }, + "organization-user-register": { + "type": "string" + }, + "organization-user-add": { + "type": "string" } } }, @@ -210,6 +219,15 @@ }, "password": { "type": "string" + }, + "organization-user-invite": { + "type": "string" + }, + "organization-user-register": { + "type": "string" + }, + "organization-user-add": { + "type": "string" } } }, @@ -229,6 +247,15 @@ }, "password": { "type": "string" + }, + "organization-user-invite": { + "type": "string" + }, + "organization-user-register": { + "type": "string" + }, + "organization-user-add": { + "type": "string" } } }, @@ -243,6 +270,15 @@ }, "password": { "type": "string" + }, + "organization-user-invite": { + "type": "string" + }, + "organization-user-register": { + "type": "string" + }, + "organization-user-add": { + "type": "string" } } }, diff --git a/src/utils/challenges/email/generate.js b/src/utils/challenges/email/generate.js index 8929e4dc7..55adf47b9 100644 --- a/src/utils/challenges/email/generate.js +++ b/src/utils/challenges/email/generate.js @@ -87,6 +87,8 @@ function generate(email, type, ctx = {}, opts = {}, nodemailer = {}) { actions.updatePassword = updatePassword.call(this, email, context.password); } + this.log.info('Generated mail %s', templateName); + return Promise .props({ ...actions, From edb94387601e330eff0e304df855bd4853f36734 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 18 Nov 2020 03:39:40 +0000 Subject: [PATCH 04/13] chore(release): 14.12.3-rc.2 [skip ci] ## [14.12.3-rc.2](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.1...v14.12.3-rc.2) (2020-11-18) ### Bug Fixes * added new props form email config in validation scheme ([6625eb6](https://github.com/makeomatic/ms-users/commit/6625eb6bad53243037044a96a013b162375041fe)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecd2c62a6..c2ab03686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [14.12.3-rc.2](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.1...v14.12.3-rc.2) (2020-11-18) + + +### Bug Fixes + +* added new props form email config in validation scheme ([6625eb6](https://github.com/makeomatic/ms-users/commit/6625eb6bad53243037044a96a013b162375041fe)) + ## [14.12.3-rc.1](https://github.com/makeomatic/ms-users/compare/v14.12.2...v14.12.3-rc.1) (2020-11-16) ### Bug Fixes diff --git a/package.json b/package.json index a0cffb35b..d901fc920 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ms-users", "description": "Core of the microservice for handling users", "main": "./lib/index.js", - "version": "14.12.3-rc.1", + "version": "14.12.3-rc.2", "scripts": { "compile": "rimraf ./lib; babel -d ./lib --copy-files ./src", "pretest": "yarn compile", From b162bdf46f1b5021b241e4cb6aaec871bba8ecba Mon Sep 17 00:00:00 2001 From: renat Date: Wed, 18 Nov 2020 07:59:43 +0400 Subject: [PATCH 05/13] fix: edit emal types in validate config --- schemas/config.json | 127 +++++++++++--------------------------------- src/constants.js | 6 +-- 2 files changed, 35 insertions(+), 98 deletions(-) diff --git a/schemas/config.json b/schemas/config.json index 626ca3e5d..ea512b26a 100644 --- a/schemas/config.json +++ b/schemas/config.json @@ -180,107 +180,16 @@ ], "properties": { "paths": { - "type": "object", - "required": [ - "activate", - "reset" - ], - "properties": { - "activate": { - "type": "string" - }, - "reset": { - "type": "string" - }, - "organization-user-invite": { - "type": "string" - }, - "organization-user-register": { - "type": "string" - }, - "organization-user-add": { - "type": "string" - } - } + "$ref": "#/definitions/emailTypes" }, "senders": { - "required": [ - "activate", - "reset", - "password" - ], - "type": "object", - "properties": { - "activate": { - "type": "string" - }, - "reset": { - "type": "string" - }, - "password": { - "type": "string" - }, - "organization-user-invite": { - "type": "string" - }, - "organization-user-register": { - "type": "string" - }, - "organization-user-add": { - "type": "string" - } - } + "$ref": "#/definitions/emailTypes" }, "subjects": { - "required": [ - "activate", - "reset", - "password" - ], - "type": "object", - "properties": { - "activate": { - "type": "string" - }, - "reset": { - "type": "string" - }, - "password": { - "type": "string" - }, - "organization-user-invite": { - "type": "string" - }, - "organization-user-register": { - "type": "string" - }, - "organization-user-add": { - "type": "string" - } - } + "$ref": "#/definitions/emailTypes" }, "templates": { - "type": "object", - "properties": { - "activate": { - "type": "string" - }, - "reset": { - "type": "string" - }, - "password": { - "type": "string" - }, - "organization-user-invite": { - "type": "string" - }, - "organization-user-register": { - "type": "string" - }, - "organization-user-add": { - "type": "string" - } - } + "$ref": "#/definitions/emailTypes" }, "email": { "oneOf": [{ @@ -835,6 +744,34 @@ } } } + }, + "emailTypes": { + "type": "object", + "required": [ + "activate", + "reset", + "password" + ], + "properties": { + "activate": { + "type": "string" + }, + "reset": { + "type": "string" + }, + "password": { + "type": "string" + }, + "organizationUserInvite": { + "type": "string" + }, + "organizationUserRegister": { + "type": "string" + }, + "organizationUserAdd": { + "type": "string" + } + } } } } diff --git a/src/constants.js b/src/constants.js index 7e0a48286..dd3670612 100644 --- a/src/constants.js +++ b/src/constants.js @@ -91,9 +91,9 @@ module.exports = exports = { USERS_ACTION_RESET: 'reset', USERS_ACTION_REGISTER: 'register', USERS_ACTION_INVITE: 'invite', - USERS_ACTION_ORGANIZATION_INVITE: 'organization-user-invite', - USERS_ACTION_ORGANIZATION_REGISTER: 'organization-user-register', - USERS_ACTION_ORGANIZATION_ADD: 'organization-user-add', + USERS_ACTION_ORGANIZATION_INVITE: 'organizationUserInvite', + USERS_ACTION_ORGANIZATION_REGISTER: 'organizationUserRegister', + USERS_ACTION_ORGANIZATION_ADD: 'organizationUserAdd', // invitations constants INVITATIONS_INDEX: 'user-invitations', From a05b4fd89f511b4aac3e2fff4a4cfd916e81bf27 Mon Sep 17 00:00:00 2001 From: renat Date: Wed, 18 Nov 2020 08:13:25 +0400 Subject: [PATCH 06/13] fix: omit required email types in validation config --- schemas/config.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/schemas/config.json b/schemas/config.json index ea512b26a..e925f85c6 100644 --- a/schemas/config.json +++ b/schemas/config.json @@ -747,11 +747,6 @@ }, "emailTypes": { "type": "object", - "required": [ - "activate", - "reset", - "password" - ], "properties": { "activate": { "type": "string" From c1f88e115c83af4ed28495f9cb06a8c4210fd47b Mon Sep 17 00:00:00 2001 From: renat Date: Wed, 18 Nov 2020 08:24:55 +0400 Subject: [PATCH 07/13] fix: fix test config --- test/configs/core.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/configs/core.js b/test/configs/core.js index 4b163200d..7092131fa 100644 --- a/test/configs/core.js +++ b/test/configs/core.js @@ -9,9 +9,9 @@ module.exports = { password: 'cpst-password', register: 'cpst-register', invite: 'rfx-invite', - 'organization-user-invite': 'sl-accept-invite', - 'organization-user-add': 'sl-registration-notify', - 'organization-user-register': 'sl-registration-notify', + organizationUserInvite: 'sl-accept-invite', + organizationUserAdd: 'sl-registration-notify', + organizationUserRegister: 'sl-registration-notify', }, }, registrationLimits: { From 50bfb9c05557c0f3d14571258d63f07b2a72ebde Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 18 Nov 2020 04:42:04 +0000 Subject: [PATCH 08/13] chore(release): 14.12.3-rc.3 [skip ci] ## [14.12.3-rc.3](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.2...v14.12.3-rc.3) (2020-11-18) ### Bug Fixes * edit emal types in validate config ([43896ba](https://github.com/makeomatic/ms-users/commit/43896ba2ba856568e2e77479e9c8e0af3b4899c2)) * fix test config ([0db5127](https://github.com/makeomatic/ms-users/commit/0db5127bc650ab0f47732ac793ab7e86ab6f2a45)) * omit required email types in validation config ([f2b16de](https://github.com/makeomatic/ms-users/commit/f2b16dee5bf9c096a1e92c9f7de75f023a38578a)) --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ab03686..310409b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [14.12.3-rc.3](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.2...v14.12.3-rc.3) (2020-11-18) + + +### Bug Fixes + +* edit emal types in validate config ([43896ba](https://github.com/makeomatic/ms-users/commit/43896ba2ba856568e2e77479e9c8e0af3b4899c2)) +* fix test config ([0db5127](https://github.com/makeomatic/ms-users/commit/0db5127bc650ab0f47732ac793ab7e86ab6f2a45)) +* omit required email types in validation config ([f2b16de](https://github.com/makeomatic/ms-users/commit/f2b16dee5bf9c096a1e92c9f7de75f023a38578a)) + ## [14.12.3-rc.2](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.1...v14.12.3-rc.2) (2020-11-18) diff --git a/package.json b/package.json index d901fc920..680ca5853 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ms-users", "description": "Core of the microservice for handling users", "main": "./lib/index.js", - "version": "14.12.3-rc.2", + "version": "14.12.3-rc.3", "scripts": { "compile": "rimraf ./lib; babel -d ./lib --copy-files ./src", "pretest": "yarn compile", From 2c708e950e8f4682358d2b90f5179f7b308fb6ba Mon Sep 17 00:00:00 2001 From: renat Date: Fri, 20 Nov 2020 07:29:19 +0400 Subject: [PATCH 09/13] fix: allow any types for user permission --- schemas/organization.members.permission.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/schemas/organization.members.permission.json b/schemas/organization.members.permission.json index 9662700b8..b1372d49f 100644 --- a/schemas/organization.members.permission.json +++ b/schemas/organization.members.permission.json @@ -18,18 +18,10 @@ "properties": { "$set": { "type": "array", - "items": { - "type": "string", - "minLength": 1 - }, "minItems": 1 }, "$remove": { "type": "array", - "items": { - "type": "string", - "minLength": 1 - }, "minItems": 1 } } From 901e8b9b0150e27e79a46eb226d3ac57b122be85 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 20 Nov 2020 04:18:52 +0000 Subject: [PATCH 10/13] chore(release): 14.12.3-rc.4 [skip ci] ## [14.12.3-rc.4](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.3...v14.12.3-rc.4) (2020-11-20) ### Bug Fixes * allow any types for user permission ([be0d9dc](https://github.com/makeomatic/ms-users/commit/be0d9dc24f3a0c644945c9a0a02b04de7fe03dc1)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 310409b50..84b6ff149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [14.12.3-rc.4](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.3...v14.12.3-rc.4) (2020-11-20) + + +### Bug Fixes + +* allow any types for user permission ([be0d9dc](https://github.com/makeomatic/ms-users/commit/be0d9dc24f3a0c644945c9a0a02b04de7fe03dc1)) + ## [14.12.3-rc.3](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.2...v14.12.3-rc.3) (2020-11-18) diff --git a/package.json b/package.json index 680ca5853..852851539 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ms-users", "description": "Core of the microservice for handling users", "main": "./lib/index.js", - "version": "14.12.3-rc.3", + "version": "14.12.3-rc.4", "scripts": { "compile": "rimraf ./lib; babel -d ./lib --copy-files ./src", "pretest": "yarn compile", From 2eac8c47ca5b484fc912ac3bdd4a8d699e082e47 Mon Sep 17 00:00:00 2001 From: renat Date: Mon, 23 Nov 2020 09:46:03 +0400 Subject: [PATCH 11/13] fix: remove invite after accept --- src/actions/organization/invites/accept.js | 8 ++++- src/actions/organization/invites/list.js | 9 +++--- test/suites/actions/organization/invites.js | 35 +++++++++++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/actions/organization/invites/accept.js b/src/actions/organization/invites/accept.js index 832dd35d3..ab01b1276 100644 --- a/src/actions/organization/invites/accept.js +++ b/src/actions/organization/invites/accept.js @@ -5,6 +5,7 @@ const { ErrorInvitationExpiredOrUsed, TOKEN_METADATA_FIELD_METADATA, inviteId, + organizationInvite, } = require('../../../constants'); const addOrganizationMembers = require('../../../utils/organization/add-organization-members'); @@ -44,11 +45,16 @@ async function acceptOrganizationMember({ params }) { member.permissions = memberInviteMetadata.permissions; member.password = password; - return addOrganizationMembers.call(this, { + const response = await addOrganizationMembers.call(this, { organizationId, audience, members: [member], }); + + await this.tokenManager.remove({ id: inviteId(organizationId, member.email), action: USERS_ACTION_ORGANIZATION_INVITE }); + await this.redis.srem(organizationInvite(organizationId), member.email); + + return response; } acceptOrganizationMember.allowed = checkOrganizationExists; diff --git a/src/actions/organization/invites/list.js b/src/actions/organization/invites/list.js index af048057a..fe5c97509 100644 --- a/src/actions/organization/invites/list.js +++ b/src/actions/organization/invites/list.js @@ -3,7 +3,8 @@ const Promise = require('bluebird'); const { checkOrganizationExists } = require('../../../utils/organization'); const { organizationInvite, inviteId, USERS_ACTION_ORGANIZATION_INVITE, TOKEN_METADATA_FIELD_METADATA } = require('../../../constants'); -const mapUser = ({ id, created, metadata }) => { +const mapUser = ({ id, created, metadata } = {}) => { + if (!id || id === '') return false; const [, userId] = id.split(':'); return { @@ -17,7 +18,7 @@ function getTokenInfo(email) { return this.tokenManager.info({ id: inviteId(this.organizationId, email), action: USERS_ACTION_ORGANIZATION_INVITE, - }); + }).catch((e) => this.log.error('resolve token info error: %o', e)); } /** @@ -33,11 +34,11 @@ function getTokenInfo(email) { async function listOrganizationInvite({ params }) { const { organizationId } = params; const invitesIds = await this.redis.smembers(organizationInvite(organizationId)); - const getInvitesInfo = invitesIds.map(getTokenInfo, { tokenManager: this.tokenManager, organizationId }); + const getInvitesInfo = invitesIds.map(getTokenInfo, { log: this.log, tokenManager: this.tokenManager, organizationId }); const invites = await Promise.all(getInvitesInfo); return { - data: invites.map(mapUser), + data: invites.map(mapUser).filter((f) => f), }; } diff --git a/test/suites/actions/organization/invites.js b/test/suites/actions/organization/invites.js index 6e5563651..d644d87ee 100644 --- a/test/suites/actions/organization/invites.js +++ b/test/suites/actions/organization/invites.js @@ -128,6 +128,32 @@ describe('#invite organization', function registerSuite() { .then(inspectPromise()); }); + it('must be able to get invites list without invited user', async function test() { + return this.dispatch('users.organization.invites.list', { organizationId: this.organization.id }) + .reflect() + .then(inspectPromise()) + .then(({ data }) => { + // At this point we're expecting to have 3 users: + // - root, created at a test case above + // - member, also created at a test case + assert.equal(data.length, 1); + for (const invite of data) { + assert(invite.id); + assert(invite.type); + assert(invite.attributes); + assert(invite.attributes.id); + assert(invite.attributes.created); + assert(invite.attributes.metadata); + assert(invite.attributes.metadata.permissions); + } + + const admin = data.find(({ id }) => id === this.admin.email); + + assert(admin); + assert.deepStrictEqual(admin.attributes.metadata.permissions, this.admin.permissions); + }); + }); + it('must be able to revoke invite to admin', async function test() { const opts = { organizationId: this.organization.id, @@ -155,6 +181,15 @@ describe('#invite organization', function registerSuite() { .then(inspectPromise(false)); }); + it('must be able to get invites list without revoked user', async function test() { + return this.dispatch('users.organization.invites.list', { organizationId: this.organization.id }) + .reflect() + .then(inspectPromise()) + .then(({ data }) => { + assert.equal(data.length, 0); + }); + }); + it('must return error on expired token', async function test() { const opts = { organizationId: this.organization.id, From 79c6df0ca97024d30e6c06f659aa8ca0cf8ff3b9 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 23 Nov 2020 06:02:27 +0000 Subject: [PATCH 12/13] chore(release): 14.12.3-rc.5 [skip ci] ## [14.12.3-rc.5](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.4...v14.12.3-rc.5) (2020-11-23) ### Bug Fixes * remove invite after accept ([287f7b3](https://github.com/makeomatic/ms-users/commit/287f7b3b55877b40c6a3f9e130658c8eb240b1c8)) --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b6ff149..7791c0475 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [14.12.3-rc.5](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.4...v14.12.3-rc.5) (2020-11-23) + + +### Bug Fixes + +* remove invite after accept ([287f7b3](https://github.com/makeomatic/ms-users/commit/287f7b3b55877b40c6a3f9e130658c8eb240b1c8)) + ## [14.12.3-rc.4](https://github.com/makeomatic/ms-users/compare/v14.12.3-rc.3...v14.12.3-rc.4) (2020-11-20) diff --git a/package.json b/package.json index 852851539..f745f5571 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ms-users", "description": "Core of the microservice for handling users", "main": "./lib/index.js", - "version": "14.12.3-rc.4", + "version": "14.12.3-rc.5", "scripts": { "compile": "rimraf ./lib; babel -d ./lib --copy-files ./src", "pretest": "yarn compile", From 6d59440f868af50ef124accbb821ebf10b0ed0b0 Mon Sep 17 00:00:00 2001 From: Renat Berezovsky Date: Fri, 27 Nov 2020 20:21:52 +0400 Subject: [PATCH 13/13] feat: added get and update actions for organization member (#484) * feat: added get and update actions for organization member * fix: bump mailer templates --- schemas/organization.members.get.json | 15 ++++++ schemas/organization.members.update.json | 19 +++++++ src/actions/organization/members/get.js | 47 +++++++++++++++++ src/actions/organization/members/update.js | 51 +++++++++++++++++++ .../organization/get-organization-members.js | 1 + .../update-organization-members.js | 27 ++++++++++ .../actions/organization/members/get.js | 34 +++++++++++++ .../actions/organization/members/update.js | 39 ++++++++++++++ yarn.lock | 6 +-- 9 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 schemas/organization.members.get.json create mode 100644 schemas/organization.members.update.json create mode 100644 src/actions/organization/members/get.js create mode 100644 src/actions/organization/members/update.js create mode 100644 src/utils/organization/update-organization-members.js create mode 100644 test/suites/actions/organization/members/get.js create mode 100644 test/suites/actions/organization/members/update.js diff --git a/schemas/organization.members.get.json b/schemas/organization.members.get.json new file mode 100644 index 000000000..b3aaa6380 --- /dev/null +++ b/schemas/organization.members.get.json @@ -0,0 +1,15 @@ +{ + "type": "object", + "required": [ + "organizationId", + "username" + ], + "properties": { + "organizationId": { + "type": "string" + }, + "username": { + "type": "string" + } + } +} diff --git a/schemas/organization.members.update.json b/schemas/organization.members.update.json new file mode 100644 index 000000000..e0ff83367 --- /dev/null +++ b/schemas/organization.members.update.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "required": [ + "organizationId", + "username", + "data" + ], + "properties": { + "organizationId": { + "type": "string" + }, + "username": { + "type": "string" + }, + "data": { + "$ref": "common.json#/definitions/metadata" + } + } +} diff --git a/src/actions/organization/members/get.js b/src/actions/organization/members/get.js new file mode 100644 index 000000000..4ac0c69e0 --- /dev/null +++ b/src/actions/organization/members/get.js @@ -0,0 +1,47 @@ +const { ActionTransport } = require('@microfleet/core'); +const redisKey = require('../../../utils/key'); +const getUserId = require('../../../utils/userData/get-user-id'); +const { getMemberData } = require('../../../utils/organization/get-organization-members'); +const { checkOrganizationExists } = require('../../../utils/organization'); +const { + ORGANIZATIONS_MEMBERS, + USERS_METADATA, + ErrorUserNotMember, +} = require('../../../constants'); + +/** + * @api {amqp} .members.Get Get organization member + * @apiVersion 1.0.0 + * @apiName members.get + * @apiGroup Organizations + * + * @apiDescription This should be used to get member. + * + * @apiParam (Payload) {String} organizationId - organization id. + * @apiParam (Payload) {String} username - member email. + */ +async function getMember({ params }) { + const { redis, config } = this; + const { organizationId, username } = params; + const { audience } = config.organizations; + + const userId = await getUserId.call(this, username); + const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId); + const memberMetadataKey = redisKey(userId, USERS_METADATA, audience); + const userInOrganization = await redis.hget(memberMetadataKey, organizationId); + if (!userInOrganization) { + throw ErrorUserNotMember; + } + + const member = await getMemberData.call(this, memberKey); + + return { + data: { + attributes: member, + }, + }; +} + +getMember.allowed = checkOrganizationExists; +getMember.transports = [ActionTransport.amqp, ActionTransport.internal]; +module.exports = getMember; diff --git a/src/actions/organization/members/update.js b/src/actions/organization/members/update.js new file mode 100644 index 000000000..48a545c07 --- /dev/null +++ b/src/actions/organization/members/update.js @@ -0,0 +1,51 @@ +const { ActionTransport } = require('@microfleet/core'); +const redisKey = require('../../../utils/key'); +const getUserId = require('../../../utils/userData/get-user-id'); +const updateMember = require('../../../utils/organization/update-organization-members'); +const { checkOrganizationExists } = require('../../../utils/organization'); +const { getMemberData } = require('../../../utils/organization/get-organization-members'); +const { + ORGANIZATIONS_MEMBERS, + USERS_METADATA, + ErrorUserNotMember, +} = require('../../../constants'); + +/** + * @api {amqp} .members.update Update organization member + * @apiVersion 1.0.0 + * @apiName members.update + * @apiGroup Organizations + * + * @apiDescription This should be used to update member. + * + * @apiParam (Payload) {String} organizationId - organization id. + * @apiParam (Payload) {String} username - member email. + * @apiParam (Payload) {Object[]} [data] - supports `$set key:value`, `$remove keys[]` + */ +async function updateMemberData({ params }) { + const { redis, config } = this; + const { organizationId, username, data } = params; + const { audience } = config.organizations; + + const userId = await getUserId.call(this, username); + const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId); + const memberMetadataKey = redisKey(userId, USERS_METADATA, audience); + const userInOrganization = await redis.hget(memberMetadataKey, organizationId); + + if (!userInOrganization) { + throw ErrorUserNotMember; + } + + await updateMember.call(this, memberKey, data); + const member = await getMemberData.call(this, memberKey); + + return { + data: { + attributes: member, + }, + }; +} + +updateMemberData.allowed = checkOrganizationExists; +updateMemberData.transports = [ActionTransport.amqp, ActionTransport.internal]; +module.exports = updateMemberData; diff --git a/src/utils/organization/get-organization-members.js b/src/utils/organization/get-organization-members.js index 3af40d415..6cb71e98f 100644 --- a/src/utils/organization/get-organization-members.js +++ b/src/utils/organization/get-organization-members.js @@ -22,3 +22,4 @@ async function getOrganizationMembers(organizationId) { } module.exports = getOrganizationMembers; +module.exports.getMemberData = getMemberData; diff --git a/src/utils/organization/update-organization-members.js b/src/utils/organization/update-organization-members.js new file mode 100644 index 000000000..baee8af7e --- /dev/null +++ b/src/utils/organization/update-organization-members.js @@ -0,0 +1,27 @@ +/* eslint-disable no-mixed-operators */ +const mapValues = require('lodash/mapValues'); +const handlePipeline = require('../pipeline-error'); + +const JSONStringify = (data) => JSON.stringify(data); + +async function updateMember(memberKey, memberData) { + const { redis } = this; + const pipeline = redis.pipeline(); + + const { $remove } = memberData; + const $removeOps = $remove && $remove.length || 0; + if ($removeOps > 0) { + pipeline.hdel(memberKey, $remove); + } + + const { $set } = memberData; + const $setKeys = $set && Object.keys($set); + const $setLength = $setKeys && $setKeys.length || 0; + if ($setLength > 0) { + pipeline.hmset(memberKey, mapValues($set, JSONStringify)); + } + + return pipeline.exec().then(handlePipeline); +} + +module.exports = updateMember; diff --git a/test/suites/actions/organization/members/get.js b/test/suites/actions/organization/members/get.js new file mode 100644 index 000000000..98903bc69 --- /dev/null +++ b/test/suites/actions/organization/members/get.js @@ -0,0 +1,34 @@ +/* eslint-disable promise/always-return, no-prototype-builtins */ +const { inspectPromise } = require('@makeomatic/deploy'); +const assert = require('assert'); +const { createOrganization, createMembers } = require('../../../../helpers/organization'); + +describe('#get organization member', function registerSuite() { + this.timeout(50000); + + beforeEach(global.startService); + beforeEach(function pretest() { return createMembers.call(this); }); + beforeEach(function pretest() { return createOrganization.call(this); }); + afterEach(global.clearRedis); + + it('must reject invalid params and return detailed error', function test() { + return this.dispatch('users.organization.members.get', {}) + .reflect() + .then(inspectPromise(false)) + .then((response) => { + assert.equal(response.name, 'HttpStatusError'); + assert.equal(response.errors.length, 2); + }); + }); + + it('must be able to get member', async function test() { + const opts = { + organizationId: this.organization.id, + username: this.userNames[0].email, + }; + + await this.dispatch('users.organization.members.get', opts).then((response) => { + assert.deepEqual(response.data.attributes.username, opts.username); + }); + }); +}); diff --git a/test/suites/actions/organization/members/update.js b/test/suites/actions/organization/members/update.js new file mode 100644 index 000000000..72433e71e --- /dev/null +++ b/test/suites/actions/organization/members/update.js @@ -0,0 +1,39 @@ +/* eslint-disable promise/always-return, no-prototype-builtins */ +const { inspectPromise } = require('@makeomatic/deploy'); +const assert = require('assert'); +const { createOrganization, createMembers } = require('../../../../helpers/organization'); + +describe('#update organization member', function registerSuite() { + this.timeout(50000); + + beforeEach(global.startService); + beforeEach(function pretest() { return createMembers.call(this); }); + beforeEach(function pretest() { return createOrganization.call(this); }); + afterEach(global.clearRedis); + + it('must reject invalid params and return detailed error', function test() { + return this.dispatch('users.organization.members.update', {}) + .reflect() + .then(inspectPromise(false)) + .then((response) => { + assert.equal(response.name, 'HttpStatusError'); + assert.equal(response.errors.length, 3); + }); + }); + + it('must be able to update member', async function test() { + const opts = { + organizationId: this.organization.id, + username: this.userNames[0].email, + data: { + $set: { + avatar: 'test-avatar', + }, + }, + }; + + await this.dispatch('users.organization.members.update', opts).then((response) => { + assert.deepEqual(response.data.attributes.avatar, 'test-avatar'); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 985ec86c7..88b13dbeb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9739,9 +9739,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.11.6" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.6.tgz#144b50d3e05eadd3ad4dd047c60ca541a8cd4e9c" - integrity sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g== + version "3.12.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.0.tgz#b943f129275c41d435eb54b643bbffee71dccf57" + integrity sha512-8lBMSkFZuAK7gGF8LswsXmir8eX8d2AAMOnxSDWjKBx/fBR6MypQjs78m6ML9zQVp1/hD4TBdfeMZMC7nW1TAA== uid-number@0.0.6: version "0.0.6"