From f973a9c6851471d676432303b717142bb887a507 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Tue, 21 Jan 2025 08:17:20 -0300 Subject: [PATCH] refactor: remove authorization method calls (server) --- apps/meteor/app/api/server/v1/roles.ts | 3 +- .../server/methods/addUserToRole.ts | 129 +++++++++-------- .../server/methods/removeUserFromRole.ts | 135 ++++++++++-------- apps/meteor/app/bot-helpers/server/index.ts | 12 +- .../app/lib/server/methods/setAdminStatus.ts | 8 +- 5 files changed, 158 insertions(+), 129 deletions(-) diff --git a/apps/meteor/app/api/server/v1/roles.ts b/apps/meteor/app/api/server/v1/roles.ts index b52e20e129200..ac559d3ed1320 100644 --- a/apps/meteor/app/api/server/v1/roles.ts +++ b/apps/meteor/app/api/server/v1/roles.ts @@ -9,6 +9,7 @@ import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUse import { getUsersInRolePaginated } from '../../../authorization/server/functions/getUsersInRole'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole'; +import { addUserToRole } from '../../../authorization/server/methods/addUserToRole'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { notifyOnRoleChanged } from '../../../lib/server/lib/notifyListener'; import { settings } from '../../../settings/server/index'; @@ -81,7 +82,7 @@ API.v1.addRoute( throw new Meteor.Error('error-user-already-in-role', 'User already in role'); } - await Meteor.callAsync('authorization:addUserToRole', role._id, user.username, roomId); + await addUserToRole(this.userId, role._id, user.username, roomId); return API.v1.success({ role, diff --git a/apps/meteor/app/authorization/server/methods/addUserToRole.ts b/apps/meteor/app/authorization/server/methods/addUserToRole.ts index 6f26c4a614613..11d28a63167ec 100644 --- a/apps/meteor/app/authorization/server/methods/addUserToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addUserToRole.ts @@ -16,80 +16,91 @@ declare module '@rocket.chat/ddp-client' { } } -Meteor.methods({ - async 'authorization:addUserToRole'(roleId: IRole['_id'], username: IUser['username'], scope) { - const userId = Meteor.userId(); +export const addUserToRole = async (userId: string, roleId: string, username: IUser['username'], scope?: string): Promise => { + if (!(await hasPermissionAsync(userId, 'access-permissions'))) { + throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { + method: 'authorization:addUserToRole', + action: 'Accessing_permissions', + }); + } - if (!userId || !(await hasPermissionAsync(userId, 'access-permissions'))) { - throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { - method: 'authorization:addUserToRole', - action: 'Accessing_permissions', - }); - } + if (!roleId || typeof roleId.valueOf() !== 'string' || !username || typeof username.valueOf() !== 'string') { + throw new Meteor.Error('error-invalid-arguments', 'Invalid arguments', { + method: 'authorization:addUserToRole', + }); + } - if (!roleId || typeof roleId.valueOf() !== 'string' || !username || typeof username.valueOf() !== 'string') { - throw new Meteor.Error('error-invalid-arguments', 'Invalid arguments', { - method: 'authorization:addUserToRole', - }); - } + let role = await Roles.findOneById>(roleId, { projection: { _id: 1 } }); + if (!role) { + role = await Roles.findOneByName>(roleId, { projection: { _id: 1 } }); - let role = await Roles.findOneById>(roleId, { projection: { _id: 1 } }); if (!role) { - role = await Roles.findOneByName>(roleId, { projection: { _id: 1 } }); - - if (!role) { - throw new Meteor.Error('error-invalid-role', 'Invalid Role', { - method: 'authorization:addUserToRole', - }); - } - methodDeprecationLogger.deprecatedParameterUsage( - 'authorization:addUserToRole', - 'role', - '7.0.0', - ({ parameter, method, version }) => `Calling ${method} with \`${parameter}\` names is deprecated and will be removed ${version}`, - ); - } - - if (role._id === 'admin' && !(await hasPermissionAsync(userId, 'assign-admin-role'))) { - throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', { + throw new Meteor.Error('error-invalid-role', 'Invalid Role', { method: 'authorization:addUserToRole', - action: 'Assign_admin', }); } + methodDeprecationLogger.deprecatedParameterUsage( + 'authorization:addUserToRole', + 'role', + '7.0.0', + ({ parameter, method, version }) => `Calling ${method} with \`${parameter}\` names is deprecated and will be removed ${version}`, + ); + } + + if (role._id === 'admin' && !(await hasPermissionAsync(userId, 'assign-admin-role'))) { + throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', { + method: 'authorization:addUserToRole', + action: 'Assign_admin', + }); + } - const user = await Users.findOneByUsernameIgnoringCase(username, { - projection: { - _id: 1, + const user = await Users.findOneByUsernameIgnoringCase(username, { + projection: { + _id: 1, + }, + }); + + if (!user?._id) { + throw new Meteor.Error('error-user-not-found', 'User not found', { + method: 'authorization:addUserToRole', + }); + } + + // verify if user can be added to given scope + if (scope && !(await Roles.canAddUserToRole(user._id, role._id, scope))) { + throw new Meteor.Error('error-invalid-user', 'User is not part of given room', { + method: 'authorization:addUserToRole', + }); + } + + const add = await addUserRolesAsync(user._id, [role._id], scope); + + if (settings.get('UI_DisplayRoles')) { + void api.broadcast('user.roleUpdate', { + type: 'added', + _id: role._id, + u: { + _id: user._id, + username, }, + scope, }); + } - if (!user?._id) { - throw new Meteor.Error('error-user-not-found', 'User not found', { - method: 'authorization:addUserToRole', - }); - } + return add; +}; - // verify if user can be added to given scope - if (scope && !(await Roles.canAddUserToRole(user._id, role._id, scope))) { - throw new Meteor.Error('error-invalid-user', 'User is not part of given room', { - method: 'authorization:addUserToRole', - }); - } +Meteor.methods({ + async 'authorization:addUserToRole'(roleId: IRole['_id'], username: IUser['username'], scope) { + const userId = Meteor.userId(); - const add = await addUserRolesAsync(user._id, [role._id], scope); - - if (settings.get('UI_DisplayRoles')) { - void api.broadcast('user.roleUpdate', { - type: 'added', - _id: role._id, - u: { - _id: user._id, - username, - }, - scope, + if (!userId) { + throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', { + method: 'authorization:addUserToRole', + action: 'Accessing_permissions', }); } - return add; + return addUserToRole(userId, roleId, username, scope); }, }); diff --git a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts index 77f8c54e2f494..fe7312886b267 100644 --- a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts +++ b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts @@ -16,85 +16,96 @@ declare module '@rocket.chat/ddp-client' { } } -Meteor.methods({ - async 'authorization:removeUserFromRole'(roleId, username, scope) { - const userId = Meteor.userId(); +export const removeUserFromRole = async (userId: string, roleId: string, username: IUser['username'], scope?: string): Promise => { + if (!(await hasPermissionAsync(userId, 'access-permissions'))) { + throw new Meteor.Error('error-action-not-allowed', 'Access permissions is not allowed', { + method: 'authorization:removeUserFromRole', + action: 'Accessing_permissions', + }); + } - if (!userId || !(await hasPermissionAsync(userId, 'access-permissions'))) { - throw new Meteor.Error('error-action-not-allowed', 'Access permissions is not allowed', { - method: 'authorization:removeUserFromRole', - action: 'Accessing_permissions', - }); - } + if (!roleId || typeof roleId.valueOf() !== 'string' || !username || typeof username.valueOf() !== 'string') { + throw new Meteor.Error('error-invalid-arguments', 'Invalid arguments', { + method: 'authorization:removeUserFromRole', + }); + } - if (!roleId || typeof roleId.valueOf() !== 'string' || !username || typeof username.valueOf() !== 'string') { - throw new Meteor.Error('error-invalid-arguments', 'Invalid arguments', { + let role = await Roles.findOneById>(roleId, { projection: { _id: 1 } }); + if (!role) { + role = await Roles.findOneByName>(roleId, { projection: { _id: 1 } }); + if (!role) { + throw new Meteor.Error('error-invalid-role', 'Invalid Role', { method: 'authorization:removeUserFromRole', }); } - let role = await Roles.findOneById>(roleId, { projection: { _id: 1 } }); - if (!role) { - role = await Roles.findOneByName>(roleId, { projection: { _id: 1 } }); - if (!role) { - throw new Meteor.Error('error-invalid-role', 'Invalid Role', { - method: 'authorization:removeUserFromRole', - }); - } + methodDeprecationLogger.deprecatedParameterUsage( + 'authorization:removeUserFromRole', + 'role', + '7.0.0', + ({ parameter, method, version }) => `Calling ${method} with ${parameter} names is deprecated and will be removed ${version}`, + ); + } - methodDeprecationLogger.deprecatedParameterUsage( - 'authorization:removeUserFromRole', - 'role', - '7.0.0', - ({ parameter, method, version }) => `Calling ${method} with ${parameter} names is deprecated and will be removed ${version}`, - ); - } + const user = await Users.findOneByUsernameIgnoringCase(username, { + projection: { + _id: 1, + roles: 1, + }, + }); - const user = await Users.findOneByUsernameIgnoringCase(username, { - projection: { - _id: 1, - roles: 1, + if (!user?._id) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'authorization:removeUserFromRole', + }); + } + + // prevent removing last user from admin role + if (role._id === 'admin') { + const adminCount = await Users.col.countDocuments({ + roles: { + $in: ['admin'], }, }); - if (!user?._id) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { - method: 'authorization:removeUserFromRole', + const userIsAdmin = user.roles?.indexOf('admin') > -1; + if (adminCount === 1 && userIsAdmin) { + throw new Meteor.Error('error-action-not-allowed', 'Leaving the app without admins is not allowed', { + method: 'removeUserFromRole', + action: 'Remove_last_admin', }); } + } - // prevent removing last user from admin role - if (role._id === 'admin') { - const adminCount = await Users.col.countDocuments({ - roles: { - $in: ['admin'], - }, - }); + const remove = await removeUserFromRolesAsync(user._id, [role._id], scope); + const event = { + type: 'removed', + _id: role._id, + u: { + _id: user._id, + username, + }, + scope, + } as const; + if (settings.get('UI_DisplayRoles')) { + void api.broadcast('user.roleUpdate', event); + } + void api.broadcast('federation.userRoleChanged', { ...event, givenByUserId: userId }); - const userIsAdmin = user.roles?.indexOf('admin') > -1; - if (adminCount === 1 && userIsAdmin) { - throw new Meteor.Error('error-action-not-allowed', 'Leaving the app without admins is not allowed', { - method: 'removeUserFromRole', - action: 'Remove_last_admin', - }); - } - } + return remove; +}; - const remove = await removeUserFromRolesAsync(user._id, [role._id], scope); - const event = { - type: 'removed', - _id: role._id, - u: { - _id: user._id, - username, - }, - scope, - } as const; - if (settings.get('UI_DisplayRoles')) { - void api.broadcast('user.roleUpdate', event); +Meteor.methods({ + async 'authorization:removeUserFromRole'(roleId, username, scope) { + const userId = Meteor.userId(); + + if (!userId) { + throw new Meteor.Error('error-action-not-allowed', 'Access permissions is not allowed', { + method: 'authorization:removeUserFromRole', + action: 'Accessing_permissions', + }); } - void api.broadcast('federation.userRoleChanged', { ...event, givenByUserId: userId }); - return remove; + return removeUserFromRole(userId, roleId, username, scope); }, }); diff --git a/apps/meteor/app/bot-helpers/server/index.ts b/apps/meteor/app/bot-helpers/server/index.ts index 6c0984ae483d9..6db9cc24c6b4b 100644 --- a/apps/meteor/app/bot-helpers/server/index.ts +++ b/apps/meteor/app/bot-helpers/server/index.ts @@ -7,6 +7,8 @@ import type { Filter, FindCursor } from 'mongodb'; import { removeUserFromRoomMethod } from '../../../server/methods/removeUserFromRoom'; import { hasRoleAsync } from '../../authorization/server/functions/hasRole'; +import { addUserToRole } from '../../authorization/server/methods/addUserToRole'; +import { removeUserFromRole } from '../../authorization/server/methods/removeUserFromRole'; import { addUsersToRoomMethod } from '../../lib/server/methods/addUsersToRoom'; import { settings } from '../../settings/server'; @@ -61,12 +63,12 @@ class BotHelpers { return p; } - async addUserToRole(userName: string, roleId: string): Promise { - await Meteor.callAsync('authorization:addUserToRole', roleId, userName); + async addUserToRole(userName: string, roleId: string, userId: string): Promise { + await addUserToRole(userId, roleId, userName); } - async removeUserFromRole(userName: string, roleId: string): Promise { - await Meteor.callAsync('authorization:removeUserFromRole', roleId, userName); + async removeUserFromRole(userName: string, roleId: string, userId: string): Promise { + await removeUserFromRole(userId, roleId, userName); } async addUserToRoom(userName: string, room: string): Promise { @@ -205,7 +207,7 @@ Meteor.methods({ async botRequest(...args) { const userID = Meteor.userId(); if (userID && (await hasRoleAsync(userID, 'bot'))) { - return botHelpers.request(...args); + return botHelpers.request(...args, userID); } throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'botRequest' }); }, diff --git a/apps/meteor/app/lib/server/methods/setAdminStatus.ts b/apps/meteor/app/lib/server/methods/setAdminStatus.ts index 300aa735b014a..c91f5ad19a6c9 100644 --- a/apps/meteor/app/lib/server/methods/setAdminStatus.ts +++ b/apps/meteor/app/lib/server/methods/setAdminStatus.ts @@ -5,6 +5,8 @@ import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; +import { addUserToRole } from '../../../authorization/server/methods/addUserToRole'; +import { removeUserFromRole } from '../../../authorization/server/methods/removeUserFromRole'; declare module '@rocket.chat/ddp-client' { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -34,8 +36,10 @@ Meteor.methods({ } if (admin) { - return Meteor.callAsync('authorization:addUserToRole', 'admin', user?.username); + await addUserToRole(uid, 'admin', user?.username); + return; } - return Meteor.callAsync('authorization:removeUserFromRole', 'admin', user?.username); + + await removeUserFromRole(uid, 'admin', user?.username); }, });