Skip to content
Snippets Groups Projects
Unverified Commit d37433b9 authored by Diego Sampaio's avatar Diego Sampaio Committed by GitHub
Browse files

refactor: Move add and remove role code out of Roles model (#34488)

parent b62b0a66
No related branches found
No related tags found
No related merge requests found
Showing
with 78 additions and 80 deletions
...@@ -5,6 +5,7 @@ import { isRoleAddUserToRoleProps, isRoleDeleteProps, isRoleRemoveUserFromRolePr ...@@ -5,6 +5,7 @@ import { isRoleAddUserToRoleProps, isRoleDeleteProps, isRoleRemoveUserFromRolePr
import { check, Match } from 'meteor/check'; import { check, Match } from 'meteor/check';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles';
import { getUsersInRolePaginated } from '../../../authorization/server/functions/getUsersInRole'; import { getUsersInRolePaginated } from '../../../authorization/server/functions/getUsersInRole';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole'; import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole';
...@@ -221,7 +222,7 @@ API.v1.addRoute( ...@@ -221,7 +222,7 @@ API.v1.addRoute(
} }
} }
await Roles.removeUserRoles(user._id, [role._id], scope); await removeUserFromRolesAsync(user._id, [role._id], scope);
if (settings.get('UI_DisplayRoles')) { if (settings.get('UI_DisplayRoles')) {
void api.broadcast('user.roleUpdate', { void api.broadcast('user.roleUpdate', {
......
...@@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ddp-client'; ...@@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Roles, Users } from '@rocket.chat/models'; import { Roles, Users } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { settings } from '../../../settings/server'; import { settings } from '../../../settings/server';
import { hasPermissionAsync } from '../functions/hasPermission'; import { hasPermissionAsync } from '../functions/hasPermission';
...@@ -75,7 +76,7 @@ Meteor.methods<ServerMethods>({ ...@@ -75,7 +76,7 @@ Meteor.methods<ServerMethods>({
}); });
} }
const add = await Roles.addUserRoles(user._id, [role._id], scope); const add = await addUserRolesAsync(user._id, [role._id], scope);
if (settings.get('UI_DisplayRoles')) { if (settings.get('UI_DisplayRoles')) {
void api.broadcast('user.roleUpdate', { void api.broadcast('user.roleUpdate', {
......
...@@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ddp-client'; ...@@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Roles, Users } from '@rocket.chat/models'; import { Roles, Users } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { settings } from '../../../settings/server'; import { settings } from '../../../settings/server';
import { hasPermissionAsync } from '../functions/hasPermission'; import { hasPermissionAsync } from '../functions/hasPermission';
...@@ -79,7 +80,7 @@ Meteor.methods<ServerMethods>({ ...@@ -79,7 +80,7 @@ Meteor.methods<ServerMethods>({
} }
} }
const remove = await Roles.removeUserRoles(user._id, [role._id], scope); const remove = await removeUserFromRolesAsync(user._id, [role._id], scope);
const event = { const event = {
type: 'removed', type: 'removed',
_id: role._id, _id: role._id,
......
import type { INewIncomingIntegration, IIncomingIntegration } from '@rocket.chat/core-typings'; import type { INewIncomingIntegration, IIncomingIntegration } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client'; import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Integrations, Subscriptions, Users, Rooms } from '@rocket.chat/models';
import { Random } from '@rocket.chat/random'; import { Random } from '@rocket.chat/random';
import { Babel } from 'meteor/babel-compiler'; import { Babel } from 'meteor/babel-compiler';
import { Match, check } from 'meteor/check'; import { Match, check } from 'meteor/check';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import _ from 'underscore'; import _ from 'underscore';
import { addUserRolesAsync } from '../../../../../server/lib/roles/addUserRoles';
import { hasPermissionAsync, hasAllPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { hasPermissionAsync, hasAllPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyListener'; import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyListener';
import { validateScriptEngine, isScriptEngineFrozen } from '../../lib/validateScriptEngine'; import { validateScriptEngine, isScriptEngineFrozen } from '../../lib/validateScriptEngine';
...@@ -154,7 +155,7 @@ export const addIncomingIntegration = async (userId: string, integration: INewIn ...@@ -154,7 +155,7 @@ export const addIncomingIntegration = async (userId: string, integration: INewIn
} }
} }
await Roles.addUserRoles(user._id, ['bot']); await addUserRolesAsync(user._id, ['bot']);
const { insertedId } = await Integrations.insertOne(integrationData); const { insertedId } = await Integrations.insertOne(integrationData);
......
import type { IIntegration, INewIncomingIntegration, IUpdateIncomingIntegration } from '@rocket.chat/core-typings'; import type { IIntegration, INewIncomingIntegration, IUpdateIncomingIntegration } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client'; import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Integrations, Subscriptions, Users, Rooms } from '@rocket.chat/models';
import { wrapExceptions } from '@rocket.chat/tools'; import { wrapExceptions } from '@rocket.chat/tools';
import { Babel } from 'meteor/babel-compiler'; import { Babel } from 'meteor/babel-compiler';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import _ from 'underscore'; import _ from 'underscore';
import { addUserRolesAsync } from '../../../../../server/lib/roles/addUserRoles';
import { hasAllPermissionAsync, hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { hasAllPermissionAsync, hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyListener'; import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyListener';
import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateScriptEngine'; import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateScriptEngine';
...@@ -164,7 +165,7 @@ Meteor.methods<ServerMethods>({ ...@@ -164,7 +165,7 @@ Meteor.methods<ServerMethods>({
}); });
} }
await Roles.addUserRoles(user._id, ['bot']); await addUserRolesAsync(user._id, ['bot']);
const updatedIntegration = await Integrations.findOneAndUpdate( const updatedIntegration = await Integrations.findOneAndUpdate(
{ _id: integrationId }, { _id: integrationId },
......
import { Messages, Roles, Rooms, Subscriptions, ReadReceipts } from '@rocket.chat/models'; import { Messages, Rooms, Subscriptions, ReadReceipts } from '@rocket.chat/models';
import type { SubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner'; import type { SubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner';
import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles';
import { FileUpload } from '../../../file-upload/server'; import { FileUpload } from '../../../file-upload/server';
import { notifyOnSubscriptionChanged } from '../lib/notifyListener'; import { notifyOnSubscriptionChanged } from '../lib/notifyListener';
...@@ -29,7 +30,7 @@ export const relinquishRoomOwnerships = async function ( ...@@ -29,7 +30,7 @@ export const relinquishRoomOwnerships = async function (
const changeOwner = subscribedRooms.filter(({ shouldChangeOwner }) => shouldChangeOwner); const changeOwner = subscribedRooms.filter(({ shouldChangeOwner }) => shouldChangeOwner);
for await (const { newOwner, rid } of changeOwner) { for await (const { newOwner, rid } of changeOwner) {
newOwner && (await Roles.addUserRoles(newOwner, ['owner'], rid)); newOwner && (await addUserRolesAsync(newOwner, ['owner'], rid));
} }
const roomIdsToRemove: string[] = subscribedRooms.filter(({ shouldBeRemoved }) => shouldBeRemoved).map(({ rid }) => rid); const roomIdsToRemove: string[] = subscribedRooms.filter(({ shouldBeRemoved }) => shouldBeRemoved).map(({ rid }) => rid);
......
import { MeteorError } from '@rocket.chat/core-services'; import { MeteorError } from '@rocket.chat/core-services';
import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings';
import { Roles } from '@rocket.chat/models'; import { Roles, Subscriptions, Users } from '@rocket.chat/models';
import { validateRoleList } from './validateRoleList'; import { validateRoleList } from './validateRoleList';
import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener';
export const addUserRolesAsync = async (userId: IUser['_id'], roleIds: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> => { export const addUserRolesAsync = async (userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> => {
if (!userId || !roleIds?.length) { if (!userId || !roles?.length) {
return false; return false;
} }
if (!(await validateRoleList(roleIds))) { if (!(await validateRoleList(roles))) {
throw new MeteorError('error-invalid-role', 'Invalid role'); throw new MeteorError('error-invalid-role', 'Invalid role');
} }
await Roles.addUserRoles(userId, roleIds, scope); if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) {
throw new Error('Roles.addUserRoles method received a role scope instead of a scope value.');
}
if (!Array.isArray(roles)) {
roles = [roles];
process.env.NODE_ENV === 'development' && console.warn('[WARN] RolesRaw.addUserRoles: roles should be an array');
}
for await (const roleId of roles) {
const role = await Roles.findOneById<Pick<IRole, '_id' | 'scope'>>(roleId, { projection: { scope: 1 } });
if (!role) {
process.env.NODE_ENV === 'development' && console.warn(`[WARN] RolesRaw.addUserRoles: role: ${roleId} not found`);
continue;
}
if (role.scope === 'Subscriptions' && scope) {
const addRolesResponse = await Subscriptions.addRolesByUserId(userId, [role._id], scope);
if (addRolesResponse.modifiedCount) {
void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId);
}
} else {
await Users.addRolesByUserId(userId, [role._id]);
}
}
return true; return true;
}; };
import { MeteorError } from '@rocket.chat/core-services'; import { MeteorError } from '@rocket.chat/core-services';
import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings';
import { Users, Roles } from '@rocket.chat/models'; import { Users, Subscriptions, Roles } from '@rocket.chat/models';
import { validateRoleList } from './validateRoleList'; import { validateRoleList } from './validateRoleList';
import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener';
export const removeUserFromRolesAsync = async (userId: IUser['_id'], roleIds: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> => { export const removeUserFromRolesAsync = async (userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> => {
if (!userId || !roleIds) { if (!userId || !roles) {
return false; return false;
} }
...@@ -14,10 +15,29 @@ export const removeUserFromRolesAsync = async (userId: IUser['_id'], roleIds: IR ...@@ -14,10 +15,29 @@ export const removeUserFromRolesAsync = async (userId: IUser['_id'], roleIds: IR
throw new MeteorError('error-invalid-user', 'Invalid user'); throw new MeteorError('error-invalid-user', 'Invalid user');
} }
if (!(await validateRoleList(roleIds))) { if (!(await validateRoleList(roles))) {
throw new MeteorError('error-invalid-role', 'Invalid role'); throw new MeteorError('error-invalid-role', 'Invalid role');
} }
await Roles.removeUserRoles(userId, roleIds, scope); if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) {
throw new Error('Roles.removeUserRoles method received a role scope instead of a scope value.');
}
for await (const roleId of roles) {
const role = await Roles.findOneById<Pick<IRole, '_id' | 'scope'>>(roleId, { projection: { scope: 1 } });
if (!role) {
continue;
}
if (role.scope === 'Subscriptions' && scope) {
const removeRolesResponse = await Subscriptions.removeRolesByUserId(userId, [roleId], scope);
if (removeRolesResponse.modifiedCount) {
void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId);
}
} else {
await Users.removeRolesByUserId(userId, [roleId]);
}
}
return true; return true;
}; };
import type { IRole } from '@rocket.chat/core-typings'; import type { IRole } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client'; import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Roles, Users } from '@rocket.chat/models'; import { Users } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { addUserRolesAsync } from '../lib/roles/addUserRoles';
import { removeUserFromRolesAsync } from '../lib/roles/removeUserFromRoles';
const rolesToChangeTo: Map<IRole['_id'], [IRole['_id']]> = new Map([['anonymous', ['user']]]); const rolesToChangeTo: Map<IRole['_id'], [IRole['_id']]> = new Map([['anonymous', ['user']]]);
declare module '@rocket.chat/ddp-client' { declare module '@rocket.chat/ddp-client' {
...@@ -33,9 +36,9 @@ Meteor.methods<ServerMethods>({ ...@@ -33,9 +36,9 @@ Meteor.methods<ServerMethods>({
rolesThatNeedChanges.map(async (role) => { rolesThatNeedChanges.map(async (role) => {
const rolesToAdd = rolesToChangeTo.get(role); const rolesToAdd = rolesToChangeTo.get(role);
if (rolesToAdd) { if (rolesToAdd) {
await Roles.addUserRoles(userId, rolesToAdd); await addUserRolesAsync(userId, rolesToAdd);
} }
await Roles.removeUserRoles(user._id, [role]); await removeUserFromRolesAsync(user._id, [role]);
}), }),
); );
} }
......
...@@ -4,7 +4,6 @@ import { Subscriptions, Users } from '@rocket.chat/models'; ...@@ -4,7 +4,6 @@ import { Subscriptions, Users } from '@rocket.chat/models';
import type { Collection, FindCursor, Db, Filter, FindOptions, Document, CountDocumentsOptions } from 'mongodb'; import type { Collection, FindCursor, Db, Filter, FindOptions, Document, CountDocumentsOptions } from 'mongodb';
import { BaseRaw } from './BaseRaw'; import { BaseRaw } from './BaseRaw';
import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener';
export class RolesRaw extends BaseRaw<IRole> implements IRolesModel { export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<IRole>>) { constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<IRole>>) {
...@@ -19,37 +18,6 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel { ...@@ -19,37 +18,6 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
return options ? this.find(query, options) : this.find(query); return options ? this.find(query, options) : this.find(query);
} }
async addUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> {
if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) {
throw new Error('Roles.addUserRoles method received a role scope instead of a scope value.');
}
if (!Array.isArray(roles)) {
roles = [roles];
process.env.NODE_ENV === 'development' && console.warn('[WARN] RolesRaw.addUserRoles: roles should be an array');
}
for await (const roleId of roles) {
const role = await this.findOneById<Pick<IRole, '_id' | 'scope'>>(roleId, { projection: { scope: 1 } });
if (!role) {
process.env.NODE_ENV === 'development' && console.warn(`[WARN] RolesRaw.addUserRoles: role: ${roleId} not found`);
continue;
}
if (role.scope === 'Subscriptions' && scope) {
// TODO remove dependency from other models - this logic should be inside a function/service
const addRolesResponse = await Subscriptions.addRolesByUserId(userId, [role._id], scope);
if (addRolesResponse.modifiedCount) {
void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId);
}
} else {
await Users.addRolesByUserId(userId, [role._id]);
}
}
return true;
}
async isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> { async isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> {
if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) {
throw new Error('Roles.isUserInRoles method received a role scope instead of a scope value.'); throw new Error('Roles.isUserInRoles method received a role scope instead of a scope value.');
...@@ -78,30 +46,6 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel { ...@@ -78,30 +46,6 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
return false; return false;
} }
async removeUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean> {
if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) {
throw new Error('Roles.removeUserRoles method received a role scope instead of a scope value.');
}
for await (const roleId of roles) {
const role = await this.findOneById<Pick<IRole, '_id' | 'scope'>>(roleId, { projection: { scope: 1 } });
if (!role) {
continue;
}
if (role.scope === 'Subscriptions' && scope) {
const removeRolesResponse = await Subscriptions.removeRolesByUserId(userId, [roleId], scope);
if (removeRolesResponse.modifiedCount) {
void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId);
}
} else {
await Users.removeRolesByUserId(userId, [roleId]);
}
}
return true;
}
async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise<IRole | null>; async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise<IRole | null>;
async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options: FindOptions<IRole>): Promise<IRole | null>; async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options: FindOptions<IRole>): Promise<IRole | null>;
......
...@@ -5,9 +5,7 @@ import type { IBaseModel } from './IBaseModel'; ...@@ -5,9 +5,7 @@ import type { IBaseModel } from './IBaseModel';
export interface IRolesModel extends IBaseModel<IRole> { export interface IRolesModel extends IBaseModel<IRole> {
findByUpdatedDate(updatedAfterDate: Date, options?: FindOptions<IRole>): FindCursor<IRole>; findByUpdatedDate(updatedAfterDate: Date, options?: FindOptions<IRole>): FindCursor<IRole>;
addUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean>;
isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean>; isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean>;
removeUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise<boolean>;
findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise<IRole | null>; findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise<IRole | null>;
findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options: FindOptions<IRole>): Promise<IRole | null>; findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options: FindOptions<IRole>): Promise<IRole | null>;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment