Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Allow admin to access any groups info and groups members. #26423

Open
wants to merge 39 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
48f8d34
groups api rest typings
albuquerquefabio Jul 29, 2022
414d7ef
Access groups members when you have an admin role
albuquerquefabio Jul 29, 2022
9cfa7f9
Access room and group info with admin role
albuquerquefabio Jul 29, 2022
66a7915
Test api to check groups members and info
albuquerquefabio Jul 29, 2022
01f7381
Fix prop declaration
albuquerquefabio Jul 29, 2022
f975e0e
Remove unnecessary declaration
albuquerquefabio Jul 29, 2022
33e0adf
Update rooms.js
albuquerquefabio Jul 29, 2022
939bb95
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Aug 30, 2022
442eb3d
Fix lint error
albuquerquefabio Aug 31, 2022
00e3945
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Aug 31, 2022
e4fb749
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Dec 1, 2022
31d9a1e
Merge branch 'develop' into chore/groups-check-role
LucianoPierdona Dec 22, 2022
e6d8c87
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Dec 22, 2022
ff8352c
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Dec 23, 2022
e31ad0b
Fix missing fields params
albuquerquefabio Dec 23, 2022
17015be
Fix: room test
albuquerquefabio Dec 23, 2022
dfcfd99
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Dec 26, 2022
d4aebd4
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Dec 27, 2022
4bedd8b
Fix conflict
albuquerquefabio Dec 27, 2022
8f20c8a
Fix room e2e
albuquerquefabio Dec 27, 2022
2113815
Remove not used const
albuquerquefabio Dec 27, 2022
dd1082e
Remove expected encrypt object
albuquerquefabio Dec 27, 2022
22636c2
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Dec 28, 2022
4cc2c23
Remove encrypted property
albuquerquefabio Dec 28, 2022
6c4f413
Merge branch 'develop' into chore/groups-check-role
LucianoPierdona Jan 4, 2023
b5a4031
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Feb 28, 2023
1c1de44
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 14, 2023
d7f36cb
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 24, 2023
898377c
validate room info params
albuquerquefabio Mar 24, 2023
6cf22f3
return partial content on groups.info
albuquerquefabio Mar 24, 2023
ceeeb10
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 24, 2023
5eeae8e
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 24, 2023
ce9a16f
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 24, 2023
2fedc5b
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 25, 2023
ebc2179
the filter (projection) was put back
albuquerquefabio Mar 25, 2023
10a5317
unnecessary await
albuquerquefabio Mar 25, 2023
3be1439
lint issue
albuquerquefabio Mar 25, 2023
f1be707
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 25, 2023
783274f
Merge branch 'develop' into chore/groups-check-role
albuquerquefabio Mar 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 71 additions & 21 deletions apps/meteor/app/api/server/v1/groups.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import type { IIntegration, IUser, RoomType } from '@rocket.chat/core-typings';
import type { IIntegration, IRoom, IUser, RoomType } from '@rocket.chat/core-typings';
import { isGroupsInfoProps } from '@rocket.chat/rest-typings';
import { Subscriptions, Rooms, Messages, Users, Uploads, Integrations } from '@rocket.chat/models';
import { Team } from '@rocket.chat/core-services';
import type { Filter } from 'mongodb';

import { Users as UsersSync, Subscriptions as SubscriptionsSync } from '../../../models/server';
import { hasAtLeastOnePermission, canAccessRoomAsync, hasAllPermission, roomAccessAttributes } from '../../../authorization/server';
import {
hasAtLeastOnePermission,
canAccessRoomAsync,
hasAllPermission,
roomAccessAttributes,
hasRole,
} from '../../../authorization/server';
import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { API } from '../api';
import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage';
Expand All @@ -17,11 +25,21 @@ import { findUsersOfRoom } from '../../../../server/lib/findUsersOfRoom';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { getLoggedInUser } from '../helpers/getLoggedInUser';
import { getPaginationItems } from '../helpers/getPaginationItems';
// Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property

