Files
leaudit-platform-frontend/app/utils/permission-mapper.ts
T
TanWenyan 547633bf38 feat: 修复权限键不匹配问题 - 前端权限映射
问题:前端权限列表显示dify:bind:*,但路由实际检查dify:dataset:*和dify:file:*
导致取消勾选后权限控制失效

解决方案:
1. 创建权限映射工具(permission-mapper.ts)
   - dify:bind:list/create/update/delete → dify:dataset:manage
   - 自动将数据库权限键映射为实际生效的权限键

2. 修改角色权限管理页面
   - 加载角色权限时应用权限键映射
   - 渲染权限列表时显示实际生效的权限键
   - 保存权限时使用映射后的权限ID

影响范围:
- 知识库管理权限(/chat-with-llm/dataset-manager)
- 角色权限分配页面(/role-permissions)

验证方式:
取消勾选dify:dataset:manage后,知识库管理接口应返回403
2025-12-08 15:29:31 +08:00

143 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 权限键映射工具
* 用于将数据库中的权限键映射为前端显示的权限键
*
* 问题背景:
* 数据库中的权限键(dify:bind:*)与路由实际检查的权限键(dify:dataset:*, dify:file:*)不一致
* 导致前端取消勾选后,后端仍然检查实际权限,权限控制失效
*/
/**
* 权限键映射表
* key: 数据库中的权限键
* value: 前端显示和实际生效的权限键
*/
const PERMISSION_KEY_MAP: Record<string, string> = {
// 知识库绑定相关 - 映射到数据集管理权限
'dify:bind:list': 'dify:dataset:manage',
'dify:bind:create': 'dify:dataset:manage',
'dify:bind:update': 'dify:dataset:manage',
'dify:bind:delete': 'dify:dataset:manage',
};
/**
* 反向映射表:实际权限键 -> 数据库权限键列表
* 用于检查某个权限是否被正确配置
*/
const REVERSE_PERMISSION_MAP: Record<string, string[]> = {
'dify:dataset:manage': [
'dify:bind:list',
'dify:bind:create',
'dify:bind:update',
'dify:bind:delete',
],
};
/**
* 将数据库权限键转换为显示权限键
* @param permissionKey 数据库中的权限键
* @returns 前端显示的权限键
*
* @example
* mapPermissionKey('dify:bind:list') // 返回 'dify:dataset:manage'
* mapPermissionKey('dify:file:read') // 返回 'dify:file:read'(无映射)
*/
export function mapPermissionKey(permissionKey: string): string {
return PERMISSION_KEY_MAP[permissionKey] || permissionKey;
}
/**
* 批量转换权限键列表
* @param permissionKeys 权限键数组
* @returns 转换后的权限键数组(去重)
*/
export function mapPermissionKeys(permissionKeys: string[]): string[] {
const mappedKeys = permissionKeys.map(key => mapPermissionKey(key));
return [...new Set(mappedKeys)]; // 去重
}
/**
* 反向查找:根据实际权限键找到对应的数据库权限键列表
* @param effectivePermissionKey 实际生效的权限键(如 'dify:dataset:manage'
* @returns 对应的数据库权限键列表
*
* @example
* findDbPermissionKeys('dify:dataset:manage')
* // 返回 ['dify:bind:list', 'dify:bind:create', 'dify:bind:update', 'dify:bind:delete']
*/
export function findDbPermissionKeys(effectivePermissionKey: string): string[] {
return REVERSE_PERMISSION_MAP[effectivePermissionKey] || [effectivePermissionKey];
}
/**
* 转换权限对象:将数据库权限对象转换为显示权限对象
* @param permission 权限对象(来自数据库)
* @returns 转换后的权限对象
*/
export interface Permission {
id: number;
permission_key: string;
display_name: string;
api_method?: string;
api_path?: string;
[key: string]: any;
}
export function mapPermission(permission: Permission): Permission {
const mappedKey = mapPermissionKey(permission.permission_key);
// 如果权限键被映射,更新显示名称(可选)
let displayName = permission.display_name;
if (mappedKey !== permission.permission_key) {
// 根据映射后的权限键更新显示名称
if (mappedKey === 'dify:dataset:manage') {
displayName = '知识库管理(查看、创建、编辑、删除)';
}
}
return {
...permission,
permission_key: mappedKey,
display_name: displayName,
};
}
/**
* 批量转换权限列表
* @param permissions 权限对象数组
* @returns 转换后的权限对象数组(去重)
*/
export function mapPermissions(permissions: Permission[]): Permission[] {
const mappedMap = new Map<string, Permission>();
permissions.forEach(permission => {
const mapped = mapPermission(permission);
// 如果映射后的权限键已存在,合并(保留第一个或根据业务逻辑)
if (!mappedMap.has(mapped.permission_key)) {
mappedMap.set(mapped.permission_key, mapped);
}
});
return Array.from(mappedMap.values());
}
/**
* 检查权限是否受映射影响
* @param permissionKey 权限键
* @returns 是否有映射关系
*/
export function hasPermissionMapping(permissionKey: string): boolean {
return PERMISSION_KEY_MAP[permissionKey] !== undefined;
}
/**
* 获取权限映射的说明信息
* @returns 映射关系的说明文本
*/
export function getPermissionMappingInfo(): string {
return `权限映射说明:\n` +
`- dify:bind:list/create/update/delete → dify:dataset:manage(知识库管理)\n` +
`取消勾选后,对应接口将返回403权限不足错误`;
}