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

refactor: Roles out of DB Watcher #32280

Merged
merged 9 commits into from
May 8, 2024
3 changes: 3 additions & 0 deletions apps/meteor/app/api/server/v1/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getUsersInRolePaginated } from '../../../authorization/server/functions
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole';
import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { notifyListenerOnRoleChanges } from '../../../lib/server/lib/notifyListenerOnRoleChanges';
import { settings } from '../../../settings/server/index';
import { API } from '../api';
import { getPaginationItems } from '../helpers/getPaginationItems';
Expand Down Expand Up @@ -179,6 +180,8 @@ API.v1.addRoute(

await Roles.removeById(role._id);

void notifyListenerOnRoleChanges(role._id, 'removed', role);

return API.v1.success();
},
},
Expand Down
24 changes: 24 additions & 0 deletions apps/meteor/app/lib/server/lib/notifyListenerOnRoleChanges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { api, dbWatchersDisabled } from '@rocket.chat/core-services';
import type { IRole } from '@rocket.chat/core-typings';
import { Roles } from '@rocket.chat/models';

type ClientAction = 'inserted' | 'updated' | 'removed';

export async function notifyListenerOnRoleChanges(
rid: IRole['_id'],
clientAction: ClientAction = 'updated',
existingRoleData?: IRole,
): Promise<void> {
if (!dbWatchersDisabled) {
return;
}

const role = existingRoleData || (await Roles.findOneById(rid));

if (role) {
void api.broadcast('watch.roles', {
clientAction,
role,
});
}
}
8 changes: 2 additions & 6 deletions apps/meteor/ee/server/api/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,7 @@ API.v1.addRoute(

const role = await insertRoleAsync(roleData, options);

return API.v1.success({
role,
});
return API.v1.success({ role });
},
},
);
Expand Down Expand Up @@ -172,9 +170,7 @@ API.v1.addRoute(

const updatedRole = await updateRole(roleId, roleData, options);

return API.v1.success({
role: updatedRole,
});
return API.v1.success({ role: updatedRole });
},
},
);
14 changes: 5 additions & 9 deletions apps/meteor/ee/server/lib/roles/insertRole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { api, MeteorError } from '@rocket.chat/core-services';
import type { IRole } from '@rocket.chat/core-typings';
import { Roles } from '@rocket.chat/models';

import { notifyListenerOnRoleChanges } from '../../../../app/lib/server/lib/notifyListenerOnRoleChanges';
import { isValidRoleScope } from '../../../../lib/roles/isValidRoleScope';

type InsertRoleOptions = {
Expand All @@ -19,21 +20,16 @@ export const insertRoleAsync = async (roleData: Omit<IRole, '_id'>, options: Ins
throw new MeteorError('error-invalid-scope', 'Invalid scope');
}

const result = await Roles.createWithRandomId(name, scope, description, false, mandatory2fa);
const role = await Roles.createWithRandomId(name, scope, description, false, mandatory2fa);

const roleId = result.insertedId;
void notifyListenerOnRoleChanges(role._id, 'inserted', role);

if (options.broadcastUpdate) {
void api.broadcast('user.roleUpdate', {
type: 'changed',
_id: roleId,
_id: role._id,
});
}

const newRole = await Roles.findOneById(roleId);
if (!newRole) {
throw new MeteorError('error-role-not-found', 'Role not found');
}

return newRole;
return role;
};
3 changes: 3 additions & 0 deletions apps/meteor/ee/server/lib/roles/updateRole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { api, MeteorError } from '@rocket.chat/core-services';
import type { IRole } from '@rocket.chat/core-typings';
import { Roles } from '@rocket.chat/models';

import { notifyListenerOnRoleChanges } from '../../../../app/lib/server/lib/notifyListenerOnRoleChanges';
import { isValidRoleScope } from '../../../../lib/roles/isValidRoleScope';

