From 5ab02b3ef86f2a7af8d507312ad5e2a5c1516505 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Mon, 29 Mar 2021 12:57:58 -0300 Subject: [PATCH 1/7] Add query param to subscriptions in the channels.members endpoint --- app/api/server/v1/channels.js | 4 ++-- app/models/server/models/Subscriptions.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index 0c287890b8f5e..c0d8dd8a417ac 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -572,14 +572,14 @@ API.v1.addRoute('channels.members', { authRequired: true }, { } const { offset, count } = this.getPaginationItems(); - const { sort = {} } = this.parseJsonQuery(); + const { sort = {}, query } = this.parseJsonQuery(); const subscriptions = Subscriptions.findByRoomId(findResult._id, { fields: { 'u._id': 1 }, sort: { 'u.username': sort.username != null ? sort.username : 1 }, skip: offset, limit: count, - }); + }, query); const total = subscriptions.count(); diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index b4094d1acc2d0..bee067987c9a5 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -499,9 +499,9 @@ export class Subscriptions extends Base { return this.find(query, options); } - findByRoomId(roomId, options) { - const query = { rid: roomId }; - return this.find(query, options); + findByRoomId(roomId, options, query = {}) { + const myQuery = { rid: roomId, ...query }; + return this.find(myQuery, options); } findByRoomIdAndNotUserId(roomId, userId, options = {}) { From 891b356b553a7eb6e4b7cb44c19b0c28f8c59cc0 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 30 Mar 2021 15:26:07 -0300 Subject: [PATCH 2/7] Add scope to permissions' verifications --- app/api/server/v1/teams.ts | 130 +++++++++++++++++++++---------- server/sdk/types/ITeamService.ts | 3 +- server/services/team/service.ts | 4 +- 3 files changed, 95 insertions(+), 42 deletions(-) diff --git a/app/api/server/v1/teams.ts b/app/api/server/v1/teams.ts index 9932e6be998e3..50d730a6e2ee9 100644 --- a/app/api/server/v1/teams.ts +++ b/app/api/server/v1/teams.ts @@ -67,13 +67,18 @@ API.v1.addRoute('teams.create', { authRequired: true }, { API.v1.addRoute('teams.addRoom', { authRequired: true }, { post() { - const { roomId, teamId, isDefault } = this.bodyParams; + const { roomId, teamId, teamName, isDefault } = this.bodyParams; - if (!hasPermission(this.userId, 'add-team-channel')) { + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); + } + + if (!hasPermission(this.userId, 'add-team-channel', team.roomId)) { return API.v1.unauthorized(); } - const room = Promise.await(Team.addRoom(this.userId, roomId, teamId, isDefault)); + const room = Promise.await(Team.addRoom(this.userId, roomId, team._id, isDefault)); return API.v1.success({ room }); }, @@ -81,13 +86,18 @@ API.v1.addRoute('teams.addRoom', { authRequired: true }, { API.v1.addRoute('teams.addRooms', { authRequired: true }, { post() { - const { rooms, teamId } = this.bodyParams; + const { rooms, teamId, teamName } = this.bodyParams; + + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); + } - if (!hasPermission(this.userId, 'add-team-channel')) { + if (!hasPermission(this.userId, 'add-team-channel', team.roomId)) { return API.v1.unauthorized(); } - const validRooms = Promise.await(Team.addRooms(this.userId, rooms, teamId)); + const validRooms = Promise.await(Team.addRooms(this.userId, rooms, team._id)); return API.v1.success({ rooms: validRooms }); }, @@ -95,15 +105,20 @@ API.v1.addRoute('teams.addRooms', { authRequired: true }, { API.v1.addRoute('teams.removeRoom', { authRequired: true }, { post() { - const { roomId, teamId } = this.bodyParams; + const { roomId, teamId, teamName } = this.bodyParams; - if (!hasPermission(this.userId, 'remove-team-channel')) { + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); + } + + if (!hasPermission(this.userId, 'remove-team-channel', team.roomId)) { return API.v1.unauthorized(); } - const canRemoveAny = !!hasPermission(this.userId, 'view-all-team-channels'); + const canRemoveAny = !!hasPermission(this.userId, 'view-all-team-channels', team.roomId); - const room = Promise.await(Team.removeRoom(this.userId, roomId, teamId, canRemoveAny)); + const room = Promise.await(Team.removeRoom(this.userId, roomId, team._id, canRemoveAny)); return API.v1.success({ room }); }, @@ -113,10 +128,15 @@ API.v1.addRoute('teams.updateRoom', { authRequired: true }, { post() { const { roomId, isDefault } = this.bodyParams; - if (!hasPermission(this.userId, 'edit-team-channel')) { + const team = Promise.await(Team.getOneByRoomId(roomId)); + if (!team) { + return API.v1.failure('room-not-on-team'); + } + + if (!hasPermission(this.userId, 'edit-team-channel', team.roomId)) { return API.v1.unauthorized(); } - const canUpdateAny = !!hasPermission(this.userId, 'view-all-team-channels'); + const canUpdateAny = !!hasPermission(this.userId, 'view-all-team-channels', team.roomId); const room = Promise.await(Team.updateRoom(this.userId, roomId, isDefault, canUpdateAny)); @@ -126,17 +146,22 @@ API.v1.addRoute('teams.updateRoom', { authRequired: true }, { API.v1.addRoute('teams.listRooms', { authRequired: true }, { get() { - const { teamId } = this.queryParams; + const { teamId, teamName } = this.queryParams; const { offset, count } = this.getPaginationItems(); - const allowPrivateTeam = hasPermission(this.userId, 'view-all-teams'); + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); + } + + const allowPrivateTeam = hasPermission(this.userId, 'view-all-teams', team.roomId); let getAllRooms = false; - if (hasPermission(this.userId, 'view-all-team-channels')) { + if (hasPermission(this.userId, 'view-all-team-channels', team.roomId)) { getAllRooms = true; } - const { records, total } = Promise.await(Team.listRooms(this.userId, teamId, getAllRooms, allowPrivateTeam, { offset, count })); + const { records, total } = Promise.await(Team.listRooms(this.userId, team._id, getAllRooms, allowPrivateTeam, { offset, count })); return API.v1.success({ rooms: records, @@ -150,15 +175,20 @@ API.v1.addRoute('teams.listRooms', { authRequired: true }, { API.v1.addRoute('teams.listRoomsOfUser', { authRequired: true }, { get() { const { offset, count } = this.getPaginationItems(); - const { teamId, userId } = this.queryParams; + const { teamId, teamName, userId } = this.queryParams; - const allowPrivateTeam = hasPermission(this.userId, 'view-all-teams'); + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); + } - if (!hasPermission(this.userId, 'view-all-team-channels')) { + const allowPrivateTeam = hasPermission(this.userId, 'view-all-teams', team.roomId); + + if (!hasPermission(this.userId, 'view-all-team-channels', team.roomId)) { return API.v1.unauthorized(); } - const { records, total } = Promise.await(Team.listRoomsOfUser(this.userId, teamId, userId, allowPrivateTeam, { offset, count })); + const { records, total } = Promise.await(Team.listRoomsOfUser(this.userId, team._id, userId, allowPrivateTeam, { offset, count })); return API.v1.success({ rooms: records, @@ -174,9 +204,14 @@ API.v1.addRoute('teams.members', { authRequired: true }, { const { offset, count } = this.getPaginationItems(); const { teamId, teamName } = this.queryParams; const { query } = this.parseJsonQuery(); - const canSeeAllMembers = hasPermission(this.userId, 'view-all-teams'); - const { records, total } = Promise.await(Team.members(this.userId, teamId, teamName, canSeeAllMembers, { offset, count }, { query })); + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); + } + const canSeeAllMembers = hasPermission(this.userId, 'view-all-teams', team.roomId); + + const { records, total } = Promise.await(Team.members(this.userId, team._id, teamName, canSeeAllMembers, { offset, count }, { query })); return API.v1.success({ members: records, @@ -189,13 +224,18 @@ API.v1.addRoute('teams.members', { authRequired: true }, { API.v1.addRoute('teams.addMembers', { authRequired: true }, { post() { - if (!hasAtLeastOnePermission(this.userId, ['add-team-member', 'edit-team-member'])) { - return API.v1.unauthorized(); + const { teamId, teamName, members } = this.bodyParams; + + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); } - const { teamId, teamName, members } = this.bodyParams; + if (!hasAtLeastOnePermission(this.userId, ['add-team-member', 'edit-team-member'], team.roomId)) { + return API.v1.unauthorized(); + } - Promise.await(Team.addMembers(this.userId, teamId, teamName, members)); + Promise.await(Team.addMembers(this.userId, team._id, teamName, members)); return API.v1.success(); }, @@ -203,13 +243,18 @@ API.v1.addRoute('teams.addMembers', { authRequired: true }, { API.v1.addRoute('teams.updateMember', { authRequired: true }, { post() { - if (!hasAtLeastOnePermission(this.userId, ['edit-team-member'])) { - return API.v1.unauthorized(); + const { teamId, teamName, member } = this.bodyParams; + + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); } - const { teamId, teamName, member } = this.bodyParams; + if (!hasAtLeastOnePermission(this.userId, ['edit-team-member'], team.roomId)) { + return API.v1.unauthorized(); + } - Promise.await(Team.updateMember(teamId, teamName, member)); + Promise.await(Team.updateMember(team._id, teamName, member)); return API.v1.success(); }, @@ -217,13 +262,18 @@ API.v1.addRoute('teams.updateMember', { authRequired: true }, { API.v1.addRoute('teams.removeMembers', { authRequired: true }, { post() { - if (!hasAtLeastOnePermission(this.userId, ['edit-team-member'])) { - return API.v1.unauthorized(); + const { teamId, teamName, members, rooms } = this.bodyParams; + + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + if (!team) { + return API.v1.failure('team-does-not-exist'); } - const { teamId, teamName, members, rooms } = this.bodyParams; + if (!hasAtLeastOnePermission(this.userId, ['edit-team-member'], team.roomId)) { + return API.v1.unauthorized(); + } - Promise.await(Team.removeMembers(teamId, teamName, members)); + Promise.await(Team.removeMembers(team._id, teamName, members)); if (rooms?.length) { Subscriptions.removeByRoomIdsAndUserId(rooms, this.userId); @@ -237,7 +287,9 @@ API.v1.addRoute('teams.leave', { authRequired: true }, { post() { const { teamId, teamName, rooms } = this.bodyParams; - Promise.await(Team.removeMembers(teamId, teamName, [{ + const team = teamId ? Promise.await(Team.getOneById(teamId)) : Promise.await(Team.getOneByName(teamName)); + + Promise.await(Team.removeMembers(team._id, teamName, [{ userId: this.userId, }])); @@ -271,10 +323,6 @@ API.v1.addRoute('teams.info', { authRequired: true }, { API.v1.addRoute('teams.delete', { authRequired: true }, { post() { - if (!hasPermission(this.userId, 'delete-team')) { - return API.v1.unauthorized(); - } - const { teamId, teamName, roomsToRemove } = this.bodyParams; if (!teamId && !teamName) { @@ -290,6 +338,10 @@ API.v1.addRoute('teams.delete', { authRequired: true }, { return API.v1.failure('Team not found.'); } + if (!hasPermission(this.userId, 'delete-team', team.roomId)) { + return API.v1.unauthorized(); + } + const rooms = Promise.await(Team.getMatchingTeamRooms(team._id, roomsToRemove)); // Remove the team's main room diff --git a/server/sdk/types/ITeamService.ts b/server/sdk/types/ITeamService.ts index 1f4c94f2d773d..20af5f1d7321b 100644 --- a/server/sdk/types/ITeamService.ts +++ b/server/sdk/types/ITeamService.ts @@ -61,7 +61,8 @@ export interface ITeamService { deleteByName(teamName: string): Promise; unsetTeamIdOfRooms(teamId: string): void; getOneById(teamId: string, options?: FindOneOptions): Promise; - getOneByName(teamName: string): Promise; + getOneByName(teamName: string, options?: FindOneOptions): Promise; + getOneByRoomId(roomId: string): Promise; getMatchingTeamRooms(teamId: string, rids: Array): Promise>; autocomplete(uid: string, name: string): Promise>; } diff --git a/server/services/team/service.ts b/server/services/team/service.ts index d7c13d77b3c5b..67d5799770c5d 100644 --- a/server/services/team/service.ts +++ b/server/services/team/service.ts @@ -613,8 +613,8 @@ export class TeamService extends ServiceClass implements ITeamService { return this.TeamModel.findOneById(teamId, options); } - async getOneByName(teamName: string): Promise { - return this.TeamModel.findOneByName(teamName); + async getOneByName(teamName: string, options?: FindOneOptions): Promise { + return this.TeamModel.findOneByName(teamName, options); } async getOneByRoomId(roomId: string): Promise { From a96eb3f640a0af91fbc76e4bb935f3908fd6e13c Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Thu, 1 Apr 2021 12:13:21 -0300 Subject: [PATCH 3/7] Add support to queries in channels.members and groups.members endpoints --- app/api/server/v1/channels.js | 4 ++-- app/api/server/v1/groups.js | 4 ++-- app/models/server/models/Subscriptions.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index c0d8dd8a417ac..c71e81151f4e8 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -579,13 +579,13 @@ API.v1.addRoute('channels.members', { authRequired: true }, { sort: { 'u.username': sort.username != null ? sort.username : 1 }, skip: offset, limit: count, - }, query); + }); const total = subscriptions.count(); const members = subscriptions.fetch().map((s) => s.u && s.u._id); - const users = Users.find({ _id: { $in: members } }, { + const users = Users.find({ ...query, _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, sort: { username: sort.username != null ? sort.username : 1 }, }).fetch(); diff --git a/app/api/server/v1/groups.js b/app/api/server/v1/groups.js index ec62530be526f..f16458c7eabf7 100644 --- a/app/api/server/v1/groups.js +++ b/app/api/server/v1/groups.js @@ -495,7 +495,7 @@ API.v1.addRoute('groups.members', { authRequired: true }, { } const { offset, count } = this.getPaginationItems(); - const { sort = {} } = this.parseJsonQuery(); + const { sort = {}, query } = this.parseJsonQuery(); const subscriptions = Subscriptions.findByRoomId(findResult.rid, { fields: { 'u._id': 1 }, @@ -508,7 +508,7 @@ API.v1.addRoute('groups.members', { authRequired: true }, { const members = subscriptions.fetch().map((s) => s.u && s.u._id); - const users = Users.find({ _id: { $in: members } }, { + const users = Users.find({ ...query, _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, sort: { username: sort.username != null ? sort.username : 1 }, }).fetch(); diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index bee067987c9a5..aedaca05c9da1 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -499,8 +499,8 @@ export class Subscriptions extends Base { return this.find(query, options); } - findByRoomId(roomId, options, query = {}) { - const myQuery = { rid: roomId, ...query }; + findByRoomId(roomId, options) { + const myQuery = { rid: roomId }; return this.find(myQuery, options); } From 02e220b790bb16135c36de3394e0fcd4c8016746 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Thu, 1 Apr 2021 12:39:47 -0300 Subject: [PATCH 4/7] Fix variable name --- app/models/server/models/Subscriptions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/server/models/Subscriptions.js b/app/models/server/models/Subscriptions.js index e577fcbec17ac..c6e29179c86d5 100644 --- a/app/models/server/models/Subscriptions.js +++ b/app/models/server/models/Subscriptions.js @@ -509,8 +509,8 @@ export class Subscriptions extends Base { } findByRoomId(roomId, options) { - const myQuery = { rid: roomId }; - return this.find(myQuery, options); + const query = { rid: roomId }; + return this.find(query, options); } findByRoomIdAndNotUserId(roomId, userId, options = {}) { From 7438fe9a3acbbc92d742b805932016946f156c31 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 9 Apr 2021 21:18:32 -0300 Subject: [PATCH 5/7] Replace query param by name, username and status params --- app/api/server/v1/channels.js | 12 +++++++++++- app/api/server/v1/groups.js | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index 16dca5cb4ea1e..749b04f5c6c57 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -576,7 +576,12 @@ API.v1.addRoute('channels.members', { authRequired: true }, { } const { offset, count } = this.getPaginationItems(); - const { sort = {}, query } = this.parseJsonQuery(); + const { sort = {} } = this.parseJsonQuery(); + const { status, username, name } = this.queryParams; + + if (status && !Array.isArray(status)) { + throw new Meteor.Error('error-status-param-not-an-array', 'The parameter "status" should be an array of strings'); + } const subscriptions = Subscriptions.findByRoomId(findResult._id, { fields: { 'u._id': 1 }, @@ -588,6 +593,11 @@ API.v1.addRoute('channels.members', { authRequired: true }, { const total = subscriptions.count(); const members = subscriptions.fetch().map((s) => s.u && s.u._id); + const query = { + name, + username, + status: status ? { $in: status } : undefined, + }; const users = Users.find({ ...query, _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, diff --git a/app/api/server/v1/groups.js b/app/api/server/v1/groups.js index 19638a4c0e5a6..0525a99722a12 100644 --- a/app/api/server/v1/groups.js +++ b/app/api/server/v1/groups.js @@ -499,7 +499,12 @@ API.v1.addRoute('groups.members', { authRequired: true }, { } const { offset, count } = this.getPaginationItems(); - const { sort = {}, query } = this.parseJsonQuery(); + const { sort = {} } = this.parseJsonQuery(); + const { status, username, name } = this.queryParams; + + if (status && !Array.isArray(status)) { + throw new Meteor.Error('error-status-param-not-an-array', 'The parameter "status" should be an array of strings'); + } const subscriptions = Subscriptions.findByRoomId(findResult.rid, { fields: { 'u._id': 1 }, @@ -511,6 +516,11 @@ API.v1.addRoute('groups.members', { authRequired: true }, { const total = subscriptions.count(); const members = subscriptions.fetch().map((s) => s.u && s.u._id); + const query = { + name, + username, + status: status ? { $in: status } : undefined, + }; const users = Users.find({ ...query, _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, From 2f8b23553b639a55993b94b760f4a93bd15a7a5d Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Thu, 22 Apr 2021 12:50:11 -0300 Subject: [PATCH 6/7] Add type check to the endpoint params --- app/api/server/v1/channels.js | 7 ++++--- app/api/server/v1/groups.js | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index 749b04f5c6c57..01e26ea289e93 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -1,4 +1,5 @@ import { Meteor } from 'meteor/meteor'; +import { Match, check } from 'meteor/check'; import _ from 'underscore'; import { Rooms, Subscriptions, Messages, Uploads, Integrations, Users } from '../../../models'; @@ -579,9 +580,9 @@ API.v1.addRoute('channels.members', { authRequired: true }, { const { sort = {} } = this.parseJsonQuery(); const { status, username, name } = this.queryParams; - if (status && !Array.isArray(status)) { - throw new Meteor.Error('error-status-param-not-an-array', 'The parameter "status" should be an array of strings'); - } + check(status, Match.Maybe([String])); + check(username, Match.Maybe(String)); + check(name, Match.Maybe(String)); const subscriptions = Subscriptions.findByRoomId(findResult._id, { fields: { 'u._id': 1 }, diff --git a/app/api/server/v1/groups.js b/app/api/server/v1/groups.js index 0525a99722a12..64845a1e989e1 100644 --- a/app/api/server/v1/groups.js +++ b/app/api/server/v1/groups.js @@ -1,6 +1,6 @@ import _ from 'underscore'; import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; +import { Match, check } from 'meteor/check'; import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; import { Subscriptions, Rooms, Messages, Uploads, Integrations, Users } from '../../../models/server'; @@ -502,9 +502,9 @@ API.v1.addRoute('groups.members', { authRequired: true }, { const { sort = {} } = this.parseJsonQuery(); const { status, username, name } = this.queryParams; - if (status && !Array.isArray(status)) { - throw new Meteor.Error('error-status-param-not-an-array', 'The parameter "status" should be an array of strings'); - } + check(status, Match.Maybe([String])); + check(username, Match.Maybe(String)); + check(name, Match.Maybe(String)); const subscriptions = Subscriptions.findByRoomId(findResult.rid, { fields: { 'u._id': 1 }, From 68e7f7fab35bb08b4297d27e1158d46d004b4f14 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 13 May 2021 14:40:42 -0300 Subject: [PATCH 7/7] Use findUserOfRoom --- app/api/server/v1/channels.js | 49 ++++++++++++--------------- app/api/server/v1/groups.js | 55 +++++++++++++++---------------- app/models/server/models/Users.js | 2 +- server/lib/findUsersOfRoom.ts | 38 +++++++++++++++++++++ server/methods/getUsersOfRoom.js | 34 +++---------------- 5 files changed, 91 insertions(+), 87 deletions(-) create mode 100644 server/lib/findUsersOfRoom.ts diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index 01e26ea289e93..58a75e2667dfe 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -2,13 +2,14 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import _ from 'underscore'; -import { Rooms, Subscriptions, Messages, Uploads, Integrations, Users } from '../../../models'; +import { Rooms, Subscriptions, Messages, Uploads, Integrations, Users } from '../../../models/server'; import { hasPermission, hasAtLeastOnePermission, hasAllPermission } from '../../../authorization/server'; import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission'; import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; -import { settings } from '../../../settings'; +import { settings } from '../../../settings/server'; import { Team } from '../../../../server/sdk'; +import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; // Returns the channel IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property @@ -576,39 +577,31 @@ API.v1.addRoute('channels.members', { authRequired: true }, { return API.v1.unauthorized(); } - const { offset, count } = this.getPaginationItems(); + const { offset: skip, count: limit } = this.getPaginationItems(); const { sort = {} } = this.parseJsonQuery(); - const { status, username, name } = this.queryParams; - check(status, Match.Maybe([String])); - check(username, Match.Maybe(String)); - check(name, Match.Maybe(String)); + check(this.queryParams, Match.ObjectIncluding({ + status: Match.Maybe([String]), + filter: Match.Maybe(String), + })); + const { status, filter } = this.queryParams; - const subscriptions = Subscriptions.findByRoomId(findResult._id, { - fields: { 'u._id': 1 }, - sort: { 'u.username': sort.username != null ? sort.username : 1 }, - skip: offset, - limit: count, + const cursor = findUsersOfRoom({ + rid: findResult._id, + ...status && { status: { $in: status } }, + skip, + limit, + filter, + ...sort?.username && { sort: { username: sort.username } }, }); - const total = subscriptions.count(); - - const members = subscriptions.fetch().map((s) => s.u && s.u._id); - const query = { - name, - username, - status: status ? { $in: status } : undefined, - }; - - const users = Users.find({ ...query, _id: { $in: members } }, { - fields: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, - sort: { username: sort.username != null ? sort.username : 1 }, - }).fetch(); + const total = cursor.count(); + const members = cursor.fetch(); return API.v1.success({ - members: users, - count: users.length, - offset, + members, + count: members.length, + offset: skip, total, }); }, diff --git a/app/api/server/v1/groups.js b/app/api/server/v1/groups.js index 64845a1e989e1..ce0f06fc1813f 100644 --- a/app/api/server/v1/groups.js +++ b/app/api/server/v1/groups.js @@ -8,6 +8,7 @@ import { hasPermission, hasAtLeastOnePermission, canAccessRoom, hasAllPermission import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser'; import { API } from '../api'; import { Team } from '../../../../server/sdk'; +import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom'; // Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property export function findPrivateGroupByIdOrName({ params, userId, checkedArchived = true }) { @@ -23,6 +24,7 @@ export function findPrivateGroupByIdOrName({ params, userId, checkedArchived = t fname: 1, prid: 1, archived: 1, + broadcast: 1, }, }; const room = params.roomId @@ -54,6 +56,7 @@ export function findPrivateGroupByIdOrName({ params, userId, checkedArchived = t ro: room.ro, t: room.t, name: roomName, + broadcast: room.broadcast, }; } @@ -491,46 +494,40 @@ API.v1.addRoute('groups.listAll', { authRequired: true }, { API.v1.addRoute('groups.members', { authRequired: true }, { get() { - const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId }); - const room = Rooms.findOneById(findResult.rid, { fields: { broadcast: 1 } }); + const findResult = findPrivateGroupByIdOrName({ + params: this.requestParams(), + userId: this.userId, + }); - if (room.broadcast && !hasPermission(this.userId, 'view-broadcast-member-list')) { + if (findResult.broadcast && !hasPermission(this.userId, 'view-broadcast-member-list')) { return API.v1.unauthorized(); } - const { offset, count } = this.getPaginationItems(); + const { offset: skip, count: limit } = this.getPaginationItems(); const { sort = {} } = this.parseJsonQuery(); - const { status, username, name } = this.queryParams; - check(status, Match.Maybe([String])); - check(username, Match.Maybe(String)); - check(name, Match.Maybe(String)); + check(this.queryParams, Match.ObjectIncluding({ + status: Match.Maybe([String]), + filter: Match.Maybe(String), + })); + const { status, filter } = this.queryParams; - const subscriptions = Subscriptions.findByRoomId(findResult.rid, { - fields: { 'u._id': 1 }, - sort: { 'u.username': sort.username != null ? sort.username : 1 }, - skip: offset, - limit: count, + const cursor = findUsersOfRoom({ + rid: findResult.rid, + ...status && { status: { $in: status } }, + skip, + limit, + filter, + ...sort?.username && { sort: { username: sort.username } }, }); - const total = subscriptions.count(); - - const members = subscriptions.fetch().map((s) => s.u && s.u._id); - const query = { - name, - username, - status: status ? { $in: status } : undefined, - }; - - const users = Users.find({ ...query, _id: { $in: members } }, { - fields: { _id: 1, username: 1, name: 1, status: 1, statusText: 1, utcOffset: 1 }, - sort: { username: sort.username != null ? sort.username : 1 }, - }).fetch(); + const total = cursor.count(); + const members = cursor.fetch(); return API.v1.success({ - members: users, - count: users.length, - offset, + members, + count: members.length, + offset: skip, total, }); }, diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index 6a31157e71cff..575e712546605 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -774,7 +774,7 @@ export class Users extends Base { } // if the search term is empty, don't need to have the $or statement (because it would be an empty regex) - if (searchTerm === '') { + if (!searchTerm) { const query = { $and: [ { diff --git a/server/lib/findUsersOfRoom.ts b/server/lib/findUsersOfRoom.ts new file mode 100644 index 0000000000000..7c6d61433ecab --- /dev/null +++ b/server/lib/findUsersOfRoom.ts @@ -0,0 +1,38 @@ +import type { Mongo } from 'meteor/mongo'; + +import { Users } from '../../app/models/server'; +import { settings } from '../../app/settings/server'; +import { IUser } from '../../definition/IUser'; + +type FindUsersParam = { + rid: string; + status?: string; + skip?: number; + limit?: number; + filter?: string; + sort?: Record; +}; + +export function findUsersOfRoom({ rid, status, skip = 0, limit = 0, filter = '', sort = {} }: FindUsersParam): Mongo.Cursor { + const options = { + fields: { + name: 1, + username: 1, + nickname: 1, + status: 1, + avatarETag: 1, + _updatedAt: 1, + }, + sort: { + statusConnection: -1, + ...sort || { [settings.get('UI_Use_Real_Name') ? 'name' : 'username']: 1 }, + }, + ...skip > 0 && { skip }, + ...limit > 0 && { limit }, + }; + + return Users.findByActiveUsersExcept(filter, undefined, options, undefined, [{ + __rooms: rid, + ...status && { status }, + }]); +} diff --git a/server/methods/getUsersOfRoom.js b/server/methods/getUsersOfRoom.js index efeea2233a45e..d17d91013cb6b 100644 --- a/server/methods/getUsersOfRoom.js +++ b/server/methods/getUsersOfRoom.js @@ -1,35 +1,11 @@ import { Meteor } from 'meteor/meteor'; -import { Subscriptions, Users } from '../../app/models/server'; -import { hasPermission } from '../../app/authorization'; -import { settings } from '../../app/settings'; - -function findUsers({ rid, status, skip, limit, filter = '' }) { - const options = { - fields: { - name: 1, - username: 1, - nickname: 1, - status: 1, - avatarETag: 1, - _updatedAt: 1, - }, - sort: { - statusConnection: -1, - [settings.get('UI_Use_Real_Name') ? 'name' : 'username']: 1, - }, - ...skip > 0 && { skip }, - ...limit > 0 && { limit }, - }; - - return Users.findByActiveUsersExcept(filter, undefined, options, undefined, [{ - __rooms: rid, - ...status && { status }, - }]).fetch(); -} +import { Subscriptions } from '../../app/models/server'; +import { hasPermission } from '../../app/authorization/server'; +import { findUsersOfRoom } from '../lib/findUsersOfRoom'; Meteor.methods({ - async getUsersOfRoom(rid, showAll, { limit, skip } = {}, filter) { + getUsersOfRoom(rid, showAll, { limit, skip } = {}, filter) { const userId = Meteor.userId(); if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getUsersOfRoom' }); @@ -46,7 +22,7 @@ Meteor.methods({ const total = Subscriptions.findByRoomIdWhenUsernameExists(rid).count(); - const users = await findUsers({ rid, status: !showAll ? { $ne: 'offline' } : undefined, limit, skip, filter }); + const users = findUsersOfRoom({ rid, status: !showAll ? { $ne: 'offline' } : undefined, limit, skip, filter }).fetch(); return { total,