资源访问控制(ACL)
OpenViking ACL 用于在同一个 account 内,把共享资源目录或文件授权给用户或用户组。ACL 不改变 account 隔离:任何授权都只在当前 account 内生效。
ACL 采用协作文档式的继承模型。目录授权默认持续作用于所有后代,子目录和文件可以继续增加直接授权,也可以用 restricted 模式在某个节点切断继承权限。
适用 URI
ACL 只作用于共享资源:
viking://resources/...的 accountADMIN隐式拥有manage。viking://resources是固定共享 scope,本身不能设置直接 ACL;ACL 从它下面的文件和目录开始。viking://user/{user_id}/resources/...是个人私有区,不接受 ACL。需要分享时,将资源移动到有权写入的共享目录,并继承该目录的 ACL。
隐式管理权不会写入 ACL 条目,也不能被 ACL 删除。它保证共享资源始终有人能够首次设置或恢复权限。
Principal 与权限级别
ACL 条目使用带类型的 principal:
user:{user_id}:当前 account 内的用户。group:{group_id}:由调用者指定、在当前 account 内唯一的用户组 ID。user:*:当前 account 内任意用户。
不支持 group:*。用户组是平铺结构;修改成员关系不会重写资源 ACL 或 context 记录,而是在下一次请求构造 RequestContext.group_ids 时生效。
请求创建的异步解析和语义任务会携带同一份 group 身份。add-resource 写入目标通过鉴权后,自动语义维护会保留原调用者身份,并显式使用内部 ACL bypass,不会把调用者角色改成 ADMIN。
| Level | 允许的操作 |
|---|---|
read | 读取、列目录、find/search/grep |
write | read 的能力,以及写入、创建、删除或移动文件、修改 tags |
manage | write 的能力,以及删除或移动目录、管理 ACL |
高等级包含低等级能力。授予 manage,等价于同时授予 read 和 write。
继承规则
普通节点合并 direct 与 inherited;restricted 节点只使用 direct:
inherited(node) 始终保存父节点当前的有效权限。即使节点处于 restricted 模式,这个字段也会随父节点继续更新;退出 restricted 后会立即使用最新 inherited。后代继承的是当前节点的有效权限,因此不会绕过中间的 restricted 边界。
例如:
report.md 的有效权限为:
Bob:
readengineering的成员:writeCarol:
read
如果把 A/B 设为 restricted,Bob 在 A 上的权限不会对 A/B 及其后代生效,但保存的 inherited 不会被删除。删除 restricted 后,Bob 会立即恢复从 A 继承的权限。
默认行为与 acl_mode
账号级 acl.enabled 默认关闭。关闭时,共享资源继续使用原有 URI namespace
可见性和写入规则,不解析或校验 ACL,也不为新建内容写入 ACL。
开启后,新建共享文件或目录的创建者会在首条 context 记录上获得直接 manage;
父目录权限作为继承 ACL 合并。已有且未设置 ACL 的内容不会迁移或改权,仍按公开
规则访问;重新关闭后,已有 ACL 也不再参与访问判断。add-resource 只把本次生成
的根目录(no_split 时为根文件)作为创建节点:根节点获得创建者直接 manage,
内部节点只继承,不重复写直接授权。重新向量化或覆盖已有 context 不会改变直接 ACL。
acl_mode 表示当前资源如何使用 ACL,与账号总开关 acl.enabled 不是一回事:
none:该资源不受 ACL 控制,沿用原有可见性规则。inherit:使用直接权限和父目录传下来的权限。restricted:只使用直接权限,但仍保存并更新父目录传下来的权限。
有 manage 权限的用户可以切换 inherit / restricted,不能直接设置 none 绕过父目录权限。退出 restricted 且没有直接权限、父目录也不受 ACL 控制时,系统会恢复为 none。restricted 节点即使没有直接权限也不会变公开,其没有单独授权的后代同样不可访问;账号管理员仍有隐式管理权。
文件操作
所有文件接口使用同一套权限判断:
| 操作 | 所需能力 |
|---|---|
| read、stat、list、tree、find、search、grep、glob、relations | read |
| write、create、mkdir、set tags | write |
| 删除或移动文件 | write |
| 删除或移动目录 | 目录及完整子树的 manage |
| 管理 ACL | manage |
| move 目标父目录 | write |
服务端会先 canonicalize URI,再在同一个鉴权入口中依次执行 account/owner/actor peer 等硬边界、有效 ACL 或 legacy fallback,以及写入和删除的 namespace 防护。
账号开启 acl.enabled 时,新建共享节点由创建者的直接 manage 完成权限
bootstrap,后续 ACL 修改要求有效 manage 能力。
目录上的 ACL 授权会被所有后代继承。list、tree 和批量结果仍逐个检查有效 ACL,因为未设置 ACL 的目录可能按原有 URI 规则可见,而某个后代已经通过自己的 ACL 进入控制域。
共享区内部移动时,节点自己的 direct ACL 和 restricted 状态随节点移动,inherited 按新父节点重新计算。个人资源移入共享区时不携带 ACL,只继承目标目录权限;共享资源移回个人区时清空 ACL。
递归修改 tags、删除或移动目录会先校验完整目标子树。任一节点缺少所需能力,或子树扫描不完整,操作都会整体中止。
目录 stat 的 count 使用相同的路径和 ACL 标量过滤,表示当前用户可见的 context 数量。
检索过滤
ACL 只保存在 context collection。每条 context 记录维护当前节点和继承权限两组原生标量字段:
acl_direct_grants 是当前节点直接 ACL,acl_inherited_grants 是父节点当前有效 ACL,acl_mode 决定 inherited 是否参与当前节点的有效权限。每个 principal 只保存最高 level,编码为 {mask}:{principal}:1 表示 read、3 表示 write、7 表示 manage。不维护独立 ACL collection。
请求的可用 principal 为 user:{ctx.user_id}、user:*,以及 ctx.group_ids 中每个 ID 对应的 group:{group_id}。检索在共享区内用 acl_mode IN [inherit, restricted] 判断受控资源,再匹配各 principal 的 1、3、7 grant token:inherit 匹配 direct 或 inherited,restricted 只匹配 direct。旧记录字段缺失、为 null 或 none 时仍走原有可见性规则,不会因空值漏检,也不会被当作 ACL 已开启。个人资源始终按 URI owner 隔离。
检索 target URI 只是搜索范围,不要求调用者能够读取 target 节点本身。用户即使不能读取中间目录,也可以检索到深层单独授权给自己的文件。
账号开启 acl.enabled 时,共享区 context 写入会保留同 URI 已有 direct ACL;
新创建节点为创建者生成直接 manage,并从父节点生成 inherited ACL。
add-resource 的内部节点只继承导入根节点。重新向量化和普通覆盖写不会把受控记录
恢复为默认可见,也不能通过普通 context 字段直接改 ACL。账号关闭该开关时,检索
只使用原有 account 和 URI scope 过滤,不使用这些 ACL 字段。
示例
将目录授权给 Bob 只读:
Bob 可以读取和检索该目录的后代,但不能写入或删除。升级为 write:
删除 Bob 在当前节点上的直接授权:
如果 Bob 仍被祖先目录授权,该继承权限继续有效。
只使用当前节点直接授权,同时保留并继续更新继承字段:
相关文档
ACL API - HTTP、SDK 和 CLI 接口
多租户 - account、user 和角色边界
Viking URI - URI namespace
检索 - 分层检索流程