Skip to content

ACL 与继承模式篇

目标:让集成方设置 / 查询 ACL、读懂权限位与预设角色、掌握容器→文档继承(断继承 / 重继承 / 位或聚合)、用 acl/check 解析有效权限、用 AclPolicy 做规模化授权与 dry-run 预览。

不在本篇范围:身份解析与双通道 granteeId 获取(见 认证与身份双通道模式篇);分享(ShareGrant / ShareLink)授权(分享模式篇);错误码完整码表(见错误码参考)。

本篇是领域模型 §ACL的纵深展开。概念页讲「resource + grantee + role 三段式 + 容器→文档继承」的模型,本篇讲取值枚举、继承规则、有效权限解析与坑。落到具体主体身份怎么来,见认证模式篇。

权限位与预设角色

权限是 6 个可组合的位:

含义
READ1读元数据 / 列表
DOWNLOAD2下载内容字节
WRITE4写 / 上传新版本
DELETE8删除
SHARE16创建分享
MANAGE32管理(含改 ACL)

预设角色是固定的位组合,按掩码精确匹配

角色掩码等价位
VIEWER3READ + DOWNLOAD
EDITOR7READ + DOWNLOAD + WRITE
MANAGER63全部 6 位

⚠️ 角色是精确匹配:只有掩码恰好等于 3 / 7 / 63 才 resolve 出 VIEWER / EDITOR / MANAGER。自定义组合(如 5 = READ + WRITE)resolve 出的 resolvedRolenull——这是正常的,集成方应按 permissionMask判权(mask & READ == READ),不要因 role 为 null 误判「无权限」。传入未定义位返 102004

grantee 三类主体

granteeType 取值 USER / ROLE / DEPARTMENT,传其它值返 102009granteeIdatkonbase 内部稳定 ID(与认证模式篇 §granteeId 解析同一口径)——不是 IdP 侧 unionId / sub,也不是集成方业务系统的原始用户主键。

一、设置 / 查询 / 删除 ACL

设置一条(POST /v1/acl/set

bash
curl -X POST 'https://api.atkonbase.example.com/v1/acl/set' \
  -H 'Authorization: Bearer ${accessToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "resource": { "resourceType": "DOCUMENT", "resourceId": "${docId}" },
    "grantee": { "granteeType": "USER", "granteeId": "${userId}" },
    "role": "VIEWER"
  }'
  • resource.resourceType 取值 DOCUMENT / CONTAINER(其它值返 102007)。
  • rolepermissionMask 二选一,双传时 role 优先;走预设角色推荐用 role,自定义位组合才用 permissionMask

期望响应:

json
{
  "code": 0,
  "data": {
    "aclId": "A1000001",
    "resource": { "resourceType": "DOCUMENT", "resourceId": "${docId}" },
    "grantee": { "granteeType": "USER", "granteeId": "${userId}" },
    "permissionMask": 3,
    "resolvedRole": "VIEWER"
  }
}

