From d79ddf10fbd6f8bce25e57cd697ae7c6c71f450c Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 23 Apr 2024 08:39:36 -0600 Subject: [PATCH] fix: `afterCreateUser` callback being called before setting user's roles (#30309) Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> --- .changeset/tame-ducks-turn.md | 5 +++++ .../app/authentication/server/startup/index.js | 15 ++++++++++----- .../app/lib/server/functions/saveUser.js | 3 ++- apps/meteor/lib/callbacks.ts | 2 +- .../api/livechat/19-business-hours.ts | 18 ++++++++++++++++++ 5 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 .changeset/tame-ducks-turn.md diff --git a/.changeset/tame-ducks-turn.md b/.changeset/tame-ducks-turn.md new file mode 100644 index 00000000000..0ad730b9b31 --- /dev/null +++ b/.changeset/tame-ducks-turn.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed a problem that caused `afterCreateUser` callback to be called without new user's roles inside. This caused Omnichannel Business Hour manager to ignore these users from assigning open business hours until the manager restarted or the business hour restarted. diff --git a/apps/meteor/app/authentication/server/startup/index.js b/apps/meteor/app/authentication/server/startup/index.js index 0f398c76654..cc5a04c275b 100644 --- a/apps/meteor/app/authentication/server/startup/index.js +++ b/apps/meteor/app/authentication/server/startup/index.js @@ -265,10 +265,14 @@ Accounts.onCreateUser(function (...args) { const { insertUserDoc } = Accounts; const insertUserDocAsync = async function (options, user) { - const globalRoles = []; + const globalRoles = new Set(); + + if (Match.test(options.globalRoles, [String]) && options.globalRoles.length > 0) { + options.globalRoles.map((role) => globalRoles.add(role)); + } if (Match.test(user.globalRoles, [String]) && user.globalRoles.length > 0) { - globalRoles.push(...user.globalRoles); + user.globalRoles.map((role) => globalRoles.add(role)); } delete user.globalRoles; @@ -277,11 +281,12 @@ const insertUserDocAsync = async function (options, user) { const defaultAuthServiceRoles = parseCSV(settings.get('Accounts_Registration_AuthenticationServices_Default_Roles') || ''); if (defaultAuthServiceRoles.length > 0) { - globalRoles.push(...defaultAuthServiceRoles); + defaultAuthServiceRoles.map((role) => globalRoles.add(role)); } } - const roles = getNewUserRoles(globalRoles); + const arrayGlobalRoles = [...globalRoles]; + const roles = options.skipNewUserRolesSetting ? arrayGlobalRoles : getNewUserRoles(arrayGlobalRoles); if (!user.type) { user.type = 'user'; @@ -326,7 +331,7 @@ const insertUserDocAsync = async function (options, user) { await addUserRolesAsync(_id, roles); // Make user's roles to be present on callback - user = await Users.findOneById(_id, { projection: { username: 1, type: 1 } }); + user = await Users.findOneById(_id, { projection: { username: 1, type: 1, roles: 1 } }); if (user.username) { if (options.joinDefaultChannels !== false) { diff --git a/apps/meteor/app/lib/server/functions/saveUser.js b/apps/meteor/app/lib/server/functions/saveUser.js index f34a54432c4..3a2808b4171 100644 --- a/apps/meteor/app/lib/server/functions/saveUser.js +++ b/apps/meteor/app/lib/server/functions/saveUser.js @@ -276,6 +276,8 @@ const saveNewUser = async function (userData, sendPassword) { password: userData.password, joinDefaultChannels: userData.joinDefaultChannels, isGuest, + globalRoles: roles, + skipNewUserRolesSetting: true, }; if (userData.email) { createUser.email = userData.email; @@ -285,7 +287,6 @@ const saveNewUser = async function (userData, sendPassword) { const updateUser = { $set: { - roles, ...(typeof userData.name !== 'undefined' && { name: userData.name }), settings: userData.settings || {}, }, diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 6dfd53b12c6..9b5f1e69b1d 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -124,7 +124,7 @@ type ChainedCallbackSignatures = { 'livechat.onLoadConfigApi': (config: { room: IOmnichannelRoom }) => Record; - 'afterCreateUser': (user: IUser) => IUser; + 'afterCreateUser': (user: AtLeast) => IUser; 'afterDeleteRoom': (rid: IRoom['_id']) => IRoom['_id']; 'livechat:afterOnHold': (room: Pick) => Pick; 'livechat:afterOnHoldChatResumed': (room: Pick) => Pick; diff --git a/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts b/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts index 8b6dc224377..869f36fd969 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts @@ -838,6 +838,24 @@ describe('LIVECHAT - business hours', function () { expect(latestAgent.statusLivechat).to.be.undefined; }); + describe('Special Case - Agent created, BH already enabled', () => { + let newAgent: ILivechatAgent; + let newAgentCredentials: IUserCredentialsHeader; + before(async () => { + newAgent = await createUser({ roles: ['user', 'livechat-agent'] }); + newAgentCredentials = await login(newAgent.username, password); + }); + after(async () => { + await deleteUser(newAgent); + }); + it('should verify a newly created agent to be assigned to the default business hour', async () => { + const latestAgent: ILivechatAgent = await getMe(newAgentCredentials as any); + expect(latestAgent).to.be.an('object'); + expect(latestAgent.openBusinessHours).to.be.an('array').of.length(1); + expect(latestAgent?.openBusinessHours?.[0]).to.be.equal(defaultBH._id); + }); + }); + after(async () => { await deleteUser(agent._id); });