首页
/ FastGPT 新资源权限接入实施指南:从权限位定义、鉴权函数到协作者管理与 Owner 转移的完整落地清单

FastGPT 新资源权限接入实施指南:从权限位定义、鉴权函数到协作者管理与 Owner 转移的完整落地清单

2026-09-09 18:04:43作者:尤辰城Agatha

本文以 FastGPT 仓库内《权限接入实施清单》(.agents/skills/support/permission/add-permission/checklist.md)为核心骨架,系统讲解如何为一个新资源类型(如 App、知识库 Dataset、技能 Skill)接入 FastGPT 的细粒度权限体系。文章涵盖设计判断、权限定义、资源 Schema、鉴权函数、API 权限校验、继承逻辑、协作者管理、Owner 转移、审计日志、前端接入与测试验证全流程,并结合 packages/globalpackages/service 中的源码实现逐项印证,帮助开发者在开发阶段就对照核对,避免上线后出现越权、误删或继承错乱等问题。

一、权限系统核心设计:位域(Bit Field)与角色映射

FastGPT 的权限模型受 Linux 文件权限启发,采用**位域(bit field)**表示权限值。在 packages/global/support/permission/type.ts 的类型注释中有明确说明:

PermissionValueType is a number, which is a bit field actually. The lowest 3 bits present the permission of reading, writing and managing. The higher bits are advanced permissions or extended permissions, which could be customized.

通用权限位定义在 packages/global/support/permission/constant.ts

export const CommonPerList: PermissionListType = {
  [CommonPerKeyEnum.owner]: OwnerRoleVal,   // ~0 >>> 0,即 0xFFFFFFFF
  [CommonPerKeyEnum.read]: 0b100,           // 第 3 位:读
  [CommonPerKeyEnum.write]: 0b010,          // 第 2 位:写
  [CommonPerKeyEnum.manage]: 0b001          // 第 1 位:管理
} as const;

对应的角色(Role)每个也是单一位的二进制数,而**角色到权限的映射(RolePerMap)**负责把角色值展开为实际权限位集合:

export const CommonRolePerMap: RolePerMapType = new Map([
  [CommonRoleList['read'].value, CommonPerList.read],                              // read  → read
  [CommonRoleList['write'].value, sumPer(CommonPerList.write, CommonPerList.read)], // write → write + read
  [CommonRoleList['manage'].value,
   sumPer(CommonPerList.manage, CommonPerList.write, CommonPerList.read)]           // manage → manage + write + read
]);

这里的关键点在于:

  • read(0b100)、write(0b010)、manage(0b001)按位包含,与角色值一一对应;
  • Owner 是特殊值OwnerRoleVal = OwnerPermissionVal = ~0 >>> 0(全 1),不是普通权限位,因此 checkPer/checkRole 中需要对 Owner 做全等判断(见 controller.ts);
  • 高 4 位起为资源扩展权限位,例如应用资源自定义了 readChatLog: 0b1000(见下文示例),可按需扩展而不影响通用低 3 位。

Permission 工具类(packages/global/support/permission/controller.ts)是权限判断的核心实现,它接收 roleisOwnerroleListperListrolePerMap,内部通过 calculatePer() 的"Binary Magic"循环把角色位逐步拆解为权限位:

private calculatePer() {
  if (this.role === OwnerRoleVal) {
    this.permission = OwnerPermissionVal;
    return;
  }
  let role = this.role;
  this.permission = 0;
  while (role > 0) {
    // Binary Magic
    this.permission |= this.rolePerMap.get(role & -role) ?? 0;
    role &= role - 1;
  }
}

role & -role 取出最低的置位位,role &= role - 1 清除该位,循环累积出最终权限。实例化后会同步计算出 hasManagePer / hasWritePer / hasReadPerhasManageRole / hasWriteRole / hasReadRole 等便捷布尔字段,供 API 层直接判断。

接入新资源的第一步,就是理解并复用这套位域模型:新资源的权限定义必须基于 CommonPerList / CommonRoleList 扩展,而不是另起炉灶。

二、设计判断:接入前先回答的六个问题

清单开篇要求开发者在上线前完成一组设计判断,这决定了后续所有实现路径:

