news 2026/9/17 22:12:55

Permify 实现 Google Docs 风格文档权限系统:组织、嵌套群组与文档级授权建模实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Permify 实现 Google Docs 风格文档权限系统:组织、嵌套群组与文档级授权建模实战

Permify 实现 Google Docs 风格文档权限系统:组织、嵌套群组与文档级授权建模实战

【免费下载链接】permifyAn open-source authorization as a service inspired by Google Zanzibar, designed to build and manage fine-grained and scalable authorization systems for any application. — Permify is now part of FusionAuth 🎉项目地址: https://gitcode.com/GitHub_Trending/pe/permify

本指南基于 Permify(受 Google Zanzibar 启发的开源授权即服务项目)官方示例文档,完整演示如何用 Permify Schema(DSL)为"Google Docs 风格"的文档管理系统建模:用户既可以获得文档的直接访问权限,也可以通过所在组织(organization)与嵌套群组(group)间接获得权限。读完本文你将掌握实体/关系/权限(action/permission)三要素建模方法、关系元组(relationship tuples)的写法,以及如何用permify validate命令对授权模型做本地断言验证。

场景概述:组织 — 群组 — 文档的三层授权结构

Google Docs 风格的文档系统有一个典型特征:权限来源多样且可叠加。一个用户能访问某份文档,可能是因为:

  1. 他是文档的直接 viewer/manager;
  2. 他所属的群组被授予了文档的查看或管理权限;
  3. 他是文档所属组织的管理员(admin),从而对组织下所有文档拥有编辑/查看权限;
  4. 群组之间还可以嵌套(group 的成员是另一个 group),形成多级继承。

这正是 Permify 最擅长的场景。官方文档在 docs/getting-started/examples/google-docs.mdx(对应docs/docs/getting-started/examples/下的同名 markdown 镜像)中给出了完整的建模方案,本文将以它为骨架展开讲解。

完整的授权模型 Schema

下面是示例的核心 Schema,它定义了userorganizationgroupdocument四个实体及其关系与权限:

entity user {} entity organization { relation group @group relation document @document relation administrator @user @group#direct_member @group#manager relation direct_member @user permission admin = administrator permission member = direct_member or administrator or group.member } entity group { relation manager @user @group#direct_member @group#manager relation direct_member @user @group#direct_member @group#manager permission member = direct_member or manager } entity document { relation org @organization relation viewer @user @group#direct_member @group#manager relation manager @user @group#direct_member @group#manager action edit = manager or org.admin action view = viewer or manager or org.admin }

模型分解:四个实体的职责与关系设计

user:授权的最终主体

entity user {}

user是空实体,代表可以被授予文档访问权限的人。它是关系元组中最常见的 subject(主体),用户既可以作为"直接授权对象"出现,也可以通过群组或组织成员关系间接获得权限。

document:被授权访问的资源

entity document { relation org @organization relation viewer @user @group#direct_member @group#manager relation manager @user @group#direct_member @group#manager action edit = manager or org.admin action view = viewer or manager or org.admin }

document实体定义了两个关系(relation)和两个动作(action):

关系(Relations)

  • org:文档所属的组织。通过@organization注解限定该关系的 subject 只能是organization实体,这是文档级权限"上溯"到组织级权限的桥梁(org.admin的语法即通过该关系访问组织的admin权限)。
  • manager:被授权管理文档的用户。subject 类型包括@user(直接指定个人),以及@group#direct_member@group#manager(指定某个群组的成员或管理员集合)。
  • viewer:被授权查看文档的用户,subject 约束与manager一致。

动作(Actions)

  • editmanager or org.admin。可编辑文档的用户 = 文档的直接 manager,或文档所属组织的管理员。
  • viewviewer or manager or org.admin。可查看文档的用户 = 文档的 viewer、manager,或所属组织的管理员。

注意editview存在隐式包含关系:凡是能编辑的必然能查看,这正是权限模型用action组合relation与其它权限的典型写法。

group:可嵌套的中间授权层

entity group { relation manager @user @group#direct_member @group#manager relation direct_member @user @group#direct_member @group#manager permission member = direct_member or manager }

group实体有两个关系:

  • manager:群组的管理员,subject 可以是具体用户,也可以是其它群组的成员/管理员集合。
  • direct_member:群组的直接成员,subject 类型同样支持@user@group#direct_member@group#manager"direct"(直接)一词非常关键:它强调该关系不经过任何推导,为群组嵌套提供了精确的成员边界。

group还定义了一个权限memberdirect_member or manager。注意在示例文档的建模中,documentviewer/manager关系引用的是@group#direct_member@group#manager这两种"直接关系",而不是group.member这个权限——这意味着只有群组的直接成员/直接管理员才能继承文档权限,群组的嵌套成员(通过direct_member@group:...引入的间接成员)不会自动获得外层群组挂载的文档权限。这种"显式控制继承深度"的设计正是细粒度授权建模的精髓,需要在实际建模中根据业务语义仔细取舍。

organization:组织级权限的汇聚点

entity organization { relation group @group relation document @document relation administrator @user @group#direct_member @group#manager relation direct_member @user permission admin = administrator permission member = direct_member or administrator or group.member }

organization实体聚合了群组、文档与用户:

  • group:组织下属的群组(subject 为@group)。
  • document:组织下属的文档(subject 为@document)。
  • administrator:组织管理员,subject 可以是用户或群组的成员/管理员集合。
  • direct_member:组织直接成员(subject 为@user)。

权限:

  • adminadministrator,即组织管理员集合。
  • memberdirect_member or administrator or group.member,组织成员 = 直接成员 ∪ 管理员 ∪ 组织下所有群组的成员。这里group.member通过relation group @group实现"跨实体权限引用",是嵌套继承的核心语法。

关系元组:注入示例授权数据

有了 Schema 后,需要写入关系元组(tuples)来表达"谁对什么拥有什么关系"。下面是示例数据,按业务语义分组:

// 为用户分配群组角色 group:tech#manager@user:ashley group:tech#direct_member@user:david group:marketing#manager@user:john group:marketing#direct_member@user:jenny group:hr#manager@user:josh group:hr#direct_member@user:joe // 群组嵌套:marketing、hr 群组的成员同时也是 tech 群组的成员 group:tech#direct_member@group:marketing#direct_member group:tech#direct_member@group:hr#direct_member // 将群组挂载到组织 organization:acme#group@group:tech organization:acme#group@group:marketing organization:acme#group@group:hr // 将文档挂载到组织 organization:acme#document@document:product_database organization:acme#document@document:marketing_materials organization:acme#document@document:hr_documents // 指定组织管理员:tech 群组的管理员 + 用户 jenny organization:acme#administrator@group:tech#manager organization:acme#administrator@user:jenny // 为文档设置权限 document:product_database#manager@group:tech#manager document:product_database#viewer@group:tech#direct_member document:marketing_materials#viewer@group:marketing#direct_member document:hr_documents#manager@group:hr#manager document:hr_documents#viewer@group:hr#direct_member

元组(tuple)的语法为entity:id#relation@subject,其中 subject 可以是entity:id(如user:ashley),也可以是带关系的entity:id#relation(如group:tech#manager)。这 21 条元组共同构建了一棵授权关系图:

  • tech 群组:ashley 是 manager,david 是 direct_member;marketing 与 hr 的 direct_member 也嵌套进 tech 群组;
  • acme 组织:挂载了 tech、marketing、hr 三个群组和三份文档;管理员为 tech 群组管理员集合与 jenny;
  • 三份文档:product_database 由 tech 群组管理,marketing_materials 由 marketing 群组查看,hr_documents 由 hr 群组管理与查看。

权限断言验证:三种典型授权路径

基于上述数据,示例给出了三个访问检查(access check)用例,用来验证授权逻辑是否符合预期。

用例一:user:ashley能编辑document:product_database吗?

根据edit = manager or org.admin,检查引擎会沿着两条路径推导:

  1. 检查 ashley 是否与document:product_database存在直接或间接的manager关系——是的,元组document:product_database#manager@group:tech#manager将 tech 群组的 manager 集合(包含 ashley)授予了该文档的 manager;
  2. 检查 ashley 是否拥有组织 admin 权限——她不是 acme 组织的管理员。

由于路径 1 命中,检查结果为 true(允许)。这演示了"通过群组角色间接获得文档权限"的授权路径。

用例二:user:joe能查看document:hr_documents吗?

根据view = viewer or manager or org.admin

  1. joe 不是文档的直接 viewer/manager,也不是组织 admin;
  2. 但 joe 是 hr 群组的 direct_member(group:hr#direct_member@user:joe),而元组document:hr_documents#viewer@group:hr#direct_member把 hr 群组的直接成员集合授予了文档的 viewer。

因此检查结果为 true(允许)。这演示了"群组成员身份传递到文档查看权限"的路径。

用例三:user:david能查看document:marketing_materials吗?

  • david 是 tech 群组的 direct_member,但 marketing_materials 文档的 viewer 只授予了 marketing 群组的直接成员(document:marketing_materials#viewer@group:marketing#direct_member),david 不在其中;
  • david 也不是文档 manager,更不是组织 admin。

检查结果为 false(拒绝)。这个用例非常有教学价值:即使 david 通过群组嵌套(marketing 是 tech 的子群组)与 tech 群组相关,但由于文档关系引用的是@group#direct_member(直接成员)而非group.member(含嵌套),跨群组继承被精确阻断。

说明:示例文档在叙述用例二时提到group:hr#member@group#member,但实际 Schema 与元组中采用的是direct_member关系(permission member = direct_member or manager),本文以仓库中真实 Schema 与元组数据为准。

本地验证:用permify validate运行断言

上述访问检查可以通过 Permify 官方提供的validator(验证器)在本地一键运行。验证文件是一个 YAML,结构由 pkg/development/file/shape.go 中的Shape结构定义,包含三大块:

  • schema:待测试的授权模型;
  • relationships:样本授权数据(元组列表);
  • scenarios:测试场景,每个场景包含checks(访问检查断言,assertions中的true/false表示期望的允许/拒绝结果),还可选entity_filterssubject_filters(分别对应 LookupEntity 与 LookupSubject 的断言,用于验证反向查询)。

完整的验证 YAML 文件

schema: >- entity user {} entity organization { relation group @group relation document @document relation administrator @user @group#direct_member @group#manager relation direct_member @user permission admin = administrator permission member = direct_member or administrator or group.member } entity group { relation manager @user @group#direct_member @group#manager relation direct_member @user @group#direct_member @group#manager permission member = direct_member or manager } entity document { relation org @organization relation viewer @user @group#direct_member @group#manager relation manager @user @group#direct_member @group#manager action edit = manager or org.admin action view = viewer or manager or org.admin } relationships: - group:tech#manager@user:ashley - group:tech#direct_member@user:david - group:marketing#manager@user:john - group:marketing#direct_member@user:jenny - group:hr#manager@user:josh - group:hr#direct_member@user:joe - group:tech#direct_member@group:marketing#direct_member - group:tech#direct_member@group:hr#direct_member - organization:acme#group@group:tech - organization:acme#group@group:marketing - organization:acme#group@group:hr - organization:acme#document@document:product_database - organization:acme#document@document:marketing_materials - organization:acme#document@document:hr_documents - organization:acme#administrator@group:tech#manager - organization:acme#administrator@user:jenny - document:product_database#manager@group:tech#manager - document:product_database#viewer@group:tech#direct_member - document:marketing_materials#viewer@group:marketing#direct_member - document:hr_documents#manager@group:hr#manager - document:hr_documents#viewer@group:hr#direct_member scenarios: - name: "scenario 1" description: "test description" checks: - entity: "document:product_database" subject: "user:ashley" assertions: edit: true - entity: "document:hr_documents" subject: "user:joe" assertions: view: true - entity: "document:marketing_materials" subject: "user:david" assertions: view: false

运行验证的步骤

  1. 克隆 Permify 仓库后,新建一个 YAML 文件,将上述内容粘贴进去;
  2. 构建并启动 Permify 服务,执行make serve(相关命令定义见 Makefile);
  3. 另开终端,运行permify validate {你的验证文件路径}开始测试。

permify validate命令由 pkg/cmd/validate.go 实现,其底层执行流程清晰可见:

  1. 通过file.NewDecoderFromURL解析 YAML(支持本地文件路径与外部 URL),解码为Shape
  2. 使用schema.NewSchemaLoader+ DSLparser+compiler加载、解析并编译 Schema(pkg/schema/loader.gopkg/dsl/parser/parser.gopkg/dsl/compiler/compiler.go);
  3. 将每条 relationship 经tuple.Tuple解析后,通过dev.Container.DW.Write写入开发容器(内存存储),写入前用实体定义校验元组合法性;
  4. 对每个 scenario 的 checks 逐条调用dev.Container.Invoker.Check执行访问检查,将结果与assertions中的期望值比对;
  5. 全部通过时输出SUCCESS,否则打印具体失败的断言(ALLOWED/DENIED 与期望不符)并退出。

将验证接入 CI 流水线

如果希望把这类断言纳入持续集成,仓库文档 docs/docs/getting-started/testing.md 介绍了permify-validate-actionGitHub Action 的用法:只需在 workflow 中通过validationFile指定验证 YAML(支持本地文件或外部 URL)即可自动执行同样的校验。

源码佐证:Google Docs 示例的集成测试

该场景不仅是文档示例,还以代码形式固化在仓库的集成测试中,可作为最可信的"可复现依据":

  • 场景 Shape 定义在 integration-test/usecases/shapes/google_docs.go:包含 Schema(此处使用resource实体名,语义与document一致)、21 条关系元组,以及三个断言场景(user:ashley edit product_database → trueuser:joe view hr_documents → trueuser:david view marketing_materials → false),与本文讲解完全对应;
  • 测试用例定义在 integration-test/usecases/google_docs_test.go:通过 ginkgo 驱动,覆盖四类 API:
    • PermissionCheckRequest单条检查;
    • PermissionBulkCheckRequest批量检查(BulkCheck);
    • PermissionLookupEntityRequest实体过滤(给定主体反查可访问的实体 ID 列表);
    • PermissionLookupSubjectRequest主体过滤(给定实体与权限反查可访问的主体 ID 列表)。

这组测试印证了文档中"断言 → 引擎推导 → 允许/拒绝"的完整闭环,也说明同一条关系数据可同时支撑正向检查(Check)与反向查询(Lookup)两类典型授权 API。

小结

通过 Google Docs 风格示例,我们完整走通了 Permify 建模到验证的闭环:

  1. 建模:用entity/relation/permission/action表达文档、群组、组织的授权结构,并通过@group#direct_member这类"关系级引用"精确控制权限继承深度;
  2. 数据:用关系元组注入样本授权数据,组织、群组嵌套、文档授权三类元组各司其职;
  3. 验证:借助permify validate与验证 YAML,把"Ashley 可编辑产品数据库、Joe 可查看 HR 文档、David 不可查看营销材料"这类业务断言固化为可重复运行的测试,并可在 CI 中持续执行。

如果想继续深入,可参考仓库中的相关文档:安装与启动指南、授权建模指南、同步授权数据,以及同类场景示例(Facebook Groups、Notion、Instagram、Mercury),从而覆盖 RBAC、ReBAC、属性/条件等更多建模范式。

【免费下载链接】permifyAn open-source authorization as a service inspired by Google Zanzibar, designed to build and manage fine-grained and scalable authorization systems for any application. — Permify is now part of FusionAuth 🎉项目地址: https://gitcode.com/GitHub_Trending/pe/permify

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 22:10:56

OSI七层模型解析与网络故障排查实战

1. OSI七层模型概述计算机网络领域有个经典的理论框架,就像建筑行业的施工蓝图一样重要——这就是OSI七层模型。1984年国际标准化组织(ISO)发布的这个参考模型,把复杂的网络通信过程分解成七个逻辑层,每层都有明确的职…

作者头像 李华
网站建设 2026/9/17 22:10:52

2023上半年软考数据库系统工程师上午真题复盘与备考指南

“数据库系统工程师”这个中级资格在软考里一直是报考大户,2023年上半年那场考试更是特别——它是软考机考改革前一场大规模的传统笔试,上午的《基础知识》科目仍然沿用75道单选题、150分钟、45分及格的老规矩。我在考场上最大的感受是:这张卷…

作者头像 李华
网站建设 2026/9/17 22:10:21

一条 TaoToken Key:补丁验证 Agent 在 FLAWED 里分档跑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 22:09:09

案例 Grok Bot 成本审计,TaoToken 帮 Agent 做权限隔离

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华