Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 83 additions & 60 deletions apps/meteor/ee/server/api/roles.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -72,6 +77,16 @@ const roleUpdatePropsSchema = {

export const isRoleUpdateProps = ajv.compile<RoleUpdateProps>(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 {
Expand All @@ -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<boolean>('UI_DisplayRoles'),
};
const options = {
broadcastUpdate: settings.get<boolean>('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<boolean>('UI_DisplayRoles'),
};
const options = {
broadcastUpdate: settings.get<boolean>('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 });
},
);
Loading