feat: 后端增加各个权限点的接入

pull/49/head
moonrailgun 3 years ago
parent 2662d154c6
commit b976a3b734

@ -153,6 +153,15 @@
color: rgba(255, 255, 255, 0.65); color: rgba(255, 255, 255, 0.65);
} }
} }
.ant-dropdown-menu-item-disabled, .ant-dropdown-menu-submenu-title-disabled {
color: rgba(255, 255, 255, 0.25);
&:hover {
color: rgba(255, 255, 255, 0.25);
background-color: transparent;
}
}
} }
} }

@ -74,5 +74,23 @@ export function call(ctx: TcContext) {
groupId, groupId,
}); });
}, },
/**
*
*/
async checkUserPermissions(
groupId: string,
userId: string,
permissions: string[]
): Promise<boolean[]> {
const userAllPermissions: string[] = await ctx.call(
'group.getUserAllPermissions',
{
groupId,
userId,
}
);
return permissions.map((p) => (userAllPermissions ?? []).includes(p));
},
}; };
} }

@ -0,0 +1,17 @@
export const PERMISSION = {
/**
* core
*/
core: {
owner: '__group_owner__', // 保留字段, 用于标识群组所有者
message: 'core.message',
invite: 'core.invite',
unlimitedInvite: 'core.unlimitedInvite',
groupDetail: 'core.groupDetail',
managePanel: 'core.managePanel',
manageInvite: 'core.manageInvite',
manageRoles: 'core.manageRoles',
},
};
export const allPermission = [...Object.values(PERMISSION.core)];