/**
* Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property
* @param {object} params - The params of the request
* @param {string} params.roomId - The room id of the private group
* @param {string} params.roomName - The room name of the private group
* @param {string} userId - The user id of the user to check the private group subscription
* @param {boolean} [checkedArchived] - If the private group is archived
* @param {boolean} [minimalCheck] - Minimal check is used when we only need to know more about the room, but not the user's subscription
*/
async function findPrivateGroupByIdOrName({
params,
checkedArchived = true,
userId,
minimalCheck = false,
}: {
params:
| {
Expand All @@ -32,6 +50,7 @@ async function findPrivateGroupByIdOrName({
};
userId: string;
checkedArchived?: boolean;
minimalCheck?: boolean;
}): Promise<{
rid: string;
open: boolean;
Expand Down Expand Up @@ -59,7 +78,7 @@ async function findPrivateGroupByIdOrName({
broadcast: 1,
},
};
let room;
let room: IRoom | null = null;
if ('roomId' in params) {
room = await Rooms.findOneById(params.roomId || '', roomOptions);
} else if ('roomName' in params) {
Expand All @@ -72,26 +91,48 @@ async function findPrivateGroupByIdOrName({

const user = await Users.findOneById(userId, { projections: { username: 1 } });

if (!room || !user || !(await canAccessRoomAsync(room, user))) {
if (!room || !user) {
throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group');
}

const hasAccess = await canAccessRoomIdAsync(room._id, userId);

// discussions have their names saved on `fname` property
const roomName = room.prid ? room.fname : room.name;

if (checkedArchived && room.archived) {
throw new Meteor.Error('error-room-archived', `The private group, ${roomName}, is archived`);
if (!minimalCheck) {
if (!hasAccess) {
throw new Meteor.Error('error-not-allowed', 'You are not allowed to access this group.');
}

if (checkedArchived && room.archived) {
throw new Meteor.Error('error-room-archived', `The private group, ${roomName}, is archived`);
}
const sub = await Subscriptions.findOneByRoomIdAndUserId(room._id, userId, { projection: { open: 1 } });

return {
rid: room._id,
open: Boolean(sub?.open),
ro: Boolean(room.ro),
t: room.t,
name: roomName || '',
broadcast: Boolean(room.broadcast),
};
}

const sub = await Subscriptions.findOneByRoomIdAndUserId(room._id, userId, { projection: { open: 1 } });
const isAdmin = hasRole(userId, 'admin');

if (!isAdmin && !hasAccess) {
throw new Meteor.Error('error-not-allowed', 'You are not allowed to access this group.');
}

return {
rid: room._id,
open: Boolean(sub?.open),
ro: room.ro,
open: false,
ro: Boolean(room.ro),
t: room.t,
name: roomName,
broadcast: room.broadcast,
name: room.name || '',
broadcast: Boolean(room.broadcast),
};
}

Expand Down Expand Up @@ -505,23 +546,31 @@ API.v1.addRoute(

API.v1.addRoute(
'groups.info',
{ authRequired: true },
{ authRequired: true, validateParams: isGroupsInfoProps },
{
async get() {
const findResult = await findPrivateGroupByIdOrName({
params: this.queryParams,
userId: this.userId,
checkedArchived: false,
minimalCheck: true,
});

const room = await Rooms.findOneById(findResult.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');
return API.v1.notFound('The required "roomId" or "roomName" param provided does not match any group');
}

const hasAccess = await canAccessRoomIdAsync(findResult.rid, this.userId);
if (hasAccess) {
return API.v1.success({
group: await composeRoomWithLastMessage(room, this.userId),
});
}
// remove room object lastMessage if user doesn't have access to it
delete room.lastMessage;

return API.v1.success({
group: await composeRoomWithLastMessage(room, this.userId),
group: room,
});
},
},
Expand Down Expand Up @@ -671,12 +720,13 @@ API.v1.addRoute(

API.v1.addRoute(
'groups.members',
{ authRequired: true },
{ authRequired: true, validateParams: isGroupsInfoProps },
{
async get() {
const findResult = await findPrivateGroupByIdOrName({
params: this.queryParams,
userId: this.userId,
minimalCheck: true,
});

if (findResult.broadcast && !(await hasPermissionAsync(this.userId, 'view-broadcast-member-list', findResult.rid))) {
Expand All @@ -694,14 +744,14 @@ API.v1.addRoute(
}),
);

const { status, filter } = this.queryParams;
const { status, filter: projection } = this.queryParams;

const { cursor, totalCount } = await findUsersOfRoom({
const { cursor, totalCount } = findUsersOfRoom({
rid: findResult.rid,
...(status && { status: { $in: status } }),
skip,
limit,
filter,
...(projection && { projection }),
sort: {
_updatedAt: -1,
...(sort?.username && { username: sort.username }),
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/app/api/server/v1/rooms.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
import type { Notifications } from '@rocket.chat/rest-typings';
import { isGETRoomsNameExists } from '@rocket.chat/rest-typings';
import { isGETRoomsNameExists, isRoomsInfoProps } from '@rocket.chat/rest-typings';
import { Messages, Rooms, Users } from '@rocket.chat/models';
import type { IRoom } from '@rocket.chat/core-typings';
import { Media } from '@rocket.chat/core-services';
Expand Down Expand Up @@ -279,7 +279,7 @@ API.v1.addRoute(

API.v1.addRoute(
'rooms.info',
{ authRequired: true },
{ authRequired: true, validateParams: isRoomsInfoProps },
{
async get() {
const room = await findRoomByIdOrName({ params: this.queryParams });
Expand Down