查询资源全部条目(GET /v1/acl/getAcl

bash
curl -X GET 'https://api.atkonbase.example.com/v1/acl/getAcl?resourceType=DOCUMENT&resourceId=${docId}' \
  -H 'Authorization: Bearer ${accessToken}'

返回该资源上的 ACL 条目数组(每条含 aclId / grantee / permissionMask / resolvedRole)。

删除一条(GET /v1/acl/delete

bash
curl -X GET 'https://api.atkonbase.example.com/v1/acl/delete?aclId=${aclId}' \
  -H 'Authorization: Bearer ${accessToken}'

aclId 来自此前 set / getAcl 响应;条目不存在返 102008(集成方按幂等处理,不存在视为已删除)。

批量设置(POST /v1/acl/batch-set

entries 是一组 set 请求体,同事务生效:

bash
curl -X POST 'https://api.atkonbase.example.com/v1/acl/batch-set' \
  -H 'Authorization: Bearer ${accessToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "entries": [
      { "resource": { "resourceType": "DOCUMENT", "resourceId": "${docId}" },
        "grantee": { "granteeType": "ROLE", "granteeId": "${roleId}" },
        "role": "EDITOR" },
      { "resource": { "resourceType": "DOCUMENT", "resourceId": "${docId}" },
        "grantee": { "granteeType": "DEPARTMENT", "granteeId": "${deptId}" },
        "role": "VIEWER" }
    ]
  }'

二、有效权限检查(POST /v1/acl/check

check 回答「这组主体对这个资源是否有某权限」,已把继承与多态主体合并解析好:

bash
curl -X POST 'https://api.atkonbase.example.com/v1/acl/check' \
  -H 'Authorization: Bearer ${accessToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "resource": { "resourceType": "DOCUMENT", "resourceId": "${docId}" },
    "grantees": [
      { "granteeType": "USER", "granteeId": "${userId}" },
      { "granteeType": "ROLE", "granteeId": "${roleId}" },
      { "granteeType": "DEPARTMENT", "granteeId": "${deptId}" }
    ],
    "permission": "WRITE"
  }'

期望响应:

json
{
  "code": 0,
  "data": {
    "allowed": true,
    "effectivePermissionMask": 7,
    "effectiveDenyMask": 0,
    "effectiveRole": "EDITOR"
  }
}
  • grantees多态主体集(一个用户的 userId + 其角色 + 其部门):任一主体命中即可通过,命中行的权限位按「或」聚合
  • permission(单个 PermissionBit)与 permissionMask(位组合)二选一,双传时 permission 优先;判定逻辑是 effectivePermissionMask & requiredMask == requiredMask
  • effectiveDenyMask —— 被 grantType=DENY 的 Policy 显式拒绝的位(见下「AclPolicy」);无 DENY 命中时为 0。
  • effectiveRole —— 基于 effectivePermissionMask 精确匹配的角色,自定义组合时为 null
  • ⚠️ 用户被禁用时会从主体集中过滤掉,其权限按 0 解析;委派链路解析不出主体集返 102003

批量掩码查询(POST /v1/acl/batchCheck

一次拿回多个同类型资源的有效掩码(单次 ≤ 200 个):

bash
curl -X POST 'https://api.atkonbase.example.com/v1/acl/batchCheck' \
  -H 'Authorization: Bearer ${accessToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "resourceType": "DOCUMENT",
    "resourceIds": ["${docId1}", "${docId2}"]
  }'

响应 data.permissions资源ID → 有效掩码 的映射:

json
{ "code": 0, "data": { "permissions": { "${docId1}": 7, "${docId2}": 0 } } }

三、容器→文档继承

  • 传播源:容器上非 Policy 派生的显式 ACL 条目。在容器授一次权,沿容器树向下传播到后代容器 + 文档
  • 断继承:容器的 aclInheritEnabled=false 切断该容器整棵子树的继承(子树不再接收上层传播)。
  • 新建 vs 重继承
    • 新建资源时继承父容器的有效 ACL;若落在断继承子树内,则不继承、改为给创建者授全部位(保证创建者能操作自己新建的资源)。
    • 对已有资源重继承(如父条目变更触发重算)时,不会自动给操作者授权——只重算继承链,不附加操作者权限。
  • 位或聚合、无覆盖:继承与多条授予都按位「或」聚合,没有「覆盖 / 降权」概念——加授权只会叠加、不会相互削减。要降权只能:① 改父容器的条目;② 断继承;③ 用 grantType=DENY 的 Policy 显式拒绝(反映在 checkeffectiveDenyMask)。
  • ⚠️ 传播树最大深度 100:继承沿容器层级最多传播 100 层;超过该深度的传播将被截断,组织容器层级时避免病态深度。

四、AclPolicy:规模化自动授权

逐条 acl/set 适合点对点授权;当「某类资源对某批主体统一授权」且资源数量大或持续新增时,用 AclPolicy 声明规则、由系统自动派生 ACL 条目(派生条目带 Policy 来源标记,与手动条目区分)。

创建 Policy(POST /v1/acl/policy/save

bash
curl -X POST 'https://api.atkonbase.example.com/v1/acl/policy/save' \
  -H 'Authorization: Bearer ${accessToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "合同文档对法务只读",
    "matchResourceType": "DOCUMENT",
    "matchContainerTypeIds": ["${contractContainerTypeId}"],
    "matchConditions": [
      { "key": "status", "operator": "EQ", "value": "signed" }
    ],
    "grantees": [{ "granteeType": "ROLE", "granteeId": "${legalRoleId}" }],
    "grantType": "ALLOW",
    "grantRole": "VIEWER",
    "enabled": true
  }'

字段说明:

  • matchResourceType —— CONTAINER / DOCUMENT
  • matchContainerTypeIds —— 按容器类型过滤。⚠️ 仅对文档有效(容器自身无 type 维度);matchResourceType=CONTAINER 时此过滤不生效,别指望它能筛容器。
  • matchConditions —— 元数据条件(key + operator + value 三元组列表)。⚠️ 空 / 不传 = 全匹配(匹配该资源类型下所有资源),不是「匹配 0 个」——批量授权前务必确认条件,否则可能误授全量。
  • grantType —— ALLOW / DENY(其它值返 102011)。
  • grantRole / grantPermissionMask —— 二选一,授予的角色或位组合。
  • enabled —— 是否启用(默认 true)。

启用 / 修改 Policy(含 /v1/acl/policy/enable · /disable · /update)会触发重评估,重新派生匹配资源的 ACL 条目。

dry-run 预览(强烈建议授权前先跑)

Policy 影响面可能很大,落库前用 dry-run 预览。两个端点:

新建预览(POST /v1/acl/policy/dry-run——传一份 Policy 条件,看会影响多少资源:

bash
curl -X POST 'https://api.atkonbase.example.com/v1/acl/policy/dry-run' \
  -H 'Authorization: Bearer ${accessToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "matchResourceType": "DOCUMENT",
    "matchContainerTypeIds": ["${contractContainerTypeId}"],
    "matchConditions": [{ "key": "status", "operator": "EQ", "value": "signed" }],
    "grantees": [{ "granteeType": "ROLE", "granteeId": "${legalRoleId}" }],
    "grantType": "ALLOW"
  }'

响应(DryRunResultDTO):

json
{
  "code": 0,
  "data": {
    "totalAffectedResources": 1240,
    "sampleResources": [ { "resourceType": "DOCUMENT", "resourceId": "D1000001" } ],
    "existingManualAclCount": 12,
    "grantType": "ALLOW",
    "truncated": false
  }
}
  • totalAffectedResources —— 命中资源总数;sampleResources —— 样本(最多 20 条);existingManualAclCount —— 同 grantee 已有的手动 ACL 条目数(提示与现有授权的重叠)。

已有 Policy 改条件预览(POST /v1/acl/policy/dry-run/{policyId}——改条件后影响如何变化,返回 DryRunDiffResultDTO

json
{
  "code": 0,
  "data": {
    "currentAffectedResources": 1240,
    "newAffectedResources": 1310,
    "toAdd": 80,
    "toRemove": 10,
    "sampleToAdd": [ { "resourceType": "DOCUMENT", "resourceId": "D1000500" } ],
    "sampleToRemove": [ { "resourceType": "DOCUMENT", "resourceId": "D1000003" } ],
    "truncated": false
  }
}
  • toAdd / toRemove —— 新条件相对当前新增 / 移除匹配的资源数;sampleToAdd / sampleToRemove —— 各自样本(最多 20 条)。
  • ⚠️ truncated(截断标记):dry-run 有 30 秒超时上限,超大匹配集会在超时时截断——truncated=true 时所有统计值是部分值,不可当精确总数用;缩小条件范围或分批评估后再读。

常见坑

  • ⚠️ 继承无覆盖:以为在子节点 set 一个更小的角色能「降权」覆盖父条目——实际是位或叠加、权限只增不减。规避:降权改父条目 / 断继承 / 用 DENY Policy。
  • ⚠️ granteeId 传外部 ID:把 IdP unionId / 业务系统用户主键当 granteeId——授权落到不存在的主体,检查恒不通过。规避:先解析出 atkonbase 内部 ID(见认证模式篇)。
  • ⚠️ matchConditions 空 = 全匹配:Policy 不传条件,误以为「没条件就不匹配」,结果对该类型全量资源授权。规避:dry-run 先看 totalAffectedResources
  • ⚠️ matchContainerTypeIds 对容器失效matchResourceType=CONTAINER 时仍指望容器类型过滤生效。规避:该过滤仅对文档有效。
  • ⚠️ 预设角色精确匹配 null:自定义掩码(如 5)的 resolvedRole 为 null,被误判为「无权限」。规避:按位判权,不依赖 role 非空。
  • ⚠️ dry-run 截断当精确值truncated=true 的统计当成完整结果。规避:读到 truncated 即缩小范围重跑。
  • ⚠️ 跨租户隔离:ACL 与 Policy 都在租户内解析,跨租户主体 / 资源不可见。规避:确保主体与资源同租户。
  • ⚠️ 用户禁用权限消失:被禁用用户从主体集过滤、权限按 0 算,表现为「昨天还能访问今天 102001」。规避:排查用户启用状态,非 ACL 配置问题。

下一步