ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Wekan 看板角色权限体系完全指南:九种角色能力矩阵、服务端强制规则与源码实现

Wekan 看板角色权限体系完全指南:九种角色能力矩阵、服务端强制规则与源码实现 Wekan 看板角色权限体系完全指南九种角色能力矩阵、服务端强制规则与源码实现【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan导读Wekan基于 Meteor 构建的开源看板把这个成员能在看板上做什么建模为九种互斥的看板角色board roles并把这九种角色的能力清单收敛到一份纯函数能力表中由服务端、客户端 UI、管理面板与文档四方共同读取杜绝规则漂移。本文以 docs/Features/Members/Roles.md 为主线深入 models/lib/boardRoleCapabilities.js、models/lib/workerCardWrite.js 等源码讲清每一列权限从哪个 allow 规则、哪条发布游标而来并给出 Web UI、REST API 与 LDAP/OIDC 三种设角色方式的可执行示例。读完你将能准确回答Worker 到底能不能改卡片标题评论受限成员能看到哪些卡REST API 为何能造出 UI 造不出的角色组合这类问题。一、角色模型一个成员恰好一个角色八个布尔标志Wekan 的看板成员模型里角色的落点不是字符串而是成员文档member上的八个布尔标志角色成员标志Board adminisAdminNormal无任何标志Normal, assigned onlyisNormalAssignedOnlyNo commentsisNoCommentsComment onlyisCommentOnlyComment only, assignedisCommentAssignedOnlyWorkerisWorkerRead onlyisReadOnlyRead only, assignedisReadAssignedOnly这八个标志定义在看板 schema 中见 models/boards.jsmembers.$.isAdmin、members.$.isNoComments、members.$.isCommentOnly、members.$.isWorker、members.$.isNormalAssignedOnly、members.$.isCommentAssignedOnly、members.$.isReadOnly、members.$.isReadAssignedOnly。每次设置角色时setMemberPermission会把这八个标志一次性全部写回成员文档因此正常路径下恰好一个为true、其余全为false——Normal就是八个标志全为 false。看板管理员 ≠ 站点管理员。user.isAdmin是实例级 Admin Panel 管理员与看板上的board-admin角色是两回事。站点管理员对自己不是成员的看板没有任何特殊权限。角色优先级当成员列表按角色排序展示时models/boards.js 的activeMembers()优先级从高到低为 admin(0) → normal(1) → normal-assigned-only(2) → no-comments(3) → comment-only(4) → comment-assigned-only(5) → worker(6) → read-only(7) → read-assigned-only(8)同级再按姓名排序同一 userId 出现多条成员记录时优先取isAdmin的那条models/boards.js。二、核心九角色 × 五能力的能力矩阵models/lib/boardRoleCapabilities.js 是权限表本身——一份纯函数模块不依赖 Meteor、DOM 或数据库五个能力维度如下seesAllCards能否看到看板上所有卡片false 只能看到指派给自己的卡片由卡片发布而非 allow 规则强制comment能否添加卡片评论与评论表情反应write能否创建/编辑卡片、列表、泳道、清单——以及移动卡片因为移动本质是卡片 updatemanageBoard能否管理看板设置、成员及其角色moveCard能否在列表/泳道间移动卡片、把自己的名字加入或移出assignees对拥有write的角色它是write的子集对 Worker 则是其对卡片能力的全部见 #3189 与 models/lib/workerCardWrite.js。完整矩阵ROLE_CAPABILITIESboardRoleCapabilities.js角色seesAllCardscommentwritemanageBoardmoveCardboard-admin✅✅✅✅✅normal✅✅✅❌✅normal-assigned-only❌✅✅❌✅no-comments✅❌✅❌✅comment-only✅✅❌❌❌comment-assigned-only❌✅❌❌❌worker✅✅❌❌✅read-only✅❌❌❌❌read-assigned-only❌❌❌❌❌模块导出的三个判定函数boardRoleCapabilities.jsmemberRoleOf(member) // 一个成员文档 → 角色isAdmin 优先未知标志组合取最强 roleCan(role, capability) // 角色 × 能力 → 布尔未知角色/未知能力一律 false权限问题没有答案就是不允许 memberCan(members, userId, capability) // 某个用户在某块看板上能否做某事其中memberRoleOf()的解析顺序很关键先看isAdmin再看其余七个标志boardRoleCapabilities.js因此管理员身上还带着别的标志时管理员身份永远优先——这正是已修复章节第 4 项缺陷的修复方式。三、能力表的四方消费者一份表四个读取方过去这套规则被拼写成三份互不相干的标志清单服务端 allow 助手、客户端canModify*助手、文档正文三份清单逐渐漂移每一处分歧都对应一个名不副实的角色。现在的设计是表只有一份其余全部读取它没有出现第四种意见的余地源码注释见 boardRoleCapabilities.js。3.1 服务端唯一的裁决权威服务端是两个 allow 助手的唯一来源server/lib/utils.jsexport function allowIsBoardMemberCommentOnly(userId, board) { return !!(board memberCan(board.members, userId, comment)); } export function allowIsBoardMemberWithWriteAccess(userId, board) { return !!(board memberCan(board.members, userId, write)); }allowIsBoardMemberWithWriteAccesswrite能力把关卡片server/permissions/cards.js 的Cards.allow、列表、泳道、清单、清单项以及跨看板移动守卫如 server/lib/utils.js 的denyCrossBoardMoveallowIsBoardMemberCommentOnlycomment能力把关卡片评论与评论表情反应server/permissions/cardComments.js、server/permissions/cardCommentReactions.js。3.2 客户端UI 只负责要不要亮出按钮客户端 client/lib/utils.js 通过currentUserCan()统一走memberCan(board.members, userId, capability)currentUserCan(capability, board Utils.getCurrentBoard()) { const userId Meteor.userId(); return !!(board memberCan(board.members, userId, capability)); }, canModifyCard(card) { return Utils.currentUserCan(write, board); }, canMoveCard() { return Utils.currentUserCan(moveCard); }, canModifyBoard() { return Utils.currentUserCan(write); },设计原则是按钮在服务端会接受时才显示canModifyCard/canModifyBoard问writecanMoveCard问moveCard#3189 之后移动与编辑分离成两个问题。每个角色只要write为真就同时拥有moveCard所以这一拆分实际只为 Worker 一个角色扩宽了答案。3.3 Admin PanelRoles Status 只读面板Admin Panel → People → Roles 的 Save 按钮下方有一个只读的Roles Status面板它直接require(/models/lib/boardRoleCapabilities)并BOARD_ROLES.map(...)...ROLE_CAPABILITIES[roleKey]渲染client/components/settings/peopleBody.js。因此页面不可能展示一个代码未强制执行的权限——面板本身就是那张表。3.4 文档由测试强制与代码保持一致models/lib/boardRoleCapabilities.js 的注释明确列出第四个读取方就是本文档经由 tests/boardRoles.test.cjs 连接详见第七节。四、逐列拆解每一列权限的强制路径4.1 能看到哪些卡片——由发布游标强制这是唯一不由 allow 规则、而由卡片发布强制的能力。对带有isNormalAssignedOnly、isCommentAssignedOnly、isReadAssignedOnly任一标志的成员卡片游标被收窄为assignees: { $in: [userId] }急加载server/publications/boards.js 中cardScopeFor(board)检查成员标志后对cardSelector追加assignees: { $in: [thisUserId] }另见第 876、908、930、957、979、1002 行同类收窄懒加载server/publications/cardsWindow.js 的窗口选择器通过...assignedOnlyCardScope(board, userId)收窄卡片的评论、附件、清单与清单项沿同一作用域windowCardIds基于同一选择器不会泄漏窗口外卡片的子对象卡片计数boardListCardCounts发布server/publications/cardsWindow.js也用同一收窄选择器——否则列表的加载更多会诱导受限成员滚动到永远到不了的卡片。这些逻辑收敛在 models/lib/boardCardScope.js 的isAssignedOnlyMember()识别三个 assigned-only 标志与assignedOnlyCardScope()返回{ assignees: { $in: [userId] } }或 null中并通过mergeCardScope()将客户端过滤与服务端作用域作为两个独立 MongoDB 合取项合并客户端键无法覆盖服务端强制键。注意isAssignedOnlyMember()只对看板活跃成员生效boardCardScope.js。非成员访问公开看板时没有成员文档承载标志不受此收窄——这是有意的该限制是对成员能看到什么的收窄不是看板可见性规则。4.2 看板设置 / 成员 / 角色——isBoardAdmin()该列即manageBoard能力只有board-admin为 true。schema 侧对应 models/boards.js 的hasAdmin()以及memberRole()models/boards.js中isAdmin首先返回board-admin的分支。4.3 移动卡片、给自己指派——字段级策略moveCard这是最值得展开的一列。为什么移动需要独立成列#3189移动是卡片的update过去长期走编辑同一条规则而 Worker 角色的全部意义就是移动卡片和给自己指派——于是两者都做不了。现在moveCard是独立能力由服务端逐字段强制models/lib/workerCardWrite.js服务端入口server/permissions/cards.js 的canUpdateCard()先走canEditCardOrLinkedCard写权限失败后若该成员是 Workerboard.hasWorker(userId)则回退到workerMayUpdateCard(userId, modifier)判定逻辑workerCardWrite.jsWorker 的更新只允许两类——移动$set仅包含listId、swimlaneId、sort及随移动写入的dateLastActivity、modifiedAtMOVE_FIELDS第 45-52 行自我指派$addToSet/$pull且载荷恰好是{ assignees: userId }——自己的 id且只能有一个键selfAssignOnly()第 73-78 行默认拒绝其余一切任何其他操作符$unset、$rename、未来 MongoDB 新增操作符、整文档替换、别人的名字、标题、标签等字段一律拒绝第 90、105-106 行。workerMayUpdateCard是纯函数且零依赖可独立测试tests/workerCardWrite.test.cjs。有意不包含workerCardWrite.js 注释boardId此处的移动限定在 Worker 所属看板内跨看板移动是另一行为由跨看板 deny 规则GHSA-gm7v-pc38-53jr见 server/lib/utils.js单独裁决指派/取消指派别人Worker 只能增删自己的名字即assign himself的精确语义members卡片上的其他成员字段角色定义说的是 assignee。五、已修复四个名不副实的角色缺陷原文档的 Fixed 一节记录的四项缺陷全部出自同一根因——规则以标志清单形式散落在三处三份清单漂移。现在它们统一读取一张表且 tests/boardRoles.test.cjs 会阻止第四种意见出现。5.1 缺陷 1Comment only, assigned曾拥有完整写权限除卡片发布外没有任何代码读取isCommentAssignedOnly它也不在写规则中于是该角色可以创建/编辑卡片、列表、清单——实际上是披着另一名字的Normal, assigned only。修复后它和Comment only一样是纯评论角色boardRoleCapabilities.js。测试验证roleCan(comment-assigned-only, write) false且两个 comment-only 角色在 comment/write/manageBoard 三维度上完全一致、仅 sees 不同boardRoles.test.cjs。5.2 缺陷 2No comments曾无法写任何东西旧写规则把isNoComments排除在外导致该角色连编辑也被封死——成了名字说只禁评论实际是第二个只读角色而 UI 还照样给它亮编辑按钮。修复后它只禁评论boardRoleCapabilities.js与名字和 schema 语义一致comment: false, write: true测试见 boardRoles.test.cjs。5.3 缺陷 3Worker 无法移动卡片或给自己指派#3189看板 schema 对 Worker 的官方描述是only allowed to move card, assign himself to card and comment但移动与自我指派都是卡片update都走写规则而写规则排除 Worker——于是这个靠两个特定写操作定义的角色两个都做不了。报告者的观感是卡片短暂显示自己的名字又弹回原指派者——乐观写入被服务端丢弃的典型表现workerCardWrite.js。修复方式正是旧 Known gaps 条目所指出的方案且不是放宽writemoveCard成为独立能力由 models/lib/workerCardWrite.js 按这次更新写了什么字段逐字段裁决。若直接放宽write等于把卡片的每个字段都交给 Worker与该角色相反。5.4 缺陷 4写规则未豁免看板管理员看板上所有has*()助手都会忽略管理员身上的标志但旧写规则读的是原始标志——于是携带isNoComments的管理员会悄悄失去写权限。Web UI 一次性写八个标志造不出这种组合但 REST API 逐个写标志能造出来详见第六节。修复方式memberRoleOf()先解析isAdmin管理员就是管理员无论还设置了什么boardRoleCapabilities.js。测试 boardRoles.test.cjs 对除isAdmin外的每个标志做了isAdmin flag组合断言。UI 助手是同一场漂移的一部分canModifyCard()过去不排除isNoComments服务端却排除、canModifyBoard()过去既不排除isNoComments也不排除isWorker——每次分歧都是给别人亮了服务端随后拒绝的按钮。现在三者都问同一张表client/lib/utils.js 注释与 boardRoles.test.cjs 的逐函数校验。六、设置角色的三种方式6.1 Web UI最安全看板侧边栏 → 点击成员的头像→ 选择角色。setMemberPermission一次写全八个标志保证互斥且无非法组合models/boards.js注意memberId currentUserId时强制保留自己原有的isAdmin防止管理员把自己降级。6.2 REST API唯一能造出UI 造不出组合的通道API 逐个接受标志因此可以产生 UI 无法产生的组合对应缺陷 4 的触发面。完整文档见 docs/API/Role.md。以下是可直接改写的 curl 示例把BOARD-ID-HERE、USER-ID-HERE与 Bearer Token 替换为真实值新增成员Normal 角色curl -H Authorization: Bearer a6DM_gOPRwBdynfXaGBaiiEwTiAuigR_Fj_81QmNpnf \ -H Content-type:application/json \ -X POST \ http://localhost:3000/api/boards/BOARD-ID-HERE/members/USER-ID-HERE/add \ -d {action: add,isAdmin: false, isNoComments:false, isCommentOnly: false, isWorker: false }修改既有成员角色接口为POST /api/boards/{boardid}/members/{memberid}按需组合标志角色请求体Admin{isAdmin: true, isNoComments:false, isCommentOnly: false, isWorker: false}Normal{isAdmin: false, isNoComments:false, isCommentOnly: false, isWorker: false}No Comments{isAdmin: false, isNoComments:true, isCommentOnly: false, isWorker: false}Comment Only{isAdmin: false, isNoComments:false, isCommentOnly: true, isWorker: false}Worker{isAdmin: false, isNoComments:false, isCommentOnly: false, isWorker: true}Worker 角色能力速记API 文档原话可以移动卡片、给自己指派、评论不可以做其他任何事——包括撤销卡片、改设置、创建/删除/编辑卡片与标签等。移除成员使用POST /api/boards/{boardid}/members/{memberid}/remove请求体{action: remove}。注意上述 API 示例仅覆盖四个主要标志isAdmin/isNoComments/isCommentOnly/isWorker另外四个 assigned-only / read-only 标志isNormalAssignedOnly、isCommentAssignedOnly、isReadOnly、isReadAssignedOnly同样存在于成员文档与 schema 中可由 API 单独设置。6.3 LDAP / OIDC 组同步组同步可以设置看板成员身份详见 docs/Features/Login/LDAP.md。七、防漂移机制让文档永远诚实普通权限文档最大的风险是静默过期——管理员正拿它决定信任谁过期的文档比没有更糟。因此 tests/boardRoles.test.cjs 把本文档的表格当作被测代码的一部分来校验node tests/boardRoles.test.cjs即可运行见文件头部注释 boardRoles.test.cjs。该测试约二十条断言覆盖表格结构解析| **开头的九行行数必须等于BOARD_ROLES.length每列必须明确回答 yes/noboardRoles.test.cjs标志真实性文档里每个\flag 必须是 schema/表里真实存在的成员标志一个不发明、一个不遗漏且无标志行恰好只有 Normal 一行boardRoles.test.cjs逐格对照能力表comment/write/manage/move 每格必须等于ROLE_CAPABILITIES的对应值sees 列必须与seesAllCards相反boardRoles.test.cjs发布作用域一致isAssignedOnlyMember()里读到的三个标志集合必须与表里seesAllCards: false的角色标志集合完全相等boardRoles.test.cjs服务端/客户端与表一致allowIsBoardMemberWithWriteAccess与allowIsBoardMemberCommentOnly必须调用memberCan(...,write/comment)且函数体内不得再出现任何原始标志名canModifyCard/canMoveCard/canModifyBoard必须分别请求write/moveCard/write同样不得自持标志清单boardRoles.test.cjsRoles Status 面板只读且读表必须require能力表、必须用共享tablePage模板渲染、不得含rowTemplate/headerTemplate/js-交互钩子与 action 按钮所有文案必须走 i18nTAPi18n.__(value ? yes : no)六列标题及角色名均为翻译键boardRoles.test.cjsKnown gaps 节内容约束表格中不允许出现 ⚠ 警告一旦出现必须连同 Known gaps 条目一起说明四个已修复缺陷必须记录在 Fixed 节且带 #3189 引用boardRoles.test.cjs。一句话如果你改了某个权限这份文档就是那次改动的一部分——测试会强制你同步更新它。八、Known gaps当前无未修复缺陷截至本仓库当前代码None recorded。设计约定是新的缺口必须记录在 Known gaps 节并写明为何不修复而不是在表格里用更温和的措辞掩盖该约束同样被测试校验。#3189Worker 移动/自我指派曾经是最后一条 ⚠ 条目修复后随测试一起移除。九、延伸阅读Members and Permissions成员与权限总览——九种角色的两行版速记Change Role at Web UI and APIREST API 角色接口——完整的 curl 示例Admin Panel管理面板——Roles Status 面板所在能力表源码、Worker 字段级写策略、卡片可见性作用域、服务端 allow 助手、客户端 UI 助手、卡片权限入口、防漂移测试。【免费下载链接】wekanThe Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/commercial-support/ . PR source translation to imports/i18n/data/en.i18n.json, other translations at https://app.transifex.com/wekan/wekan项目地址: https://gitcode.com/GitHub_Trending/we/wekan创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表