@ -10,6 +10,7 @@ import {
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'; import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses';
import _ from 'lodash'; import _ from 'lodash';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { allPermission } from '../../lib/role';
import { User } from '../user/user'; import { User } from '../user/user';
export enum GroupPanelType { export enum GroupPanelType {
@ -196,19 +197,13 @@ export class Group extends TimeStamps implements Base {
this: ReturnModelType<typeof Group>, this: ReturnModelType<typeof Group>,
groupId: string, groupId: string,
roleId: string, roleId: string,
roleName: string, roleName: string
operatorUserId: string
): Promise<Group> { ): Promise<Group> {
const group = await this.findById(groupId); const group = await this.findById(groupId);
if (!group) { if (!group) {
throw new Error('Not Found Group'); throw new Error('Not Found Group');
} }
// 首先判断是否有修改权限的权限
if (String(group.owner) !== operatorUserId) {
throw new Error('No Permission');
}
const modifyRole = group.roles.find((role) => String(role._id) === roleId); const modifyRole = group.roles.find((role) => String(role._id) === roleId);
if (!modifyRole) { if (!modifyRole) {
throw new Error('Not Found Role'); throw new Error('Not Found Role');
@ -227,19 +222,13 @@ export class Group extends TimeStamps implements Base {
this: ReturnModelType<typeof Group>, this: ReturnModelType<typeof Group>,
groupId: string, groupId: string,
roleId: string, roleId: string,
permissions: string[], permissions: string[]
operatorUserId: string
): Promise<Group> { ): Promise<Group> {
const group = await this.findById(groupId); const group = await this.findById(groupId);
if (!group) { if (!group) {
throw new Error('Not Found Group'); throw new Error('Not Found Group');
} }
// 首先判断是否有修改权限的权限
if (String(group.owner) !== operatorUserId) {
throw new Error('No Permission');
}
const modifyRole = group.roles.find((role) => String(role._id) === roleId); const modifyRole = group.roles.find((role) => String(role._id) === roleId);
if (!modifyRole) { if (!modifyRole) {
throw new Error('Not Found Role'); throw new Error('Not Found Role');
@ -264,6 +253,11 @@ export class Group extends TimeStamps implements Base {
throw new Error('Not Found Group'); throw new Error('Not Found Group');
} }
if (String(group.owner) === userId) {
// 群组管理者有所有权限
return [...allPermission];
}
const member = group.members.find( const member = group.members.find(
(member) => String(member.userId) === userId (member) => String(member.userId) === userId
); );

@ -22,6 +22,7 @@ import {
} from 'tailchat-server-sdk'; } from 'tailchat-server-sdk';
import { call } from '../../../lib/call'; import { call } from '../../../lib/call';
import moment from 'moment'; import moment from 'moment';
import { PERMISSION } from '../../../lib/role';
interface GroupService interface GroupService
extends TcService, extends TcService,
@ -320,14 +321,30 @@ class GroupService extends TcService {
throw new EntityError(t('该数据不允许修改')); throw new EntityError(t('该数据不允许修改'));
} }
const group = await this.adapter.model.findById(groupId).exec(); const [isGroupOwner, hasRolePermission] = await call(
if (String(group.owner) !== userId) { ctx
throw new NoPermissionError(); ).checkUserPermissions(groupId, userId, [
PERMISSION.core.owner,
PERMISSION.core.manageRoles,
]);
if (fieldName === 'fallbackPermissions') {
if (!hasRolePermission) {
throw new NoPermissionError(t('没有操作权限'));
} }
} else if (!isGroupOwner) {
throw new NoPermissionError(t('不是群组管理员无法编辑'));
}
const group = await this.adapter.model.findById(groupId).exec();
group[fieldName] = fieldValue; group[fieldName] = fieldValue;
await group.save(); await group.save();
if (fieldName === 'fallbackPermissions') {
await this.cleanGroupAllUserPermissionCache(groupId);
}
this.notifyGroupInfoUpdate(ctx, group); this.notifyGroupInfoUpdate(ctx, group);
} }
@ -472,15 +489,12 @@ class GroupService extends TcService {
const { groupId, memberIds, roles } = ctx.params; const { groupId, memberIds, roles } = ctx.params;
const { t, userId } = ctx.meta; const { t, userId } = ctx.meta;
const isOwner: boolean = await this.actions['isGroupOwner']( const [hasPermission] = await call(ctx).checkUserPermissions(
{
groupId, groupId,
}, userId,
{ [PERMISSION.core.manageRoles]
parentCtx: ctx,
}
); );
if (!isOwner) { if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限')); throw new NoPermissionError(t('没有操作权限'));
} }
@ -520,15 +534,12 @@ class GroupService extends TcService {
const { groupId, memberIds, roles } = ctx.params; const { groupId, memberIds, roles } = ctx.params;
const { t, userId } = ctx.meta; const { t, userId } = ctx.meta;
const isOwner: boolean = await this.actions['isGroupOwner']( const [hasPermission] = await call(ctx).checkUserPermissions(
{
groupId, groupId,
}, userId,
{ [PERMISSION.core.manageRoles]
parentCtx: ctx,
}
); );
if (!isOwner) { if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限')); throw new NoPermissionError(t('没有操作权限'));
} }
@ -570,17 +581,15 @@ class GroupService extends TcService {
) { ) {
const { groupId, name, type, parentId, provider, pluginPanelName, meta } = const { groupId, name, type, parentId, provider, pluginPanelName, meta } =
ctx.params; ctx.params;
const { t } = ctx.meta; const { t, userId } = ctx.meta;
const isOwner: boolean = await this.actions['isGroupOwner'](
{ const [hasPermission] = await call(ctx).checkUserPermissions(
groupId, groupId,
}, userId,
{ [PERMISSION.core.managePanel]
parentCtx: ctx,
}
); );
if (!isOwner) { if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限')); throw new NoPermissionError(t('没有操作权限'));
} }
@ -626,16 +635,14 @@ class GroupService extends TcService {
) { ) {
const { groupId, panelId, name, provider, pluginPanelName, meta } = const { groupId, panelId, name, provider, pluginPanelName, meta } =
ctx.params; ctx.params;
const { t } = ctx.meta; const { t, userId } = ctx.meta;
const isOwner: boolean = await this.actions['isGroupOwner'](
{ const [hasPermission] = await call(ctx).checkUserPermissions(
groupId, groupId,
}, userId,
{ [PERMISSION.core.managePanel]
parentCtx: ctx,
}
); );
if (!isOwner) { if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限')); throw new NoPermissionError(t('没有操作权限'));
} }
@ -674,16 +681,14 @@ class GroupService extends TcService {
*/ */
async deleteGroupPanel(ctx: TcContext<{ groupId: string; panelId: string }>) { async deleteGroupPanel(ctx: TcContext<{ groupId: string; panelId: string }>) {
const { groupId, panelId } = ctx.params; const { groupId, panelId } = ctx.params;
const { t } = ctx.meta; const { t, userId } = ctx.meta;
const isOwner: boolean = await this.actions['isGroupOwner'](
{ const [hasPermission] = await call(ctx).checkUserPermissions(
groupId, groupId,
}, userId,
{ [PERMISSION.core.managePanel]
parentCtx: ctx,
}
); );
if (!isOwner) { if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限')); throw new NoPermissionError(t('没有操作权限'));
} }
@ -746,19 +751,27 @@ class GroupService extends TcService {
ctx: TcContext<{ groupId: string; roleName: string; permissions: string[] }> ctx: TcContext<{ groupId: string; roleName: string; permissions: string[] }>
) { ) {
const { groupId, roleName, permissions } = ctx.params; const { groupId, roleName, permissions } = ctx.params;
const userId = ctx.meta.userId; const { userId, t } = ctx.meta;
const [hasPermission] = await call(ctx).checkUserPermissions(
groupId,
userId,
[PERMISSION.core.managePanel]
);
if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限'));
}
const group = await this.adapter.model const group = await this.adapter.model
.findOneAndUpdate( .findOneAndUpdate(
{ {
_id: new Types.ObjectId(groupId), _id: new Types.ObjectId(groupId),
owner: new Types.ObjectId(userId),
}, },
{ {
$push: { $push: {
roles: { roles: {
name: roleName, name: roleName,
permissions: [], permissions,
}, },
}, },
}, },
@ -777,13 +790,21 @@ class GroupService extends TcService {
*/ */
async deleteGroupRole(ctx: TcContext<{ groupId: string; roleId: string }>) { async deleteGroupRole(ctx: TcContext<{ groupId: string; roleId: string }>) {
const { groupId, roleId } = ctx.params; const { groupId, roleId } = ctx.params;
const userId = ctx.meta.userId; const { userId, t } = ctx.meta;
const [hasPermission] = await call(ctx).checkUserPermissions(
groupId,
userId,
[PERMISSION.core.managePanel]
);
if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限'));
}
const group = await this.adapter.model const group = await this.adapter.model
.findOneAndUpdate( .findOneAndUpdate(
{ {
_id: new Types.ObjectId(groupId), _id: new Types.ObjectId(groupId),
owner: new Types.ObjectId(userId),
}, },
{ {
$pull: { $pull: {
@ -813,13 +834,21 @@ class GroupService extends TcService {
}> }>
) { ) {
const { groupId, roleId, roleName } = ctx.params; const { groupId, roleId, roleName } = ctx.params;
const userId = ctx.meta.userId; const { userId, t } = ctx.meta;
const [hasPermission] = await call(ctx).checkUserPermissions(
groupId,
userId,
[PERMISSION.core.managePanel]
);
if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限'));
}
const group = await this.adapter.model.updateGroupRoleName( const group = await this.adapter.model.updateGroupRoleName(
groupId, groupId,
roleId, roleId,
roleName, roleName
userId
); );
const json = await this.notifyGroupInfoUpdate(ctx, group); const json = await this.notifyGroupInfoUpdate(ctx, group);
@ -837,13 +866,21 @@ class GroupService extends TcService {
}> }>
) { ) {
const { groupId, roleId, permissions } = ctx.params; const { groupId, roleId, permissions } = ctx.params;
const userId = ctx.meta.userId; const { userId, t } = ctx.meta;
const [hasPermission] = await call(ctx).checkUserPermissions(
groupId,
userId,
[PERMISSION.core.managePanel]
);
if (!hasPermission) {
throw new NoPermissionError(t('没有操作权限'));
}
const group = await this.adapter.model.updateGroupRolePermission( const group = await this.adapter.model.updateGroupRolePermission(
groupId, groupId,
roleId, roleId,
permissions, permissions
userId
); );
const json = await this.notifyGroupInfoUpdate(ctx, group); const json = await this.notifyGroupInfoUpdate(ctx, group);
@ -871,6 +908,7 @@ class GroupService extends TcService {
/** /**
* () * ()
* groupIduserId
*/ */
async getUserAllPermissions( async getUserAllPermissions(
ctx: TcContext<{ ctx: TcContext<{
@ -963,6 +1001,14 @@ class GroupService extends TcService {
private cleanGroupUserPermission(groupId: string, userId: string) { private cleanGroupUserPermission(groupId: string, userId: string) {
this.cleanActionCache('getUserAllPermissions', [groupId, userId]); this.cleanActionCache('getUserAllPermissions', [groupId, userId]);
} }
/**
*
* @param groupId id
*/
private cleanGroupAllUserPermissionCache(groupId: string) {
this.cleanActionCache('getUserAllPermissions', [groupId]);
}
} }
export default GroupService; export default GroupService;

@ -12,6 +12,7 @@ import {
TcDbService, TcDbService,
PureContext, PureContext,
} from 'tailchat-server-sdk'; } from 'tailchat-server-sdk';
import { PERMISSION } from '../../../lib/role';
interface GroupService interface GroupService
extends TcService, extends TcService,
@ -62,21 +63,21 @@ class GroupService extends TcService {
inviteType: 'normal' | 'permanent'; inviteType: 'normal' | 'permanent';
}> }>
): Promise<GroupInvite> { ): Promise<GroupInvite> {
const groupId = ctx.params.groupId; const { groupId, inviteType } = ctx.params;
const inviteType = ctx.params.inviteType; const { userId, t } = ctx.meta;
const userId = ctx.meta.userId;
const t = ctx.meta.t; const [hasNormalPermission, hasUnlimitedPermission] = await call(
ctx
// TODO: 基于RBAC判定群组权限 ).checkUserPermissions(groupId, userId, [
// 先视为仅群组所有者可以创建群组邀请 PERMISSION.core.invite,
const isGroupOwner = await ctx.call<boolean, { groupId: string }>( PERMISSION.core.unlimitedInvite,
'group.isGroupOwner', ]);
{
groupId, if (
} (inviteType === 'normal' && !hasNormalPermission) ||
); (inviteType === 'permanent' && !hasUnlimitedPermission)
if (isGroupOwner !== true) { ) {
throw new NoPermissionError(t('不是群组所有者, 没有分享权限')); throw new NoPermissionError(t('没有创建邀请码权限'));
} }
const invite = await this.adapter.model.createGroupInvite( const invite = await this.adapter.model.createGroupInvite(
@ -96,18 +97,15 @@ class GroupService extends TcService {
}> }>
) { ) {
const groupId = ctx.params.groupId; const groupId = ctx.params.groupId;
const t = ctx.meta.t; const { t, userId } = ctx.meta;
// TODO: 基于RBAC判定群组权限 const [hasPermission] = await call(ctx).checkUserPermissions(
// 先视为仅群组所有者可以创建群组邀请
const isGroupOwner = await ctx.call<boolean, { groupId: string }>(
'group.isGroupOwner',
{
groupId, groupId,
} userId,
[PERMISSION.core.manageInvite]
); );
if (isGroupOwner !== true) { if (!hasPermission) {
throw new NoPermissionError(t('不是群组所有者, 没有查看权限')); throw new NoPermissionError(t('没有查看权限'));
} }
const list = await this.adapter.model.find({ const list = await this.adapter.model.find({
@ -170,18 +168,15 @@ class GroupService extends TcService {
async deleteInvite(ctx: TcContext<{ groupId: string; inviteId: string }>) { async deleteInvite(ctx: TcContext<{ groupId: string; inviteId: string }>) {
const groupId = ctx.params.groupId; const groupId = ctx.params.groupId;
const inviteId = ctx.params.inviteId; const inviteId = ctx.params.inviteId;
const t = ctx.meta.t; const { t, userId } = ctx.meta;
// TODO: 基于RBAC判定群组权限 const [hasPermission] = await call(ctx).checkUserPermissions(
// 先视为仅群组所有者可以创建群组邀请
const isGroupOwner = await ctx.call<boolean, { groupId: string }>(
'group.isGroupOwner',
{
groupId, groupId,
} userId,
[PERMISSION.core.manageInvite]
); );
if (isGroupOwner !== true) { if (!hasPermission) {
throw new NoPermissionError(t('不是群组所有者, 没有查看权限')); throw new NoPermissionError(t('没有删除权限'));
} }
await this.adapter.model.deleteOne({ await this.adapter.model.deleteOne({

Loading…
Cancel
Save