
ToolJet 如何用 RBAC 管好多个团队从三种默认角色到细粒度资源授权【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet多团队共用一个实例时自建部署先要解决权限边界自建的 ToolJet 实例往往要服务全公司开发写应用、运营看看板、财务查报表。大家全塞进默认角色结果是财务能看到在研应用、开发能摸到生产数据源。ToolJet 的 RBAC 权限模型分两层开关——组级开关和细粒度资源授权配好权限的关键是分清每层能控制什么、哪些底线由系统强制。三种内置角色的权限边界先决策选谁再谈要不要建自定义组先给结论新用户进工作空间按四个问题对号入座——要管理工作空间设置与用户权限的给 Admin人数要克制要写应用、工作流的给 Builder只用已发布应用的给 End-user只有当两个团队需要不同的资源边界比如财务只碰财务应用时才建自定义组而不是把默认角色硬掰成第四种角色。这三个角色与两种组类型在group-permissions模块的 constants/index.ts 中被固化export enum USER_ROLE { END_USER end-user, ADMIN admin, BUILDER builder, } export enum GROUP_PERMISSIONS_TYPE { DEFAULT default, CUSTOM_GROUP custom, }这段代码说明了角色名是系统枚举而非自由文本每个组织初始化即拥有三个 default 组custom 组则由 Admin 自由命名并独立配权两类组共用同一套存储结构。三种角色在「组级权限位」上的差异是出厂时写死的默认值权限位含义AdminBuilderEnd-userappCreate / appDelete创建/删除应用✅✅❌folderCreate / folderDelete创建/删除文件夹✅✅❌workflowCreate / workflowDelete创建/删除工作流✅✅❌moduleCreate / moduleDelete创建/删除模块✅✅❌orgConstantCRUD工作空间常量增删改✅✅❌tjdbCRUD内置数据库ToolJet Database操作✅✅❌dataSourceCreate / dataSourceDelete创建/删除数据源✅✅❌appPromote / appRelease应用晋级/发布✅✅❌isBuilderLevel是否为构建级组✅✅❌从表里能看出一个容易误判的点Builder 与 Admin 在这一层完全等价连多环境流程位的appPromote/appRelease都开着真正区分两者的不是这些开关而是下一章的资源级授权End-user 则全部关闭退化成纯消费者角色。支撑这套开关的数据模型很直接group_permissions.entity.ts 里的permission_groups表用organization_id绑定组织type区分 default/custom成员挂在GroupUsers关联表上删组级联删人细粒度授权则通过一对多关联groupGranularPermissions挂载另外还保留PageUser、QueryUser、ComponentUser三个关联——页面、查询、组件级别的访问同样以组为授权单位。三个默认角色的产品化描述见官方文档 user-roles.md自定义组的操作细节见 custom-groups.md。这一层开关只回答「能不能做某类事」一旦要回答「能碰哪几个具体应用、哪几个数据源」就需要第二层。细粒度权限的存储结构isAll 标志与级联到资源表组级开关太粗两位 Builder 都能建应用但 A 团队的 Builder 不该看到 B 团队的应用。这个「精确到资源」的需求由granular_permissions表承担核心实体见 granular_permissions.entity.tsEntity({ name: granular_permissions }) export class GranularPermissions extends BaseEntity { PrimaryGeneratedColumn(uuid) id: string; Column({ name: group_id }) groupId: string; Column({ name: name, nullable: false }) name: string; Column({ name: type, nullable: false, type: enum, enum: ResourceType }) type: ResourceType; Column({ name: is_all, nullable: false, default: true }) isAll: boolean; OneToOne(() AppsGroupPermissions, { onDelete: CASCADE }) appsGroupPermissions: AppsGroupPermissions; OneToOne(() DataSourcesGroupPermissions, { onDelete: CASCADE }) dataSourcesGroupPermission: DataSourcesGroupPermissions; OneToOne(() FoldersGroupPermissions, { onDelete: CASCADE }) foldersGroupPermissions: FoldersGroupPermissions; }这个实体值得逐字段读type声明授权针对哪类资源取值是app、data_source、workflow、folder、module、workflow_folder、module_folder共七个值三种文件夹类型统一复用文件夹授权表isAll是默认值true的开关——为true时授权覆盖该类资源全集创建逻辑会主动清空资源枚举列表为false时才通过GroupApps/GroupFolders这类中间表逐个绑定具体资源 ID。三个OneToOne关系全部带onDelete: CASCADE意味着删掉一条授权记录对应的应用/数据源/文件夹动作行自动清理不会出现孤儿授权。每个默认角色在应用资源上的默认授权同样写在常量里注意canEdit与canView互斥编辑隐含查看所以编辑方只留一个 ✅角色canEditcanViewDevelopmentStagingProductionReleasedAdmin✅—✅✅✅✅Builder✅—✅✅❌✅End-user❌✅❌❌❌✅这张表暴露了 Builder 与 Admin 在资源层的第一个真实差异Builder 默认进不了生产环境而 End-user 的出厂边界是「只看 Released 版本」。界面上与存储的对应关系也直观Workspace settings 的 Groups 页里每个自定义组有 Users / Permissions / Granular access 三个页签后一个页签里的 Apps、Data source 入口就是一次次向granular_permissions表写入授权。访问控制的全景说明在官方 access-control.md 中。结构看着自由但每个写入路径上都有同一套硬性规则在守着。安全护栏写入授权时系统替你守住的四条红线无论前端怎么操作后端 granular-permissions.util.service.ts 在落库前强制执行四条规则Admin 默认组不可配细粒度权限。创建和更新两个入口都先校验组名validateGranularPermissionCreateOperation(group: GroupPermissions) { if (group.name USER_ROLE.ADMIN) { throw new BadRequestException(ERROR_HANDLER.ADMIN_DEFAULT_GROUP_GRANULAR_PERMISSIONS); } } protected validateAppResourcePermissionUpdateOperation( group: GroupPermissions, actions, isModule false ) { // Modules are never assignable to end-users — reject for Build-with (canView) too, not just Edit. if (group.name USER_ROLE.END_USER (actions.canEdit || isModule)) { throw new BadRequestException(ERROR_HANDLER.EDITOR_LEVEL_PERMISSION_NOT_ALLOWED_END_USER); } }前一段说明 Admin 组的权限是系统全量约定走细粒度接口修改会直接 400避免误操作让管理员失权后一段是 End-user 硬边界的应用/工作流分支canEdit为真即拒绝模块Module更严格连canView也拒绝——源码注释写得很清楚模块永远不会分配给 end-user。⚠️End-user 的构建级权限按资源类型逐类封死。应用/工作流拒绝canEdit数据源拒绝canConfigure与canUse任一为真文件夹拒绝canEditFolder/canEditAppsmodule_folder 则任何授权都拒绝。拒绝时抛出的不是裸错误而是带type: USER_ROLE_CHANGE_ADD_PERMISSIONS的结构化响应data里附上组内所有 end-user 的邮箱管理员能直接定位该把谁调出组。多环境访问受许可证门控。给组授予canAccessDevelopment/canAccessStaging/canAccessProduction时服务会先查组织是否持有MULTI_ENVIRONMENT许可证条款licenseTermsService.getLicenseTerms未持有时落入上面的 end-user 拒绝逻辑。这意味着环境隔离能力本身是许可证特性自建基础版实例上这一刀是砍不下来的。可选的角色自动升级。更新授权若携带allowRoleChange: true校验逻辑在确认这是构建级变更且组内存在 end-user 时调用changeEndUserToEditor把这些 end-user 批量升级为 builder否则抛 405USER_ROLE_CHANGE类型。前端「改组权限弹出角色变更确认」的交互来源就在这一条。这四条规则合起来的含义是落库的每条授权都必然合法安全不依赖管理员的自觉而依赖写入路径上的强制校验。为财务团队配最小权限从建组到改角色的完整操作路径目标场景财务成员只能查看指定财务应用能管理工作空间常量不碰任何数据源配置。第一步建组。点击工作台左下角设置图标 → Workspace settings Groups → Create new group输入组名名称唯一、最长 50 字符后端create方法落库后会写入GROUP_PERMISSION_CREATE审计日志若已有结构相似的组也可以对旧组用 Duplicate按需勾选是否带走权限位、成员、应用级授权复制完成后还会调许可证服务校验组织用户配额。第二步配权限。在 Permissions 页签打开组级开关如orgConstantCRUD到 Granular access 页签新增一条 Apps 类型授权isAll关闭指定财务应用 IDcanView打开、canEdit关闭环境位保持全关——只开放 Released 就够财务用了。第三步加人。在 Users 页签把财务同事批量加入后端在事务内写入GroupUsers关联并记录USER_ADD_TO_GROUP审计事件同样带许可证校验。第四步验证。用财务账号登录工作台应只显示指定的财务应用尝试进入数据源配置页应被拒。至此最小权限闭环成立。日常运维中最高频的动作是改角色某位财务同事转岗做实施需要构建能力不必重建组——在 Workspace settings Users 找到该行点击行尾 kebab⋮→ Edit user details在右侧 User groups 下拉里更新组点 Update 后阅读弹窗警告并 Continue 即可。角色变更后权限按继承规则重算多组成员取最高权限把用户加入更高权限的组会自动升级其角色降级时则自动移除超出新角色权限的自定义组。从「谁能建应用」到「谁能看哪个应用、在哪个环境看」ToolJet 把多团队实例的权限问题收敛成三层可审计、可级联、可被许可证门控的数据结构——自建实例能安全承载全公司内网工具靠的就是这套边界。【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考