From bcebb6cc8f3a2787b3775a1fd5e3fc8c0257cdbe Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 25 Sep 2023 20:23:30 -0300 Subject: [PATCH 01/14] initial improvements --- apps/meteor/app/api/server/v1/groups.ts | 9 +- .../app/lib/server/functions/createRoom.ts | 134 +++++++++++------- .../lib/server/methods/createPrivateGroup.ts | 28 ++-- apps/meteor/server/lib/roles/addUserRoles.ts | 9 +- .../meteor/server/models/raw/Subscriptions.ts | 33 +++++ .../core-services/src/types/IRoomService.ts | 1 + 6 files changed, 142 insertions(+), 72 deletions(-) diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index df54b683fda4..e4681ddaaf21 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -302,9 +302,10 @@ API.v1.addRoute( { authRequired: true }, { async post() { - if (!(await hasPermissionAsync(this.userId, 'create-p'))) { - return API.v1.unauthorized(); - } + // TODO check error returned by createPrivateGroupMethod + // if (!(await hasPermissionAsync(this.userId, 'create-p'))) { + // return API.v1.unauthorized(); + // } if (!this.bodyParams.name) { return API.v1.failure('Body param "name" is required'); @@ -324,7 +325,7 @@ API.v1.addRoute( const readOnly = typeof this.bodyParams.readOnly !== 'undefined' ? this.bodyParams.readOnly : false; const result = await createPrivateGroupMethod( - this.userId, + this.user, this.bodyParams.name, this.bodyParams.members ? this.bodyParams.members : [], readOnly, diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 192139f96b7c..2e7a6ccd2204 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -19,10 +19,86 @@ const isValidName = (name: unknown): name is string => { const onlyUsernames = (members: unknown): members is string[] => Array.isArray(members) && members.every((member) => typeof member === 'string'); +async function createUsersSubscriptions({ + room, + shouldBeHandledByFederation, + members, + now, + owner, + options, +}: { + room: IRoom; + shouldBeHandledByFederation: boolean; + members: string[]; + now: Date; + owner: IUser; + options?: ICreateRoomParams['options']; +}) { + if (shouldBeHandledByFederation) { + const extra: Partial = options?.subscriptionExtra || {}; + extra.open = true; + extra.ls = now; + + if (room.prid) { + extra.prid = room.prid; + } + + await Subscriptions.createWithRoomAndUser(room, owner, extra); + + return; + } + + const membersCursor = Users.findUsersByUsernames(members, { + projection: { 'username': 1, 'settings.preferences': 1, 'federated': 1, 'roles': 1 }, + }); + + let total = 0; + + const subs = []; + + for await (const member of membersCursor) { + try { + await callbacks.run('federation.beforeAddUserToARoom', { user: member, inviter: owner }, room); + await callbacks.run('beforeAddedToRoom', { user: member, inviter: owner }); + } catch (error) { + continue; + } + + const extra: Partial = options?.subscriptionExtra || {}; + + extra.open = true; + + if (room.prid) { + extra.prid = room.prid; + } + + if (member.username === owner.username) { + extra.ls = now; + extra.roles = ['owner']; + } + + total++; + + // TODO move this to outside the loop + if (!['d', 'l'].includes(room.t)) { + await Users.addRoomByUserId(member._id, room._id); + } + + subs.push({ + user: member, + extraData: extra, + }); + } + + await Subscriptions.createWithRoomAndManyUsers(room, subs); + + await Rooms.incUsersCountById(room._id, total); +} + export const createRoom = async ( type: T, name: T extends 'd' ? undefined : string, - ownerUsername: string | undefined, + owner: IUser | undefined, members: T extends 'd' ? IUser[] : string[] = [], excludeSelf?: boolean, readOnly?: boolean, @@ -45,7 +121,7 @@ export const createRoom = async ( // options, }); if (type === 'd') { - return createDirectRoom(members as IUser[], extraData, { ...options, creator: options?.creator || ownerUsername }); + return createDirectRoom(members as IUser[], extraData, { ...options, creator: options?.creator || owner?.username }); } if (!onlyUsernames(members)) { @@ -61,15 +137,13 @@ export const createRoom = async ( }); } - if (!ownerUsername) { + if (!owner) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: 'RocketChat.createRoom', }); } - const owner = await Users.findOneByUsernameIgnoringCase(ownerUsername, { projection: { username: 1, name: 1 } }); - - if (!ownerUsername || !owner) { + if (!owner?.username) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: 'RocketChat.createRoom', }); @@ -138,51 +212,15 @@ export const createRoom = async ( if (type === 'c') { await callbacks.run('beforeCreateChannel', owner, roomProps); } - const room = await Rooms.createWithFullRoomData(roomProps); - const shouldBeHandledByFederation = room.federated === true || ownerUsername.includes(':'); - if (shouldBeHandledByFederation) { - const extra: Partial = options?.subscriptionExtra || {}; - extra.open = true; - extra.ls = now; - if (room.prid) { - extra.prid = room.prid; - } - - await Subscriptions.createWithRoomAndUser(room, owner, extra); - } else { - for await (const username of [...new Set(members)]) { - const member = await Users.findOneByUsername(username, { - projection: { 'username': 1, 'settings.preferences': 1, 'federated': 1, 'roles': 1 }, - }); - if (!member) { - continue; - } - - try { - await callbacks.run('federation.beforeAddUserToARoom', { user: member, inviter: owner }, room); - await callbacks.run('beforeAddedToRoom', { user: member, inviter: owner }); - } catch (error) { - continue; - } - - const extra: Partial = options?.subscriptionExtra || {}; - - extra.open = true; + const room = await Rooms.createWithFullRoomData(roomProps); - if (room.prid) { - extra.prid = room.prid; - } + const shouldBeHandledByFederation = room.federated === true || owner.username.includes(':'); - if (username === owner.username) { - extra.ls = now; - } - - await Subscriptions.createWithRoomAndUser(room, member, extra); - } - } + await createUsersSubscriptions({ room, members, now, owner, options, shouldBeHandledByFederation }); - await addUserRolesAsync(owner._id, ['owner'], room._id); + // TODO remove this and add logic to createUsersSubscriptions + // await addUserRolesAsync(owner._id, ['owner'], room._id); if (type === 'c') { if (room.teamId) { @@ -191,7 +229,7 @@ export const createRoom = async ( await Message.saveSystemMessage('user-added-room-to-team', team.roomId, room.name || '', owner); } } - await callbacks.run('afterCreateChannel', owner, room); + callbacks.runAsync('afterCreateChannel', owner, room); } else if (type === 'p') { callbacks.runAsync('afterCreatePrivateGroup', owner, room); } diff --git a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts index 65298949a345..af0229436adb 100644 --- a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts +++ b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts @@ -1,4 +1,4 @@ -import type { ICreatedRoom } from '@rocket.chat/core-typings'; +import type { ICreatedRoom, IUser } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; @@ -21,7 +21,7 @@ declare module '@rocket.chat/ui-contexts' { } export const createPrivateGroupMethod = async ( - userId: string, + user: IUser, name: string, members: string[], readOnly = false, @@ -35,23 +35,12 @@ export const createPrivateGroupMethod = async ( > => { check(name, String); check(members, Match.Optional([String])); - if (!userId) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'createPrivateGroup', - }); - } - const user = await Users.findOneById(userId, { projection: { username: 1 } }); - if (!user) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'createPrivateGroup', - }); - } - if (!(await hasPermissionAsync(userId, 'create-p'))) { + if (!(await hasPermissionAsync(user._id, 'create-p'))) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'createPrivateGroup' }); } - return createRoom('p', name, user.username, members, excludeSelf, readOnly, { + return createRoom('p', name, user, members, excludeSelf, readOnly, { customFields, ...extraData, }); @@ -67,6 +56,13 @@ Meteor.methods({ }); } - return createPrivateGroupMethod(uid, name, members, readOnly, customFields, extraData); + const user = await Users.findOneById(uid, { projection: { username: 1 } }); + if (!user) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'createPrivateGroup', + }); + } + + return createPrivateGroupMethod(user, name, members, readOnly, customFields, extraData); }, }); diff --git a/apps/meteor/server/lib/roles/addUserRoles.ts b/apps/meteor/server/lib/roles/addUserRoles.ts index 395056903ae4..16cb0ad19b77 100644 --- a/apps/meteor/server/lib/roles/addUserRoles.ts +++ b/apps/meteor/server/lib/roles/addUserRoles.ts @@ -9,10 +9,11 @@ export const addUserRolesAsync = async (userId: IUser['_id'], roleIds: IRole['_i return false; } - const user = await Users.findOneById(userId, { projection: { _id: 1 } }); - if (!user) { - throw new MeteorError('error-invalid-user', 'Invalid user'); - } + // TODO remove this + // const user = await Users.findOneById(userId, { projection: { _id: 1 } }); + // if (!user) { + // throw new MeteorError('error-invalid-user', 'Invalid user'); + // } if (!(await validateRoleList(roleIds))) { throw new MeteorError('error-invalid-role', 'Invalid role'); diff --git a/apps/meteor/server/models/raw/Subscriptions.ts b/apps/meteor/server/models/raw/Subscriptions.ts index 4b42367bad05..167f982fe67f 100644 --- a/apps/meteor/server/models/raw/Subscriptions.ts +++ b/apps/meteor/server/models/raw/Subscriptions.ts @@ -17,6 +17,7 @@ import type { IndexDescription, UpdateFilter, InsertOneResult, + InsertManyResult, } from 'mongodb'; import { getDefaultSubscriptionPref } from '../../../app/utils/lib/getDefaultSubscriptionPref'; @@ -1605,6 +1606,38 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri return result; } + async createWithRoomAndManyUsers( + room: IRoom, + users: { user: IUser; extraData: Record }[] = [], + ): Promise> { + const subscriptions = users.map(({ user, extraData }) => ({ + open: false, + alert: false, + unread: 0, + userMentions: 0, + groupMentions: 0, + ts: room.ts, + rid: room._id, + name: room.name, + fname: room.fname, + ...(room.customFields && { customFields: room.customFields }), + t: room.t, + u: { + _id: user._id, + username: user.username, + name: user.name, + }, + ...(room.prid && { prid: room.prid }), + ...getDefaultSubscriptionPref(user), + ...extraData, + })); + + // @ts-expect-error - types not good :( + const result = await this.insertMany(subscriptions); + + return result; + } + // REMOVE async removeByUserId(userId: string): Promise { const query = { diff --git a/packages/core-services/src/types/IRoomService.ts b/packages/core-services/src/types/IRoomService.ts index d9eee82029af..f7be69ce2a7c 100644 --- a/packages/core-services/src/types/IRoomService.ts +++ b/packages/core-services/src/types/IRoomService.ts @@ -4,6 +4,7 @@ export interface ISubscriptionExtraData { open: boolean; ls?: Date; prid?: string; + roles?: string[]; } interface ICreateRoomOptions extends Partial> { From 568236f42501609a0de85d2c05cabe320333f17f Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 12:02:25 -0300 Subject: [PATCH 02/14] add model definitions --- apps/meteor/server/models/raw/Users.js | 12 ++++++++++++ packages/model-typings/src/models/IUsersModel.ts | 1 + 2 files changed, 13 insertions(+) diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 0663bbdcda28..6d41b05c76f3 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1488,6 +1488,18 @@ export class UsersRaw extends BaseRaw { ); } + addRoomByUserIds(uids, rid) { + return this.updateMany( + { + _id: { $in: uids }, + __rooms: { $ne: rid }, + }, + { + $addToSet: { __rooms: rid }, + }, + ); + } + removeRoomByRoomIds(rids) { return this.updateMany( { diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index c0ce51f79f45..d5d2a2f09447 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -239,6 +239,7 @@ export interface IUsersModel extends IBaseModel { removeAllRoomsByUserId(userId: string): Promise; removeRoomByUserId(userId: string, rid: string): Promise; addRoomByUserId(userId: string, rid: string): Promise; + addRoomByUserIds(uids: string[], rid: string): Promise; removeRoomByRoomIds(rids: string[]): Promise; getLoginTokensByUserId(userId: string): FindCursor; addPersonalAccessTokenToUser(data: { userId: string; loginTokenObject: IPersonalAccessToken }): Promise; From 3bb7bed42131d32db166de3b1f28611c7ce04f65 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:41:24 -0300 Subject: [PATCH 03/14] fix usages of createRoom with new signature --- apps/meteor/app/apps/server/bridges/rooms.ts | 6 +++++- .../server/methods/createDiscussion.ts | 2 +- .../server/classes/ImportDataConverter.ts | 6 +++++- .../app/lib/server/methods/createChannel.ts | 5 ++--- .../meteor-accounts-saml/server/lib/SAML.ts | 4 ++-- .../app/slashcommands-create/server/server.ts | 8 ++++++-- .../slashcommands-inviteall/server/server.ts | 5 ++++- apps/meteor/ee/server/lib/ldap/Manager.ts | 18 ++++++++++-------- apps/meteor/ee/server/lib/oauth/Manager.ts | 10 ++++++++-- .../server/methods/createDirectMessage.ts | 6 +++++- .../rocket-chat/adapters/Room.ts | 15 +++++++++++++-- apps/meteor/server/services/room/service.ts | 6 ++---- 12 files changed, 63 insertions(+), 28 deletions(-) diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index 481292d61790..91b0049513f0 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -55,7 +55,11 @@ export class AppRoomBridge extends RoomBridge { } private async createPrivateGroup(userId: string, room: ICoreRoom, members: string[]): Promise { - return (await createPrivateGroupMethod(userId, room.name || '', members, room.ro, room.customFields, this.prepareExtraData(room))).rid; + const user = await Users.findOneById(userId); + if (!user) { + throw new Error('Invalid user'); + } + return (await createPrivateGroupMethod(user, room.name || '', members, room.ro, room.customFields, this.prepareExtraData(room))).rid; } protected async getById(roomId: string, appId: string): Promise { diff --git a/apps/meteor/app/discussion/server/methods/createDiscussion.ts b/apps/meteor/app/discussion/server/methods/createDiscussion.ts index ce5c09947a60..50f2f5877657 100644 --- a/apps/meteor/app/discussion/server/methods/createDiscussion.ts +++ b/apps/meteor/app/discussion/server/methods/createDiscussion.ts @@ -156,7 +156,7 @@ const create = async ({ const discussion = await createRoom( type, name, - user.username as string, + user, [...new Set(invitedUsers)].filter(Boolean), false, false, diff --git a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts index f241879cdc67..1b596d625d9b 100644 --- a/apps/meteor/app/importer/server/classes/ImportDataConverter.ts +++ b/apps/meteor/app/importer/server/classes/ImportDataConverter.ts @@ -1034,7 +1034,11 @@ export class ImportDataConverter { return; } if (roomData.t === 'p') { - roomInfo = await createPrivateGroupMethod(startedByUserId, roomData.name, members, false, {}, {}, true); + const user = await Users.findOneById(startedByUserId); + if (!user) { + throw new Error('importer-channel-invalid-creator'); + } + roomInfo = await createPrivateGroupMethod(user, roomData.name, members, false, {}, {}, true); } else { roomInfo = await createChannelMethod(startedByUserId, roomData.name, members, false, {}, {}, true); } diff --git a/apps/meteor/app/lib/server/methods/createChannel.ts b/apps/meteor/app/lib/server/methods/createChannel.ts index ff8182cec8c9..98cea517bed4 100644 --- a/apps/meteor/app/lib/server/methods/createChannel.ts +++ b/apps/meteor/app/lib/server/methods/createChannel.ts @@ -35,8 +35,7 @@ export const createChannelMethod = async ( throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'createChannel' }); } - const user = await Users.findOneById(userId, { projection: { username: 1 } }); - + const user = await Users.findOneById(userId); if (!user?.username) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'createChannel' }); } @@ -44,7 +43,7 @@ export const createChannelMethod = async ( if (!(await hasPermissionAsync(userId, 'create-c'))) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'createChannel' }); } - return createRoom('c', name, user.username, members, excludeSelf, readOnly, { + return createRoom('c', name, user, members, excludeSelf, readOnly, { customFields, ...extraData, }); diff --git a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts index 06c3014a8a56..f62ab71f2302 100644 --- a/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts +++ b/apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts @@ -480,7 +480,6 @@ export class SAML { continue; } - const room = await Rooms.findOneByNameAndType(roomName, 'c', {}); const privRoom = await Rooms.findOneByNameAndType(roomName, 'p', {}); if (privRoom && includePrivateChannelsInUpdate === true) { @@ -488,6 +487,7 @@ export class SAML { continue; } + const room = await Rooms.findOneByNameAndType(roomName, 'c', {}); if (room) { await addUserToRoom(room._id, user); continue; @@ -496,7 +496,7 @@ export class SAML { if (!room && !privRoom) { // If the user doesn't have an username yet, we can't create new rooms for them if (user.username) { - await createRoom('c', roomName, user.username); + await createRoom('c', roomName, user); } } } diff --git a/apps/meteor/app/slashcommands-create/server/server.ts b/apps/meteor/app/slashcommands-create/server/server.ts index a3c70f012fa1..104d50c56926 100644 --- a/apps/meteor/app/slashcommands-create/server/server.ts +++ b/apps/meteor/app/slashcommands-create/server/server.ts @@ -1,6 +1,6 @@ import { api } from '@rocket.chat/core-services'; import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings'; -import { Rooms } from '@rocket.chat/models'; +import { Rooms, Users } from '@rocket.chat/models'; import { i18n } from '../../../server/lib/i18n'; import { createChannelMethod } from '../../lib/server/methods/createChannel'; @@ -50,7 +50,11 @@ slashCommands.add({ } if (getParams(params).indexOf('private') > -1) { - await createPrivateGroupMethod(userId, channelStr, []); + const user = await Users.findOneById(userId); + if (!user) { + return; + } + await createPrivateGroupMethod(user, channelStr, []); return; } diff --git a/apps/meteor/app/slashcommands-inviteall/server/server.ts b/apps/meteor/app/slashcommands-inviteall/server/server.ts index 9917775aca06..5376bd6ae64b 100644 --- a/apps/meteor/app/slashcommands-inviteall/server/server.ts +++ b/apps/meteor/app/slashcommands-inviteall/server/server.ts @@ -37,6 +37,9 @@ function inviteAll(type: T): SlashCommand['callback'] { } const user = await Users.findOneById(userId); + if (!user) { + return; + } const lng = user?.language || settings.get('Language') || 'en'; const baseChannel = type === 'to' ? await Rooms.findOneById(message.rid) : await Rooms.findOneByName(channel); @@ -69,7 +72,7 @@ function inviteAll(type: T): SlashCommand['callback'] { const users = (await cursor.toArray()).map((s: ISubscription) => s.u.username).filter(isTruthy); if (!targetChannel && ['c', 'p'].indexOf(baseChannel.t) > -1) { - baseChannel.t === 'c' ? await createChannelMethod(userId, channel, users) : await createPrivateGroupMethod(userId, channel, users); + baseChannel.t === 'c' ? await createChannelMethod(userId, channel, users) : await createPrivateGroupMethod(user, channel, users); void api.broadcast('notify.ephemeralMessage', userId, message.rid, { msg: i18n.t('Channel_created', { postProcess: 'sprintf', diff --git a/apps/meteor/ee/server/lib/ldap/Manager.ts b/apps/meteor/ee/server/lib/ldap/Manager.ts index deb6cdcec666..6c04574ad557 100644 --- a/apps/meteor/ee/server/lib/ldap/Manager.ts +++ b/apps/meteor/ee/server/lib/ldap/Manager.ts @@ -1,6 +1,6 @@ import { Team } from '@rocket.chat/core-services'; import type { ILDAPEntry, IUser, IRoom, IRole, IImportUser, IImportRecord } from '@rocket.chat/core-typings'; -import { Users as UsersRaw, Roles, Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models'; +import { Users, Roles, Subscriptions as SubscriptionsRaw, Rooms } from '@rocket.chat/models'; import type ldapjs from 'ldapjs'; import type { @@ -271,10 +271,12 @@ export class LDAPEEManager extends LDAPManager { logger.debug(`Channel '${channel}' doesn't exist, creating it.`); const roomOwner = settings.get('LDAP_Sync_User_Data_Channels_Admin') || ''; - // #ToDo: Remove typecastings when createRoom is converted to ts. - const room = await createRoom('c', channel, roomOwner, [], false, false, { + + const user = await Users.findOneByUsernameIgnoringCase(roomOwner); + + const room = await createRoom('c', channel, user, [], false, false, { customFields: { ldap: true }, - } as any); + }); if (!room?.rid) { logger.error(`Unable to auto-create channel '${channel}' during ldap sync.`); return; @@ -574,7 +576,7 @@ export class LDAPEEManager extends LDAPManager { } private static async updateExistingUsers(ldap: LDAPConnection, converter: LDAPDataConverter): Promise { - const users = await UsersRaw.findLDAPUsers().toArray(); + const users = await Users.findLDAPUsers().toArray(); for await (const user of users) { const ldapUser = await this.findLDAPUser(ldap, user); @@ -586,7 +588,7 @@ export class LDAPEEManager extends LDAPManager { } private static async updateUserAvatars(ldap: LDAPConnection): Promise { - const users = await UsersRaw.findLDAPUsers().toArray(); + const users = await Users.findLDAPUsers().toArray(); for await (const user of users) { const ldapUser = await this.findLDAPUser(ldap, user); if (!ldapUser) { @@ -615,7 +617,7 @@ export class LDAPEEManager extends LDAPManager { } private static async logoutDeactivatedUsers(ldap: LDAPConnection): Promise { - const users = await UsersRaw.findConnectedLDAPUsers().toArray(); + const users = await Users.findConnectedLDAPUsers().toArray(); for await (const user of users) { const ldapUser = await this.findLDAPUser(ldap, user); @@ -624,7 +626,7 @@ export class LDAPEEManager extends LDAPManager { } if (this.isUserDeactivated(ldapUser)) { - await UsersRaw.unsetLoginTokens(user._id); + await Users.unsetLoginTokens(user._id); } } } diff --git a/apps/meteor/ee/server/lib/oauth/Manager.ts b/apps/meteor/ee/server/lib/oauth/Manager.ts index b24d7436a784..b75c8aa9a7a5 100644 --- a/apps/meteor/ee/server/lib/oauth/Manager.ts +++ b/apps/meteor/ee/server/lib/oauth/Manager.ts @@ -1,6 +1,6 @@ import type { IUser } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; -import { Roles, Rooms } from '@rocket.chat/models'; +import { Roles, Rooms, Users } from '@rocket.chat/models'; import { addUserToRoom } from '../../../../app/lib/server/functions/addUserToRoom'; import { createRoom } from '../../../../app/lib/server/functions/createRoom'; @@ -20,6 +20,12 @@ export class OAuthEEManager { if (channelsMap && user && identity && groupClaimName) { const groupsFromSSO = identity[groupClaimName] || []; + const userChannelAdmin = await Users.findOneByUsernameIgnoringCase(channelsAdmin); + if (!userChannelAdmin) { + logger.error(`could not create channel, user not found: ${channelsAdmin}`); + return; + } + for await (const ssoGroup of Object.keys(channelsMap)) { if (typeof ssoGroup === 'string') { let channels = channelsMap[ssoGroup]; @@ -30,7 +36,7 @@ export class OAuthEEManager { const name = await getValidRoomName(channel.trim(), undefined, { allowDuplicates: true }); let room = await Rooms.findOneByNonValidatedName(name); if (!room) { - const createdRoom = await createRoom('c', channel, channelsAdmin, [], false, false); + const createdRoom = await createRoom('c', channel, userChannelAdmin, [], false, false); if (!createdRoom?.rid) { logger.error(`could not create channel ${channel}`); return; diff --git a/apps/meteor/server/methods/createDirectMessage.ts b/apps/meteor/server/methods/createDirectMessage.ts index d92c7e46292e..ccbfe8916cae 100644 --- a/apps/meteor/server/methods/createDirectMessage.ts +++ b/apps/meteor/server/methods/createDirectMessage.ts @@ -104,7 +104,11 @@ export async function createDirectMessage( } catch (error) { throw new Meteor.Error((error as any)?.message); } - const { _id: rid, inserted, ...room } = await createRoom('d', undefined, undefined, roomUsers as IUser[], false, undefined, {}, options); + const { + _id: rid, + inserted, + ...room + } = await createRoom<'d'>('d', undefined, undefined, roomUsers as IUser[], false, undefined, {}, options); return { // @ts-expect-error - room type is already defined in the `createRoom` return type diff --git a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts index 018a5f87704c..c4aee8bcf2aa 100644 --- a/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts +++ b/apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Room.ts @@ -58,7 +58,12 @@ export class RocketChatRoomAdapter { .trim() .replace(/ /g, '-'), ); - const { rid, _id } = await createRoom(federatedRoom.getRoomType(), roomName, usernameOrId); + const owner = await Users.findOneByUsernameIgnoringCase(usernameOrId); + if (!owner) { + throw new Error('Cannot create a room without a creator'); + } + + const { rid, _id } = await createRoom(federatedRoom.getRoomType(), roomName, owner); const roomId = rid || _id; await MatrixBridgedRoom.createOrUpdateByLocalRoomId( roomId, @@ -90,10 +95,16 @@ export class RocketChatRoomAdapter { const readonly = false; const excludeSelf = false; const extraData = undefined; + + const owner = await Users.findOneByUsernameIgnoringCase(usernameOrId); + if (!owner) { + throw new Error('Cannot create a room without a creator'); + } + const { rid, _id } = await createRoom( federatedRoom.getRoomType(), federatedRoom.getDisplayName(), - usernameOrId, + owner, federatedRoom.getMembersUsernames(), excludeSelf, readonly, diff --git a/apps/meteor/server/services/room/service.ts b/apps/meteor/server/services/room/service.ts index 61b5bfeee504..7b9b85cecbd0 100644 --- a/apps/meteor/server/services/room/service.ts +++ b/apps/meteor/server/services/room/service.ts @@ -23,15 +23,13 @@ export class RoomService extends ServiceClassInternal implements IRoomService { throw new Error('no-permission'); } - const user = await Users.findOneById>(uid, { - projection: { username: 1 }, - }); + const user = await Users.findOneById(uid); if (!user?.username) { throw new Error('User not found'); } // TODO convert `createRoom` function to "raw" and move to here - return createRoom(type, name, user.username, members, false, readOnly, extraData, options) as unknown as IRoom; + return createRoom(type, name, user, members, false, readOnly, extraData, options) as unknown as IRoom; } async createDirectMessage({ to, from }: { to: string; from: string }): Promise<{ rid: string }> { From 6f060bcd6f521893968eb3a88cb8db00aa24317b Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:42:23 -0300 Subject: [PATCH 04/14] add missing new type --- .../src/models/ISubscriptionsModel.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/model-typings/src/models/ISubscriptionsModel.ts b/packages/model-typings/src/models/ISubscriptionsModel.ts index aebda87c78cb..9e9f3d6dbedc 100644 --- a/packages/model-typings/src/models/ISubscriptionsModel.ts +++ b/packages/model-typings/src/models/ISubscriptionsModel.ts @@ -1,5 +1,15 @@ import type { ISubscription, IRole, IUser, IRoom, RoomType, SpotlightUser } from '@rocket.chat/core-typings'; -import type { FindOptions, FindCursor, UpdateResult, DeleteResult, Document, AggregateOptions, Filter, InsertOneResult } from 'mongodb'; +import type { + FindOptions, + FindCursor, + UpdateResult, + DeleteResult, + Document, + AggregateOptions, + Filter, + InsertOneResult, + InsertManyResult, +} from 'mongodb'; import type { IBaseModel } from './IBaseModel'; @@ -216,6 +226,10 @@ export interface ISubscriptionsModel extends IBaseModel { ): Promise; removeByUserId(userId: string): Promise; createWithRoomAndUser(room: IRoom, user: IUser, extraData?: Record): Promise>; + createWithRoomAndManyUsers( + room: IRoom, + users: { user: IUser; extraData: Record }[], + ): Promise>; removeByRoomIdsAndUserId(rids: string[], userId: string): Promise; removeByRoomIdAndUserId(roomId: string, userId: string): Promise; From bb04fda3e5ac9a14180589a1ffd7edc55d4ba2d8 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:42:35 -0300 Subject: [PATCH 05/14] add check to user query --- apps/meteor/server/models/raw/Users.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 6d41b05c76f3..113f18ea83da 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -384,6 +384,10 @@ export class UsersRaw extends BaseRaw { } findOneByUsernameIgnoringCase(username, options) { + if (!username) { + throw new Error('invalid username'); + } + const query = { username }; return this.findOne(query, { From 5a351dc941716f2f1af6f7259a534c1089a01236 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:43:01 -0300 Subject: [PATCH 06/14] remove redundant user check --- apps/meteor/server/lib/roles/addUserRoles.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/meteor/server/lib/roles/addUserRoles.ts b/apps/meteor/server/lib/roles/addUserRoles.ts index 16cb0ad19b77..a064553f5cb4 100644 --- a/apps/meteor/server/lib/roles/addUserRoles.ts +++ b/apps/meteor/server/lib/roles/addUserRoles.ts @@ -1,6 +1,6 @@ import { MeteorError } from '@rocket.chat/core-services'; import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; -import { Users, Roles } from '@rocket.chat/models'; +import { Roles } from '@rocket.chat/models'; import { validateRoleList } from './validateRoleList'; @@ -9,12 +9,6 @@ export const addUserRolesAsync = async (userId: IUser['_id'], roleIds: IRole['_i return false; } - // TODO remove this - // const user = await Users.findOneById(userId, { projection: { _id: 1 } }); - // if (!user) { - // throw new MeteorError('error-invalid-user', 'Invalid user'); - // } - if (!(await validateRoleList(roleIds))) { throw new MeteorError('error-invalid-role', 'Invalid role'); } From 29604ef28712044f26d9e9b074afa3b4b0f2c67d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:43:17 -0300 Subject: [PATCH 07/14] more improvement to createRoom --- .../app/lib/server/functions/createRoom.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 2e7a6ccd2204..57a1cf6ce86a 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -8,7 +8,6 @@ import { Meteor } from 'meteor/meteor'; import { Apps } from '../../../../ee/server/apps/orchestrator'; import { callbacks } from '../../../../lib/callbacks'; import { beforeCreateRoomCallback } from '../../../../lib/callbacks/beforeCreateRoomCallback'; -import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName'; import { createDirectRoom } from './createDirectRoom'; @@ -48,15 +47,19 @@ async function createUsersSubscriptions({ return; } - const membersCursor = Users.findUsersByUsernames(members, { - projection: { 'username': 1, 'settings.preferences': 1, 'federated': 1, 'roles': 1 }, - }); - let total = 0; const subs = []; + const memberIds = []; + + const membersCursor = Users.findUsersByUsernames(members, { + projection: { 'username': 1, 'settings.preferences': 1, 'federated': 1, 'roles': 1 }, + }); + for await (const member of membersCursor) { + memberIds.push(member._id); + try { await callbacks.run('federation.beforeAddUserToARoom', { user: member, inviter: owner }, room); await callbacks.run('beforeAddedToRoom', { user: member, inviter: owner }); @@ -79,17 +82,16 @@ async function createUsersSubscriptions({ total++; - // TODO move this to outside the loop - if (!['d', 'l'].includes(room.t)) { - await Users.addRoomByUserId(member._id, room._id); - } - subs.push({ user: member, extraData: extra, }); } + if (!['d', 'l'].includes(room.t)) { + await Users.addRoomByUserIds(memberIds, room._id); + } + await Subscriptions.createWithRoomAndManyUsers(room, subs); await Rooms.incUsersCountById(room._id, total); @@ -98,7 +100,7 @@ async function createUsersSubscriptions({ export const createRoom = async ( type: T, name: T extends 'd' ? undefined : string, - owner: IUser | undefined, + owner: T extends 'd' ? IUser | undefined : IUser, members: T extends 'd' ? IUser[] : string[] = [], excludeSelf?: boolean, readOnly?: boolean, From 6910b0e343fe0ac6dfada599af7d3086c1a476d6 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:45:35 -0300 Subject: [PATCH 08/14] add excludeSelf param to groups.create --- apps/meteor/app/api/server/v1/groups.ts | 1 + packages/rest-typings/src/v1/groups/GroupsCreateProps.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index e4681ddaaf21..05b59cf17f30 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -331,6 +331,7 @@ API.v1.addRoute( readOnly, this.bodyParams.customFields, this.bodyParams.extraData, + this.bodyParams.excludeSelf ?? false, ); const room = await Rooms.findOneById(result.rid, { projection: API.v1.defaultFieldsToExclude }); diff --git a/packages/rest-typings/src/v1/groups/GroupsCreateProps.ts b/packages/rest-typings/src/v1/groups/GroupsCreateProps.ts index c34a720bd4b7..7c3781d787c4 100644 --- a/packages/rest-typings/src/v1/groups/GroupsCreateProps.ts +++ b/packages/rest-typings/src/v1/groups/GroupsCreateProps.ts @@ -14,6 +14,7 @@ export type GroupsCreateProps = { encrypted: boolean; teamId?: string; }; + excludeSelf?: boolean; }; const GroupsCreatePropsSchema = { From 52d71e7e4ca51fd06fdc9f72b3fc216ef34be119 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 13:48:46 -0300 Subject: [PATCH 09/14] add excludeSelf param to channels.create --- apps/meteor/app/api/server/v1/channels.ts | 10 +++++++++- .../src/v1/channels/ChannelsCreateProps.ts | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/meteor/app/api/server/v1/channels.ts b/apps/meteor/app/api/server/v1/channels.ts index 4a7aec073442..8e0541b8040b 100644 --- a/apps/meteor/app/api/server/v1/channels.ts +++ b/apps/meteor/app/api/server/v1/channels.ts @@ -670,7 +670,14 @@ async function createChannelValidator(params: { async function createChannel( userId: string, - params: { name?: string; members?: string[]; customFields?: Record; extraData?: Record; readOnly?: boolean }, + params: { + name?: string; + members?: string[]; + customFields?: Record; + extraData?: Record; + readOnly?: boolean; + excludeSelf?: boolean; + }, ): Promise<{ channel: IRoom }> { const readOnly = typeof params.readOnly !== 'undefined' ? params.readOnly : false; const id = await createChannelMethod( @@ -680,6 +687,7 @@ async function createChannel( readOnly, params.customFields, params.extraData, + params.excludeSelf, ); return { diff --git a/packages/rest-typings/src/v1/channels/ChannelsCreateProps.ts b/packages/rest-typings/src/v1/channels/ChannelsCreateProps.ts index c8bb88cfc1c6..e25dfb0ce2fb 100644 --- a/packages/rest-typings/src/v1/channels/ChannelsCreateProps.ts +++ b/packages/rest-typings/src/v1/channels/ChannelsCreateProps.ts @@ -12,6 +12,7 @@ export type ChannelsCreateProps = { encrypted?: boolean; teamId?: string; }; + excludeSelf?: boolean; }; const channelsCreatePropsSchema = { From d0be4e1ae0ad6287c53e582dae4fbe2425610e90 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 18:52:11 -0300 Subject: [PATCH 10/14] code cleanup --- apps/meteor/app/lib/server/functions/createRoom.ts | 9 +-------- apps/meteor/app/lib/server/methods/createPrivateGroup.ts | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 57a1cf6ce86a..c0d548e14424 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -47,8 +47,6 @@ async function createUsersSubscriptions({ return; } - let total = 0; - const subs = []; const memberIds = []; @@ -80,8 +78,6 @@ async function createUsersSubscriptions({ extra.roles = ['owner']; } - total++; - subs.push({ user: member, extraData: extra, @@ -94,7 +90,7 @@ async function createUsersSubscriptions({ await Subscriptions.createWithRoomAndManyUsers(room, subs); - await Rooms.incUsersCountById(room._id, total); + await Rooms.incUsersCountById(room._id, subs.length); } export const createRoom = async ( @@ -221,9 +217,6 @@ export const createRoom = async ( await createUsersSubscriptions({ room, members, now, owner, options, shouldBeHandledByFederation }); - // TODO remove this and add logic to createUsersSubscriptions - // await addUserRolesAsync(owner._id, ['owner'], room._id); - if (type === 'c') { if (room.teamId) { const team = await Team.getOneById(room.teamId); diff --git a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts index af0229436adb..75097b5c89b8 100644 --- a/apps/meteor/app/lib/server/methods/createPrivateGroup.ts +++ b/apps/meteor/app/lib/server/methods/createPrivateGroup.ts @@ -56,7 +56,7 @@ Meteor.methods({ }); } - const user = await Users.findOneById(uid, { projection: { username: 1 } }); + const user = await Users.findOneById(uid); if (!user) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'createPrivateGroup', From 5d869b4722de1a2f55f99d9f96d795fb0fe4117d Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Tue, 26 Sep 2023 19:06:46 -0300 Subject: [PATCH 11/14] check error --- apps/meteor/app/api/server/v1/groups.ts | 46 +++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index 05b59cf17f30..ef18d4256348 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -1,4 +1,4 @@ -import { Team } from '@rocket.chat/core-services'; +import { Team, isMeteorError } from '@rocket.chat/core-services'; import type { IIntegration, IUser, IRoom, RoomType } from '@rocket.chat/core-typings'; import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models'; import { check, Match } from 'meteor/check'; @@ -302,11 +302,6 @@ API.v1.addRoute( { authRequired: true }, { async post() { - // TODO check error returned by createPrivateGroupMethod - // if (!(await hasPermissionAsync(this.userId, 'create-p'))) { - // return API.v1.unauthorized(); - // } - if (!this.bodyParams.name) { return API.v1.failure('Body param "name" is required'); } @@ -324,25 +319,32 @@ API.v1.addRoute( const readOnly = typeof this.bodyParams.readOnly !== 'undefined' ? this.bodyParams.readOnly : false; - const result = await createPrivateGroupMethod( - this.user, - this.bodyParams.name, - this.bodyParams.members ? this.bodyParams.members : [], - readOnly, - this.bodyParams.customFields, - this.bodyParams.extraData, - this.bodyParams.excludeSelf ?? false, - ); - - const room = await Rooms.findOneById(result.rid, { projection: API.v1.defaultFieldsToExclude }); + try { + const result = await createPrivateGroupMethod( + this.user, + this.bodyParams.name, + this.bodyParams.members ? this.bodyParams.members : [], + readOnly, + this.bodyParams.customFields, + this.bodyParams.extraData, + this.bodyParams.excludeSelf ?? false, + ); + + const room = await Rooms.findOneById(result.rid, { projection: API.v1.defaultFieldsToExclude }); + if (!room) { + throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group'); + } - if (!room) { - throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group'); + return API.v1.success({ + group: await composeRoomWithLastMessage(room, this.userId), + }); + } catch (error: unknown) { + if (isMeteorError(error) && error.reason === 'error-not-allowed') { + return API.v1.unauthorized(); + } } - return API.v1.success({ - group: await composeRoomWithLastMessage(room, this.userId), - }); + return API.v1.internalError(); }, }, ); From 040a0d6151ce10bb254159faec7bad12cf379fa4 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 2 Oct 2023 12:09:03 -0300 Subject: [PATCH 12/14] improve fn getSubscriptionAutotranslateDefaultConfig --- .../functions/addUserToDefaultChannels.ts | 2 +- .../app/lib/server/functions/addUserToRoom.ts | 2 +- .../app/lib/server/functions/createRoom.ts | 2 +- ...tSubscriptionAutotranslateDefaultConfig.ts | 25 ++++++++----------- .../meteor/server/methods/addAllUserToRoom.ts | 2 +- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts index ad632a3b7dfc..835f59419ad5 100644 --- a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts +++ b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts @@ -12,7 +12,7 @@ export const addUserToDefaultChannels = async function (user: IUser, silenced?: }).toArray(); for await (const room of defaultRooms) { if (!(await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { projection: { _id: 1 } }))) { - const autoTranslateConfig = await getSubscriptionAutotranslateDefaultConfig(user); + const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(user); // Add a subscription to this user await Subscriptions.createWithRoomAndUser(room, user, { ts: new Date(), diff --git a/apps/meteor/app/lib/server/functions/addUserToRoom.ts b/apps/meteor/app/lib/server/functions/addUserToRoom.ts index 41000cda2038..4e29576cf3bb 100644 --- a/apps/meteor/app/lib/server/functions/addUserToRoom.ts +++ b/apps/meteor/app/lib/server/functions/addUserToRoom.ts @@ -71,7 +71,7 @@ export const addUserToRoom = async function ( await callbacks.run('beforeJoinRoom', userToBeAdded, room); } - const autoTranslateConfig = await getSubscriptionAutotranslateDefaultConfig(userToBeAdded); + const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(userToBeAdded); await Subscriptions.createWithRoomAndUser(room, userToBeAdded as IUser, { ts: now, diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index e18fc6ff11a5..db807c6cc797 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -80,7 +80,7 @@ async function createUsersSubscriptions({ extra.roles = ['owner']; } - const autoTranslateConfig = await getSubscriptionAutotranslateDefaultConfig(member); + const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(member); subs.push({ user: member, diff --git a/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts b/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts index 13540246f0e6..9465c9168295 100644 --- a/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts +++ b/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts @@ -1,28 +1,23 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { Settings } from '@rocket.chat/models'; -export const getSubscriptionAutotranslateDefaultConfig = async ( - user: IUser, -): Promise< +import { settings } from '../../app/settings/server'; + +export function getSubscriptionAutotranslateDefaultConfig(user: IUser): | { autoTranslate: boolean; autoTranslateLanguage: string; } - | undefined -> => { - const [autoEnableSetting, languageSetting] = await Promise.all([ - Settings.findOneById('AutoTranslate_AutoEnableOnJoinRoom'), - Settings.findOneById('Language'), - ]); - const { language: userLanguage } = user.settings?.preferences || {}; - - if (!autoEnableSetting?.value) { + | undefined { + if (!settings.get('AutoTranslate_AutoEnableOnJoinRoom')) { return; } - if (!userLanguage || userLanguage === 'default' || languageSetting?.value === userLanguage) { + const languageSetting = settings.get('Language'); + + const { language: userLanguage } = user.settings?.preferences || {}; + if (!userLanguage || userLanguage === 'default' || languageSetting === userLanguage) { return; } return { autoTranslate: true, autoTranslateLanguage: userLanguage }; -}; +} diff --git a/apps/meteor/server/methods/addAllUserToRoom.ts b/apps/meteor/server/methods/addAllUserToRoom.ts index acba1bed406b..11232908b847 100644 --- a/apps/meteor/server/methods/addAllUserToRoom.ts +++ b/apps/meteor/server/methods/addAllUserToRoom.ts @@ -56,7 +56,7 @@ Meteor.methods({ continue; } await callbacks.run('beforeJoinRoom', user, room); - const autoTranslateConfig = await getSubscriptionAutotranslateDefaultConfig(user); + const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(user); await Subscriptions.createWithRoomAndUser(room, user, { ts: now, open: true, From e90b905a489f09e5cf407931caf4a3f20260f340 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 2 Oct 2023 13:55:31 -0300 Subject: [PATCH 13/14] fix members validation --- apps/meteor/app/lib/server/functions/createRoom.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index db807c6cc797..449c051c3b37 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -58,8 +58,6 @@ async function createUsersSubscriptions({ }); for await (const member of membersCursor) { - memberIds.push(member._id); - try { await callbacks.run('federation.beforeAddUserToARoom', { user: member, inviter: owner }, room); await callbacks.run('beforeAddedToRoom', { user: member, inviter: owner }); @@ -67,6 +65,8 @@ async function createUsersSubscriptions({ continue; } + memberIds.push(member._id); + const extra: Partial = options?.subscriptionExtra || {}; extra.open = true; From 08a57e4b3815f224838d299616b13cfa42ca09ea Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 5 Oct 2023 11:01:01 -0300 Subject: [PATCH 14/14] improve typing --- apps/meteor/app/lib/server/functions/createRoom.ts | 2 +- .../app/utils/lib/getDefaultSubscriptionPref.ts | 4 ++-- apps/meteor/lib/callbacks.ts | 3 ++- .../getSubscriptionAutotranslateDefaultConfig.ts | 4 ++-- apps/meteor/server/models/raw/Subscriptions.ts | 13 +++++++++++-- .../model-typings/src/models/ISubscriptionsModel.ts | 4 ++-- packages/model-typings/src/models/IUsersModel.ts | 2 +- 7 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index 449c051c3b37..312451f54845 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -53,7 +53,7 @@ async function createUsersSubscriptions({ const memberIds = []; - const membersCursor = Users.findUsersByUsernames(members, { + const membersCursor = Users.findUsersByUsernames>(members, { projection: { 'username': 1, 'settings.preferences': 1, 'federated': 1, 'roles': 1 }, }); diff --git a/apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts b/apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts index a388548c18a8..adb4c2ab1ae9 100644 --- a/apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts +++ b/apps/meteor/app/utils/lib/getDefaultSubscriptionPref.ts @@ -1,4 +1,4 @@ -import type { ISubscription, IUser } from '@rocket.chat/core-typings'; +import type { AtLeast, ISubscription, IUser } from '@rocket.chat/core-typings'; /** * @type {(userPref: Pick) => { @@ -7,7 +7,7 @@ import type { ISubscription, IUser } from '@rocket.chat/core-typings'; * emailPrefOrigin: 'user'; * }} */ -export const getDefaultSubscriptionPref = (userPref: IUser) => { +export const getDefaultSubscriptionPref = (userPref: AtLeast) => { const subscription: Partial = {}; const { desktopNotifications, pushNotifications, emailNotificationMode, highlights } = userPref.settings?.preferences || {}; diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 9c7333a355b3..2d683bf27e2a 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -17,6 +17,7 @@ import type { InquiryWithAgentInfo, ILivechatTagRecord, TransferData, + AtLeast, } from '@rocket.chat/core-typings'; import type { FilterOperators } from 'mongodb'; @@ -56,7 +57,7 @@ interface EventLikeCallbackSignatures { 'livechat.afterTakeInquiry': (inq: InquiryWithAgentInfo, agent: { agentId: string; username: string }) => void; 'livechat.afterAgentRemoved': (params: { agent: Pick }) => void; 'afterAddedToRoom': (params: { user: IUser; inviter?: IUser }, room: IRoom) => void; - 'beforeAddedToRoom': (params: { user: IUser; inviter: IUser }) => void; + 'beforeAddedToRoom': (params: { user: AtLeast; inviter: IUser }) => void; 'afterCreateDirectRoom': (params: IRoom, second: { members: IUser[]; creatorId: IUser['_id'] }) => void; 'beforeDeleteRoom': (params: IRoom) => void; 'beforeJoinDefaultChannels': (user: IUser) => void; diff --git a/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts b/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts index 9465c9168295..92e76d8c2ec1 100644 --- a/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts +++ b/apps/meteor/server/lib/getSubscriptionAutotranslateDefaultConfig.ts @@ -1,8 +1,8 @@ -import type { IUser } from '@rocket.chat/core-typings'; +import type { AtLeast, IUser } from '@rocket.chat/core-typings'; import { settings } from '../../app/settings/server'; -export function getSubscriptionAutotranslateDefaultConfig(user: IUser): +export function getSubscriptionAutotranslateDefaultConfig(user: AtLeast): | { autoTranslate: boolean; autoTranslateLanguage: string; diff --git a/apps/meteor/server/models/raw/Subscriptions.ts b/apps/meteor/server/models/raw/Subscriptions.ts index 167f982fe67f..c4ba44bdd7f9 100644 --- a/apps/meteor/server/models/raw/Subscriptions.ts +++ b/apps/meteor/server/models/raw/Subscriptions.ts @@ -1,4 +1,13 @@ -import type { IRole, IRoom, ISubscription, IUser, RocketChatRecordDeleted, RoomType, SpotlightUser } from '@rocket.chat/core-typings'; +import type { + AtLeast, + IRole, + IRoom, + ISubscription, + IUser, + RocketChatRecordDeleted, + RoomType, + SpotlightUser, +} from '@rocket.chat/core-typings'; import type { ISubscriptionsModel } from '@rocket.chat/model-typings'; import { Rooms, Users } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; @@ -1608,7 +1617,7 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri async createWithRoomAndManyUsers( room: IRoom, - users: { user: IUser; extraData: Record }[] = [], + users: { user: AtLeast; extraData: Record }[] = [], ): Promise> { const subscriptions = users.map(({ user, extraData }) => ({ open: false, diff --git a/packages/model-typings/src/models/ISubscriptionsModel.ts b/packages/model-typings/src/models/ISubscriptionsModel.ts index 9e9f3d6dbedc..53b0a69ec232 100644 --- a/packages/model-typings/src/models/ISubscriptionsModel.ts +++ b/packages/model-typings/src/models/ISubscriptionsModel.ts @@ -1,4 +1,4 @@ -import type { ISubscription, IRole, IUser, IRoom, RoomType, SpotlightUser } from '@rocket.chat/core-typings'; +import type { ISubscription, IRole, IUser, IRoom, RoomType, SpotlightUser, AtLeast } from '@rocket.chat/core-typings'; import type { FindOptions, FindCursor, @@ -228,7 +228,7 @@ export interface ISubscriptionsModel extends IBaseModel { createWithRoomAndUser(room: IRoom, user: IUser, extraData?: Record): Promise>; createWithRoomAndManyUsers( room: IRoom, - users: { user: IUser; extraData: Record }[], + users: { user: AtLeast; extraData: Record }[], ): Promise>; removeByRoomIdsAndUserId(rids: string[], userId: string): Promise; removeByRoomIdAndUserId(roomId: string, userId: string): Promise; diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 9b7ea84b0e8b..f14f5bc90d0d 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -318,7 +318,7 @@ export interface IUsersModel extends IBaseModel { findByUsernameNameOrEmailAddress(nameOrUsernameOrEmail: string, options?: FindOptions): FindCursor; findCrowdUsers(options?: FindOptions): FindCursor; getLastLogin(options?: FindOptions): Promise; - findUsersByUsernames(usernames: string[], options?: FindOptions): FindCursor; + findUsersByUsernames(usernames: string[], options?: FindOptions): FindCursor; findUsersByIds(userIds: string[], options?: FindOptions): FindCursor; findUsersWithUsernameByIds(userIds: string[], options?: FindOptions): FindCursor; findUsersWithUsernameByIdsNotOffline(userIds: string[], options?: FindOptions): FindCursor;