diff --git a/apps/meteor/ee/server/api/roles.ts b/apps/meteor/ee/server/api/roles.ts index fdba9832bebda..761c0b6004e33 100644 --- a/apps/meteor/ee/server/api/roles.ts +++ b/apps/meteor/ee/server/api/roles.ts @@ -1,7 +1,12 @@ import type { IRole } from '@rocket.chat/core-typings'; import { License } from '@rocket.chat/license'; import { Roles } from '@rocket.chat/models'; -import { ajv } from '@rocket.chat/rest-typings'; +import { + ajv, + validateBadRequestErrorResponse, + validateUnauthorizedErrorResponse, + validateForbiddenErrorResponse, +} from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; import { settings } from '../../../app/settings/server/index'; @@ -72,6 +77,16 @@ const roleUpdatePropsSchema = { export const isRoleUpdateProps = ajv.compile(roleUpdatePropsSchema); +const roleResponseSchema = ajv.compile<{ role: IRole }>({ + type: 'object', + properties: { + role: { $ref: '#/components/schemas/IRole' }, + success: { type: 'boolean', enum: [true] }, + }, + required: ['role', 'success'], + additionalProperties: false, +}); + declare module '@rocket.chat/rest-typings' { // eslint-disable-next-line @typescript-eslint/naming-convention interface Endpoints { @@ -88,86 +103,94 @@ declare module '@rocket.chat/rest-typings' { } } -API.v1.addRoute( +API.v1.post( 'roles.create', - { authRequired: true, license: ['custom-roles'] }, { - async post() { - if (!License.hasModule('custom-roles')) { - throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature'); - } - - if (!isRoleCreateProps(this.bodyParams)) { - throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.'); - } + authRequired: true, + license: ['custom-roles'], + body: isRoleCreateProps, + response: { + 200: roleResponseSchema, + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, + }, + }, + async function action() { + if (!License.hasModule('custom-roles')) { + throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature'); + } - const { userId } = this; + const { userId } = this; - if (!userId || !(await hasPermissionAsync(userId, 'access-permissions'))) { - throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); - } + if (!userId || !(await hasPermissionAsync(userId, 'access-permissions'))) { + throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); + } - const { name, scope, description, mandatory2fa } = this.bodyParams; + const { name, scope, description, mandatory2fa } = this.bodyParams; - if (await Roles.findOneByIdOrName(name)) { - throw new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists'); - } + if (await Roles.findOneByIdOrName(name)) { + throw new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists'); + } - const roleData = { - description: description || '', - ...(mandatory2fa !== undefined && { mandatory2fa }), - name, - scope: scope || 'Users', - protected: false, - }; + const roleData = { + description: description || '', + ...(mandatory2fa !== undefined && { mandatory2fa }), + name, + scope: scope || 'Users', + protected: false, + }; - const options = { - broadcastUpdate: settings.get('UI_DisplayRoles'), - }; + const options = { + broadcastUpdate: settings.get('UI_DisplayRoles'), + }; - const role = await insertRoleAsync(roleData, options); + const role = await insertRoleAsync(roleData, options); - return API.v1.success({ role }); - }, + return API.v1.success({ role }); }, ); -API.v1.addRoute( +API.v1.post( 'roles.update', - { authRequired: true, license: ['custom-roles'] }, { - async post() { - if (!isRoleUpdateProps(this.bodyParams)) { - throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.'); - } - - if (!(await hasPermissionAsync(this.userId, 'access-permissions'))) { - throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); - } + authRequired: true, + license: ['custom-roles'], + body: isRoleUpdateProps, + response: { + 200: roleResponseSchema, + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, + }, + }, + async function action() { + if (!(await hasPermissionAsync(this.userId, 'access-permissions'))) { + throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed'); + } - const { roleId, name, scope, description, mandatory2fa } = this.bodyParams; + const { roleId, name, scope, description, mandatory2fa } = this.bodyParams; - const role = await Roles.findOne(roleId); + const role = await Roles.findOne(roleId); - if (!License.hasModule('custom-roles') && !role?.protected) { - throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature'); - } + if (!License.hasModule('custom-roles') && !role?.protected) { + throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature'); + } - const roleData = { - description: description || '', - ...(mandatory2fa !== undefined && { mandatory2fa }), - name, - scope: scope || 'Users', - protected: false, - }; + const roleData = { + description: description || '', + ...(mandatory2fa !== undefined && { mandatory2fa }), + name, + scope: scope || 'Users', + protected: false, + }; - const options = { - broadcastUpdate: settings.get('UI_DisplayRoles'), - }; + const options = { + broadcastUpdate: settings.get('UI_DisplayRoles'), + }; - const updatedRole = await updateRole(roleId, roleData, options); + const updatedRole = await updateRole(roleId, roleData, options); - return API.v1.success({ role: updatedRole }); - }, + return API.v1.success({ role: updatedRole }); }, );