type UpdateRoleOptions = {
Expand Down Expand Up @@ -38,6 +39,8 @@ export const updateRole = async (roleId: IRole['_id'], roleData: Omit<IRole, '_i

await Roles.updateById(roleId, roleData.name, roleData.scope, roleData.description, roleData.mandatory2fa);

void notifyListenerOnRoleChanges(roleId);

if (options.broadcastUpdate) {
void api.broadcast('user.roleUpdate', {
type: 'changed',
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/server/database/watchCollections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@ const onlyCollections = DBWATCHER_ONLY_COLLECTIONS.split(',')
export function getWatchCollections(): string[] {
const collections = [
Users.getCollectionName(),
Subscriptions.getCollectionName(),
LivechatInquiry.getCollectionName(),
LivechatDepartmentAgents.getCollectionName(),
Permissions.getCollectionName(),
Roles.getCollectionName(),
Rooms.getCollectionName(),
LoginServiceConfiguration.getCollectionName(),
InstanceStatus.getCollectionName(),
Expand All @@ -45,11 +43,13 @@ export function getWatchCollections(): string[] {
PbxEvents.getCollectionName(),
Settings.getCollectionName(),
LivechatPriority.getCollectionName(),
Subscriptions.getCollectionName(),
];

// add back to the list of collections in case db watchers are enabled
if (!dbWatchersDisabled) {
collections.push(Messages.getCollectionName());
collections.push(Roles.getCollectionName());
}

if (onlyCollections.length > 0) {
Expand Down
11 changes: 9 additions & 2 deletions apps/meteor/server/lib/roles/createOrUpdateProtectedRole.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
import type { IRole, AtLeast } from '@rocket.chat/core-typings';
import { Roles } from '@rocket.chat/models';

import { notifyListenerOnRoleChanges } from '../../../app/lib/server/lib/notifyListenerOnRoleChanges';

export const createOrUpdateProtectedRoleAsync = async (
roleId: string,
roleData: AtLeast<Omit<IRole, '_id' | 'protected'>, 'name'>,
): Promise<void> => {
const role = await Roles.findOneById<Pick<IRole, '_id' | 'name' | 'scope' | 'description' | 'mandatory2fa'>>(roleId, {
projection: { name: 1, scope: 1, description: 1, mandatory2fa: 1 },
});

if (role) {
await Roles.updateById(
const updatedRole = await Roles.updateById(
roleId,
roleData.name || role.name,
roleData.scope || role.scope,
roleData.description || role.description,
roleData.mandatory2fa || role.mandatory2fa,
);

void notifyListenerOnRoleChanges(roleId, 'updated', updatedRole);

return;
}

await Roles.insertOne({
const insertedRole = await Roles.insertOne({
_id: roleId,
scope: 'Users',
description: '',
mandatory2fa: false,
...roleData,
protected: true,
});

void notifyListenerOnRoleChanges(insertedRole.insertedId, 'inserted');
sampaiodiego marked this conversation as resolved.
Show resolved Hide resolved
};
41 changes: 28 additions & 13 deletions apps/meteor/server/models/raw/Roles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { IRole, IRoom, IUser, RocketChatRecordDeleted } from '@rocket.chat/core-typings';
import type { IRolesModel } from '@rocket.chat/model-typings';
import { Subscriptions, Users } from '@rocket.chat/models';
import type { Collection, FindCursor, Db, Filter, FindOptions, InsertOneResult, UpdateResult, WithId, Document } from 'mongodb';
import type { Collection, FindCursor, Db, Filter, FindOptions, Document } from 'mongodb';

import { BaseRaw } from './BaseRaw';

Expand Down Expand Up @@ -190,21 +190,31 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
return this.find(query, options || {});
}

updateById(
async updateById(
_id: IRole['_id'],
name: IRole['name'],
scope: IRole['scope'],
description: IRole['description'] = '',
mandatory2fa: IRole['mandatory2fa'] = false,
): Promise<UpdateResult> {
const queryData = {
name,
scope,
description,
mandatory2fa,
};
): Promise<IRole> {
const response = await this.findOneAndUpdate(
{ _id },
{
$set: {
name,
scope,
description,
mandatory2fa,
},
},
{ upsert: true, returnDocument: 'after' },
);

if (!response.value) {
throw new Error('Role not found');
}

return this.updateOne({ _id }, { $set: queryData }, { upsert: true });
return response.value;
}

findUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise<FindCursor<IUser>>;
Expand Down Expand Up @@ -242,13 +252,13 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
}
}

createWithRandomId(
async createWithRandomId(
name: IRole['name'],
scope: IRole['scope'] = 'Users',
description = '',
protectedRole = true,
mandatory2fa = false,
): Promise<InsertOneResult<WithId<IRole>>> {
): Promise<IRole> {
const role = {
name,
scope,
Expand All @@ -257,7 +267,12 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
mandatory2fa,
};

return this.insertOne(role);
const res = await this.insertOne(role);

return {
ricardogarim marked this conversation as resolved.
Show resolved Hide resolved
_id: res.insertedId,
...role,
};
}

async canAddUserToRole(uid: IUser['_id'], roleId: IRole['_id'], scope?: IRoom['_id']): Promise<boolean> {
Expand Down
9 changes: 4 additions & 5 deletions apps/meteor/server/modules/listeners/listeners.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { ISetting as AppsSetting } from '@rocket.chat/apps-engine/definitio
import type { IServiceClass } from '@rocket.chat/core-services';
import { EnterpriseSettings } from '@rocket.chat/core-services';
import { isSettingColor, isSettingEnterprise, UserStatus } from '@rocket.chat/core-typings';
import type { IUser, IRoom, VideoConference, ISetting, IOmnichannelRoom, IMessage, IOTRMessage } from '@rocket.chat/core-typings';
import type { IUser, IRoom, IRole, VideoConference, ISetting, IOmnichannelRoom, IMessage, IOTRMessage } from '@rocket.chat/core-typings';
import { Logger } from '@rocket.chat/logger';
import { parse } from '@rocket.chat/message-parser';

Expand Down Expand Up @@ -205,11 +205,10 @@ export class ListenersModule {
});

service.onEvent('watch.roles', ({ clientAction, role }): void => {
const payload = {
notifications.streamRoles.emitWithoutBroadcast('roles', {
type: clientAction,
...role,
};
notifications.streamRoles.emitWithoutBroadcast('roles', payload as any);
...(role as IRole),
ricardogarim marked this conversation as resolved.
Show resolved Hide resolved
});
});

service.onEvent('watch.inquiries', async ({ clientAction, inquiry, diff }): Promise<void> => {
Expand Down
6 changes: 3 additions & 3 deletions packages/model-typings/src/models/IRolesModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings';
import type { FindCursor, FindOptions, InsertOneResult, UpdateResult, WithId } from 'mongodb';
import type { FindCursor, FindOptions } from 'mongodb';

import type { IBaseModel } from './IBaseModel';

Expand Down Expand Up @@ -32,7 +32,7 @@ export interface IRolesModel extends IBaseModel<IRole> {
scope: IRole['scope'],
description?: IRole['description'],
mandatory2fa?: IRole['mandatory2fa'],
): Promise<UpdateResult>;
): Promise<IRole>;
findUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise<FindCursor<IUser>>;

findUsersInRole(roleId: IRole['_id'], scope: IRoom['_id'] | undefined, options: FindOptions<IUser>): Promise<FindCursor<IUser>>;
Expand All @@ -58,7 +58,7 @@ export interface IRolesModel extends IBaseModel<IRole> {
description?: string,
protectedRole?: boolean,
mandatory2fa?: boolean,
): Promise<InsertOneResult<WithId<IRole>>>;
): Promise<IRole>;

canAddUserToRole(uid: IUser['_id'], roleId: IRole['_id'], scope?: IRoom['_id']): Promise<boolean>;
}
Loading