diff --git a/apps/meteor/app/api/server/api.d.ts b/apps/meteor/app/api/server/api.d.ts index 0bee475c759e..7d82f0de9031 100644 --- a/apps/meteor/app/api/server/api.d.ts +++ b/apps/meteor/app/api/server/api.d.ts @@ -8,6 +8,7 @@ import type { UrlParams, } from '@rocket.chat/rest-typings'; import type { IUser, IMethodConnection } from '@rocket.chat/core-typings'; +import type { ValidateFunction } from 'ajv'; import { ITwoFactorOptions } from '../../2fa/server/code'; @@ -54,7 +55,7 @@ export type NonEnterpriseTwoFactorOptions = { twoFactorOptions: ITwoFactorOptions; }; -type Options = +type Options = ( | { permissionsRequired?: string[]; authRequired?: boolean; @@ -64,7 +65,10 @@ type Options = authRequired: true; twoFactorRequired: true; twoFactorOptions?: ITwoFactorOptions; - }; + } +) & { + validateParams?: ValidateFunction; +}; type Request = { method: 'GET' | 'POST' | 'PUT' | 'DELETE'; @@ -80,9 +84,17 @@ type PartialThis = { type ActionThis = { urlParams: UrlParams; // TODO make it unsafe - readonly queryParams: TMethod extends 'GET' ? Partial> : Record; + readonly queryParams: TMethod extends 'GET' + ? TOptions extends { validateParams: ValidateFunction } + ? T + : Partial> + : Record; // TODO make it unsafe - readonly bodyParams: TMethod extends 'GET' ? Record : Partial>; + readonly bodyParams: TMethod extends 'GET' + ? Record + : TOptions extends { validateParams: ValidateFunction } + ? T + : Partial>; readonly request: Request; requestParams(): OperationParams; getLoggedInUser(): IUser | undefined; diff --git a/apps/meteor/app/api/server/api.js b/apps/meteor/app/api/server/api.js index 9d6ddfb11718..7259e14cca5b 100644 --- a/apps/meteor/app/api/server/api.js +++ b/apps/meteor/app/api/server/api.js @@ -397,6 +397,9 @@ export class APIClass extends Restivus { try { api.enforceRateLimit(objectForRateLimitMatch, this.request, this.response, this.userId); + if (_options.validateParams && _options.validateParams(this.request.method === 'GET' ? this.queryParams : this.bodyParams)) { + throw new Meteor.Error('error-invalid-params', _options.validateParams.errors?.map((error) => error.message).join('\n ')); + } if (shouldVerifyPermissions && (!this.userId || !hasAllPermission(this.userId, _options.permissionsRequired))) { throw new Meteor.Error('error-unauthorized', 'User does not have the permissions required for this action', { permissions: _options.permissionsRequired,