Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BREAK] Freeze Custom Roles #27481

Merged
merged 15 commits into from Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
88 changes: 1 addition & 87 deletions apps/meteor/app/api/server/v1/roles.ts
@@ -1,12 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { check, Match } from 'meteor/check';
import {
isRoleAddUserToRoleProps,
isRoleCreateProps,
isRoleDeleteProps,
isRoleRemoveUserFromRoleProps,
isRoleUpdateProps,
} from '@rocket.chat/rest-typings';
import { isRoleAddUserToRoleProps, isRoleDeleteProps, isRoleRemoveUserFromRoleProps } from '@rocket.chat/rest-typings';
import type { IRole } from '@rocket.chat/core-typings';
import { Roles } from '@rocket.chat/models';
import { api } from '@rocket.chat/core-services';
Expand All @@ -19,8 +13,6 @@ import { settings } from '../../../settings/server/index';
import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { updateRole } from '../../../../server/lib/roles/updateRole';
import { insertRole } from '../../../../server/lib/roles/insertRole';

API.v1.addRoute(
'roles.list',
Expand Down Expand Up @@ -58,48 +50,6 @@ API.v1.addRoute(
},
);

API.v1.addRoute(
'roles.create',
{ authRequired: true },
{
async post() {
if (!isRoleCreateProps(this.bodyParams)) {
throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.');
}

const userId = Meteor.userId();

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;

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 options = {
broadcastUpdate: settings.get<boolean>('UI_DisplayRoles'),
};

const role = insertRole(roleData, options);

return API.v1.success({
role,
});
},
},
);

API.v1.addRoute(
'roles.addUserToRole',
{ authRequired: true },
Expand Down Expand Up @@ -190,42 +140,6 @@ API.v1.addRoute(
},
);

API.v1.addRoute(
'roles.update',
{ authRequired: true },
{
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');
}

const { roleId, name, scope, description, mandatory2fa } = this.bodyParams;

const roleData = {
description: description || '',
...(mandatory2fa !== undefined && { mandatory2fa }),
name,
scope: scope || 'Users',
protected: false,
};

const options = {
broadcastUpdate: settings.get<boolean>('UI_DisplayRoles'),
};

const role = updateRole(roleId, roleData, options);

return API.v1.success({
role,
});
},
},
);

API.v1.addRoute(
'roles.delete',
{ authRequired: true },
Expand Down
1 change: 0 additions & 1 deletion apps/meteor/app/authorization/server/index.js
Expand Up @@ -10,7 +10,6 @@ import './methods/addUserToRole';
import './methods/deleteRole';
import './methods/removeRoleFromPermission';
import './methods/removeUserFromRole';
import './methods/saveRole';
import './streamer/permissions';

export {
Expand Down
48 changes: 0 additions & 48 deletions apps/meteor/app/authorization/server/methods/saveRole.ts

This file was deleted.

6 changes: 4 additions & 2 deletions apps/meteor/client/views/admin/permissions/EditRolePage.tsx
Expand Up @@ -10,7 +10,7 @@ import GenericModal from '../../../components/GenericModal';
import VerticalBar from '../../../components/VerticalBar';
import RoleForm from './RoleForm';

const EditRolePage = ({ role }: { role?: IRole }): ReactElement => {
const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: boolean }): ReactElement => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const setModal = useSetModal();
Expand Down Expand Up @@ -74,6 +74,8 @@ const EditRolePage = ({ role }: { role?: IRole }): ReactElement => {
}
};

const deleteRoleMessage = isEnterprise ? t('Delete_Role_Warning') : t('Delete_Role_Warning_Community_Edition');

setModal(
<GenericModal
variant='danger'
Expand All @@ -82,7 +84,7 @@ const EditRolePage = ({ role }: { role?: IRole }): ReactElement => {
onCancel={(): void => setModal()}
confirmText={t('Delete')}
>
{t('Delete_Role_Warning')}
{deleteRoleMessage}
</GenericModal>,
);
});
Expand Down
Expand Up @@ -4,19 +4,26 @@ import { useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';

import PageSkeleton from '../../../components/PageSkeleton';
import { useIsEnterprise } from '../../../hooks/useIsEnterprise';
import EditRolePage from './EditRolePage';
import { useRole } from './hooks/useRole';

const EditRolePageWithData = ({ roleId }: { roleId?: IRole['_id'] }): ReactElement => {
const t = useTranslation();
const role = useRole(roleId);
const context = useRouteParameter('context');
const { data, isLoading } = useIsEnterprise();

if (!role && context === 'edit') {
return <Callout type='danger'>{t('error-invalid-role')}</Callout>;
}

return <EditRolePage key={roleId} role={role} />;
if (isLoading || !data) {
return <PageSkeleton />;
}

return <EditRolePage key={roleId} role={role} isEnterprise={data.isEnterprise} />;
};

export default EditRolePageWithData;
1 change: 1 addition & 0 deletions apps/meteor/ee/definition/rest/index.ts
Expand Up @@ -3,3 +3,4 @@
import './v1/engagementDashboard';
import './v1/omnichannel';
import './v1/sessions';
import './v1/roles';
84 changes: 84 additions & 0 deletions apps/meteor/ee/definition/rest/v1/roles.ts
@@ -0,0 +1,84 @@
import type { IRole } from '@rocket.chat/core-typings';
import Ajv from 'ajv';

const ajv = new Ajv({
coerceTypes: true,
});

type RoleCreateProps = Pick<IRole, 'name'> & Partial<Pick<IRole, 'description' | 'scope' | 'mandatory2fa'>>;

const roleCreatePropsSchema = {
type: 'object',
properties: {
name: {
type: 'string',
},
description: {
type: 'string',
nullable: true,
},
scope: {
type: 'string',
enum: ['Users', 'Subscriptions'],
nullable: true,
},
mandatory2fa: {
type: 'boolean',
nullable: true,
},
},
required: ['name'],
additionalProperties: false,
};

export const isRoleCreateProps = ajv.compile<RoleCreateProps>(roleCreatePropsSchema);

type RoleUpdateProps = {
roleId: IRole['_id'];
name: IRole['name'];
} & Partial<RoleCreateProps>;

const roleUpdatePropsSchema = {
type: 'object',
properties: {
roleId: {
type: 'string',
},
name: {
type: 'string',
},
description: {
type: 'string',
nullable: true,
},
scope: {
type: 'string',
enum: ['Users', 'Subscriptions'],
nullable: true,
},
mandatory2fa: {
type: 'boolean',
nullable: true,
},
},
required: ['roleId', 'name'],
additionalProperties: false,
};

export const isRoleUpdateProps = ajv.compile<RoleUpdateProps>(roleUpdatePropsSchema);

declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Endpoints {
'/v1/roles.create': {
POST: (params: RoleCreateProps) => {
role: IRole;
};
};
'/v1/roles.update': {
POST: (role: RoleUpdateProps) => {
role: IRole;
};
};
}
}
1 change: 1 addition & 0 deletions apps/meteor/ee/server/api/index.ts
Expand Up @@ -2,3 +2,4 @@ import './api';
import './ldap';
import './licenses';
import './sessions';
import './roles';