判断项 含义 影响
资源是否有 owner 是否存在资源所有者(tmbId 决定是否需要 Owner 转移逻辑、删除是否要求 Owner 权限
资源是否属于 team 是否挂靠在团队下 决定是否需要 teamId 归属校验与团队级权限
资源是否需要协作者 是否允许多人对同一资源协作 决定是否接入 collaborator/listcollaborator/update
是否有 folder / parent-child 结构 是否存在目录/父子层级 决定是否引入 parentId、子树权限同步
是否支持 inheritPermission 子资源是否继承父级权限 决定是否实现继承合并与恢复继承逻辑
是否需要 owner 转移 资源能否更换所有者 决定是否实现 changeOwner 接口与转移日志

这些问题的答案直接映射到第三~八节的实现清单。确认资源是否有 owner、是否属于 team 是必选项,其余按资源形态按需勾选。

三、权限定义落地:枚举、常量文件与 Permission 子类

清单在"FastGPT 主仓库 → 权限定义"中要求三件事,逐一说明:

3.1 在 PerResourceTypeEnum 中注册新资源类型

枚举定义在 packages/global/support/permission/constant.ts

export enum PerResourceTypeEnum {
  team = 'team',
  app = 'app',
  dataset = 'dataset',
  model = 'model',
  agentSkill = 'agentSkill'
}

新增资源类型时需在此追加一个枚举成员(例如 xxx = 'xxx'),该值会被持久化到权限记录中,用于区分 ResourcePermissionType(见 type.ts 中的 resourceType 字段)。

3.2 创建 {resource}/constant.ts,声明四项内容

清单要求在 packages/global/support/permission/{resource}/constant.ts 中定义:

  • {Resource}RoleList:角色列表,继承自 CommonRoleList,可覆盖名称与描述;
  • {Resource}RolePerMap:角色→权限映射,通常以 CommonRolePerMap 为基础追加扩展;
  • {Resource}PerList:权限位列表,{ ...CommonPerList, 扩展位 }
  • {Resource}DefaultRoleVal:默认角色值,一般取 NullRoleVal(0)。

以知识库为例,packages/global/support/permission/dataset/constant.ts 是最简形态——直接复用通用三件套:

export const DatasetRoleList = { read: {...}, write: {...}, manage: {...} };
export const DatasetRolePerMap: RolePerMapType = CommonRolePerMap;
export const DatasetPerList = CommonPerList;
export const DataSetDefaultRoleVal = NullRoleVal;

以应用(App)为例,packages/global/support/permission/app/constant.ts 展示了扩展权限位的写法——App 额外定义了 readChatLog(查看聊天日志)这一高阶权限,占用第 4 位:

export enum AppPermissionKeyEnum {
  ReadChatLog = 'readChatLog'
}
export const AppPerList: PermissionListType<AppPermissionKeyEnum> = {
  ...CommonPerList,
  readChatLog: 0b1000
};
export const AppRoleList: RoleListType<AppPermissionKeyEnum> = {
  [CommonPerKeyEnum.read]: { ...CommonRoleList[CommonPerKeyEnum.read], name: ..., description: ... },
  ...
  [AppPermissionKeyEnum.ReadChatLog]: { value: 0b1000, checkBoxType: 'multiple', name: ..., description: '' }
};
export const AppRolePerMap: RolePerMapType = new Map([
  ...CommonRolePerMap,
  [CommonRoleList.manage.value, sumPer(read, write, manage, readChatLog)!], // 管理员自动含读聊天日志
  [AppRoleList.ReadChatLog.value, sumPer(read, readChatLog)!]               // 只读+读聊天日志
]);
export const AppDefaultRoleVal = NullRoleVal;

注意 checkBoxType'single' | 'multiple' | 'hidden' 三种(type.ts):通用低 3 位角色是单选,高阶扩展权限通常为 'multiple' 多选,'hidden' 用于不可见角色。

3.3 创建 {resource}/controller.ts,实现 {Resource}Permission 子类

{Resource}Permission 继承通用 Permission 类,注入该资源的角色/权限定义,并可注册权限更新回调。以 packages/global/support/permission/app/controller.ts 为例:

export class AppPermission extends Permission {
  hasReadChatLogPer: boolean = false;
  hasReadChatLogRole: boolean = false;
  constructor(props?: PerConstructPros) {
    if (!props) { props = { role: AppDefaultRoleVal }; }
    else if (!props?.role) { props.role = AppDefaultRoleVal; }
    props.roleList = AppRoleList;
    props.rolePerMap = AppRolePerMap;
    props.perList = AppPerList;
    super(props);
    this.setUpdatePermissionCallback(() => {
      this.hasReadChatLogPer = this.checkPer(AppPerList.readChatLog);
      this.hasReadChatLogRole = this.checkRole(AppRoleList.readChatLog.value);
    });
  }
}

这样在鉴权层实例化 new AppPermission({ role, isOwner }) 后,即可通过 Per.hasReadPer / Per.hasWritePer / Per.hasManagePer 等布尔属性统一判断。同时 controller.ts 提供了 PermissionSchema(zod),用于 OpenAPI 文档声明权限对象序列化后的 JSON 字段(roleisOwnerhas*Perhas*Role),前端实际依赖这些字段。

四、资源 Schema:四个关键字段

清单要求资源模型(Mongo Schema)按需包含:

字段 类型 用途 必需性
teamId string 资源所属团队,鉴权时校验归属(String(app.teamId) !== teamId 即拒绝) 必需
tmbId string 资源 owner(团队成员),判断 `isOwner = tmbPer.isOwner
parentId string 父资源 ID,用于 folder / parent-child 层级与继承 有层级时必需
inheritPermission boolean 是否允许从父级继承协作者权限 支持继承时必需

packages/service/support/permission/app/auth.ts 中可以看到 teamId 归属与 tmbId owner 判断的实际用法:团队不匹配直接拒绝,isOwner 由"团队所有者"或"资源创建者本人"共同决定。

五、鉴权函数 auth{Resource}:统一入口与父级权限合并

清单要求创建 packages/service/support/permission/{resource}/auth.ts,实现 auth{Resource} 函数。仓库中已有 App、Dataset、Skill、MCP、Evaluation、User 等实现(见 packages/service/support/permission/app/auth.tspackages/service/support/permission/dataset/auth.tspackages/service/support/permission/skill/auth.ts 等)。

authAppByTmbIdauth.ts)为例,其核心流程是:

  1. 通过 getTmbInfoByTmbId 获取调用者的 teamIdtmbPer(团队内成员权限);
  2. 加载资源,校验存在性与 teamId 归属;
  3. 处理 hidden 类型资源的特殊策略(仅读权限或读聊天日志权限);
  4. 继承合并:若资源支持继承(shouldInheritResourcePermission(app.inheritPermission))且不是 folder 本身、且存在 parentId,则并行获取父级权限 folderPer自身权限 myPer,用 sumPer(folderPer, myPer) 合并后构造 AppPermission
    const Per = new AppPermission({ role: sumPer(folderPer, myPer), isOwner });
    
  5. 收藏/快捷资源自动附加读角色;最后校验目标权限位是否满足。

这就是清单中"如有继承,已实现父级权限合并"的源码落点——子资源的最终生效权限 = 父级权限 ∪ 自身显式权限

sumPer 的实现细节(utils.ts)值得一提:空参数返回 undefined(以便回退到默认值),结果溢出(res < 0)时返回 OwnerRoleVal

六、API 权限校验:Read / Write / Owner 的边界

清单对各类接口的权限要求是硬性规范,整理为矩阵:

接口类型 校验权限值 说明
列表接口 ReadPermissionVal(0b100) 能读到资源即可出现在列表
详情接口 ReadPermissionVal 同上
创建接口 WritePermissionVal 或 team 级创建权限 新资源创建者自动成为 owner
更新接口 WritePermissionVal 注意:写权限即可更新,不需要 Manage
删除接口 OwnerPermissionVal(不是 Manage!) 最易出错:删除是 Owner 专属动作
Folder 创建接口(如有) 按父级权限 父目录有写入权限才能建子资源
恢复继承接口(如有) 按资源类型 通常要求 Write/Manage 级

其中"删除要求 owner,而不是 manage"被清单在"最终检查"一节再次强调,这是 FastGPT 权限模型的一个重要设计决策:销毁资源是高风险动作,仅资源所有者可执行。实现时 checkPer(OwnerPermissionVal) 走的是全等分支(controller.ts),即只有 permission === OwnerPermissionVal 才为 true,普通角色叠加永远无法达到。

ReadPermissionVal / WritePermissionVal / ManagePermissionVal 三个导出常量定义在 constant.ts,鉴权函数按 per 参数接收它们,例如 authAppByTmbId({ tmbId, appId, per: ReadPermissionVal })

七、继承(inheritPermission):folder 类型、复制、移动与恢复

如果资源支持继承,清单要求实现四件事:

  1. 明确 folder 类型列表:区分目录型资源与叶子资源。App 侧通过 AppFolderTypeList 判断——shouldInheritResourcePermission(app.inheritPermission) && !AppFolderTypeList.includes(app.type) && !!app.parentId 才做父级合并(见 auth.ts),folder 本身不向上继承。
  2. 资源创建时复制父协作者:新子资源继承父级的协作者配置作为初始状态。
  3. 资源移动时同步子树权限:移动资源到新的父目录后,需要重新计算整棵子树的生效权限。
  4. resumeInheritPermission 逻辑:恢复继承时,需基于当前生效协作者(父级合并后的快照)生成显式协作者记录,即清单"最终检查"中的"继承断开后生成正确的显式协作者快照"。

packages/global/support/permission/utils.ts 中还有两个与继承/协作者强相关的工具函数:

  • mergeCollaboratorList({ parentClbs, childClbs }):将父级协作者与子资源自身协作者按协作者 ID 合并,同一协作者在两处出现时做权限位按位或(sumPer);父级为 Owner 的协作者在子级降级为 Manage(permission: ManageRoleVal),避免全 1 权限污染子树。
  • isPrivateResourceByCollaborators(...):判断资源在给定协作者集合下是否仍为私有(realClbs.length <= 1),继承型资源会先合并父级与自身协作者,避免重复计数。
  • checkRoleUpdateConflict({ parentClbs, newChildClbs }):检测协作者更新是否与父级产生冲突(见第八节)。

八、fastgpt-pro(企业版):协作者管理、Owner 转移与审计

清单第二部分面向 fastgpt-pro 仓库(本开源仓库的 pro 目录对应独立的企业版内容)。其核心是**协作者(Collaborator)**体系:

8.1 协作者类型与数据结构

协作者 ID 是"三选一"的联合类型(packages/global/support/permission/collaborator.ts):

export type CollaboratorIdType = RequireOnlyOne<{
  tmbId: string;    // 团队成员
  groupId: string;  // 成员组
  orgId: string;    // 组织
}>;

getCollaboratorIdutils.ts)按 tmbId || groupId || orgId 取出唯一标识。协作者列表接口返回的 CollaboratorListType 包含两类数据(collaborator.ts):

  • clbs:最终生效协作者(子资源自身 + 继承合并后的结果);
  • parentClbs:父级协作者(用于前端展示"来自父级"的继承态提示)。

8.2 collaborator/listcollaborator/update

  • collaborator/list:返回 clbs(最终生效协作者)与 parentClbs(父级协作者),前端据此展示最终权限与继承来源。
  • collaborator/update:更新协作者,清单列出的校验规则:
    • 需要 ManagePermissionVal(管理权限才能管理协作者,比更新资源本身的 WritePermissionVal 更严格);
    • 不能修改自己的权限(防自抬权限);
    • 非 owner 不能修改管理员权限(保护管理角色边界);
    • 继承冲突时自动断开继承:当更新的协作者同时也是父级协作者、且修改会与父级权限冲突时,需断开 inheritPermission,转为显式协作者快照。对应工具函数即 checkRoleUpdateConflictutils.ts),其判定逻辑是:遍历发生变化的协作者(getChangedCollaborators 输出),若某协作者同时存在于父级且其变更位与父级权限有交集((changedClb.changedRole & parent.permission) !== 0)或被删除,则判定为冲突。

8.3 Owner 转移(changeOwner

清单要求(如适用)实现 changeOwner 接口,且:

  • 需要 OwnerPermissionVal(只有现任 owner 能发起转移);
  • 更新资源表的 tmbId 为新 owner;
  • 根资源断开继承(转移后资源脱离父级继承关系,避免权限来源混乱);
  • 修正权限记录(旧 owner 的权限记录与新 owner 的权限记录需同步修正)。

清单"最终检查"中对应的验证点是:"Owner 转移后旧/新 owner 权限记录正确"——转移后旧 owner 不应再保留 Owner 级权限,新 owner 应获得完整 Owner 权限。

8.4 审计日志

企业版要求为以下动作记录审计日志:更新协作者、删除协作者、Owner 转移、恢复继承(如有)、移动资源(如有)。这些日志是权限问题排查与合规审计的基础。

九、前端接入:组件、API 与继承态提示

清单对前端的要求如下:

项目 说明
协作者列表 API 调用 对接 collaborator/list,渲染 clbsparentClbs
协作者更新 API 调用 对接 collaborator/update,处理冲突回退
Owner 转移 API 调用(如有) 对接 changeOwner,转移前二次确认
权限配置弹窗 / 协作者管理组件 复用或实现资源权限配置 UI
继承态提示 UI(如有) 展示"权限来自父级"的标识,区分显式协作者与继承协作者
恢复继承入口(如有) 提供断开继承后的恢复操作入口

前端展示的"最终权限"必须与后端实际鉴权一致(清单"最终检查"最后一条),这也是 PermissionSchema 序列化字段存在的意义——前后端共用同一套 hasReadPer / hasWritePer / hasManagePer / isOwner 语义。

十、测试:单元测试与集成测试

清单对测试提出了明确要求,分为两层:

10.1 单元测试

  • Permission 类与角色映射:验证 RolePerMap 展开正确,如 read 角色 → 仅 read 权限、write 角色 → write+read、manage 角色 → 全低 3 位;
  • getTmbPermission 优先级逻辑:验证团队内成员默认权限、资源显式权限的合并优先级;
  • 继承型资源的父子权限合并:验证 sumPer(folderPer, myPer) 的结果与 mergeCollaboratorList 的位或合并行为(含父级 Owner 降级为 Manage 的特殊分支)。

10.2 集成测试

  • 主要 API 的权限边界(读/写/管理分别能访问哪些接口);
  • 删除是否要求 owner(而非 manage);
  • 移动与继承恢复逻辑;
  • 协作者更新冲突处理(继承冲突自动断开);
  • Owner 转移后权限记录正确性。

仓库的测试基建位于 packages/service/testpackages/global/testtest,采用 vitest,可通过 pnpm vitest 系列命令运行(见根目录 package.json 与各包的 vitest.config.ts)。

十一、最终检查(上线前核对)

清单末尾的最终检查项是上线前的验收清单,每一项都对应一个易错点:

  • 删除要求 owner,而不是 manage:务必在删除接口使用 OwnerPermissionVal,并编写对应集成测试锁定行为;
  • group / org 协作者按预期生效:验证 groupIdorgId 两类协作者与 tmbId 一样参与权限合并与展示;
  • 继承断开后生成正确的显式协作者快照:断开继承时基于当前生效协作者固化快照,避免权限丢失;
  • 移动资源后子树权限同步:父目录变更后子树内所有资源的生效权限即时更新;
  • Owner 转移后旧/新 owner 权限记录正确:旧 owner 不再持有 Owner 权限,新 owner 完整接管;
  • 前后端展示的"最终权限"与后端实际鉴权一致:以 clbs 为准,前端仅作展示,杜绝"显示有权限、实际无权限"或反向的界面误导。

十二、接入新资源的推荐实施顺序

综合清单与源码,一个典型的新资源权限接入流程可归纳为:

  1. 设计判断:回答六个问题,明确 owner / team / 协作者 / 层级 / 继承 / 转移的需求范围;
  2. 权限定义:注册 PerResourceTypeEnum,创建 constant.ts(RoleList、RolePerMap、PerList、DefaultRoleVal)与 controller.ts(Permission 子类);
  3. 资源 Schema:补齐 teamIdtmbIdparentIdinheritPermission 字段;
  4. 鉴权函数:实现 auth{Resource},含继承场景的父级权限合并;
  5. API 接入:按"读/写/Owner"矩阵为各接口挂接鉴权;
  6. 继承逻辑(如适用):folder 列表、创建复制、移动同步、resumeInheritPermission
  7. 协作者与企业版能力(fastgpt-pro):collaborator/listcollaborator/updatechangeOwner、审计日志;
  8. 前端:协作者组件、继承态提示、恢复继承入口;
  9. 测试与验收:单元 + 集成测试覆盖权限边界,对照最终检查清单逐项核销后上线。

该清单本身可直接打印作为上线核对卡(checklist.md 开头注明"上线前核对清单,可打印使用"),配合本仓库的源码(权限核心权限常量鉴权实现)即可完成一次完整、可验证的资源权限接入。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.89 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
602
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
526