Skip to content

系统角色与自定义角色 ​

面向要做管理控制台的产品团队:租户管理员能看到应用的角色,能新建自己的角色、复制角色、勾选权限。本页说明两类角色分别放在哪里,请求如何通过它们获得授权,以及控制台的每个动作对应哪个控制面调用。它接续不使用空间的应用。

两类角色 ​

系统角色自定义角色
位置应用文档的 roles租户文档的 roles
谁来写平台,随应用一起租户的管理员
适用于每个租户仅该租户
应用文档所属的应用app,必填:自定义角色属于一个应用
权限列表,或 "*"(目录里的全部)只能是列表:写 "*" 会让一次目录发布悄悄扩大每个租户的角色
可以为空是是

两种情况下,成员都在令牌或 MembershipProvider 里按 id 持有角色。id 在所有地方都是同一个字符串,所以自定义角色最好用不透明的 id(名称可以在控制台里改,id 不行)。

文档 ​

应用文档在上一页的基础上多了一个没有权限的角色,控制台会把它显示为待配置的角色:

json
{
  "schema": 2,
  "revision": 1,
  "publishedAt": "2026-11-01T08:00:00Z",
  "catalog": {
    "groups": [
      {
        "name": "Warehouse",
        "permissions": [
          {
            "id": "warehouse.read",
            "name": "Read warehouses",
            "action": "wms:warehouse.read"
          },
          {
            "id": "warehouse.edit",
            "name": "Edit warehouses",
            "action": "wms:warehouse.edit"
          },
          {
            "id": "inbound.ship",
            "name": "Ship inbound orders",
            "action": "wms:inbound.ship",
            "resource": "inbound/{id}"
          }
        ]
      }
    ]
  },
  "roles": [
    {
      "id": "clerk",
      "name": "Clerk",
      "description": "Looks things up",
      "permissions": [
        "warehouse.read"
      ]
    },
    {
      "id": "manager",
      "name": "Manager",
      "permissions": [
        "warehouse.read",
        "warehouse.edit",
        "inbound.ship"
      ]
    },
    {
      "id": "admin",
      "name": "Administrator",
      "permissions": "*"
    },
    {
      "id": "draft",
      "name": "To be set up",
      "permissions": []
    }
  ]
}

租户 acme 有两个自定义角色:

json
{
  "schema": 2,
  "revision": 1,
  "publishedAt": "2026-11-01T08:00:00Z",
  "roles": [
    {
      "id": "night-shift",
      "app": "wms",
      "name": "Night shift",
      "description": "Watches the dock",
      "permissions": [
        "warehouse.read"
      ]
    },
    {
      "id": "dispatcher",
      "app": "wms",
      "name": "Dispatcher",
      "permissions": [
        "warehouse.read",
        "inbound.ship"
      ]
    }
  ]
}

两者都写成角色,而不是绑定:CoSec 读取文档时为每个权限派生一条绑定,所以控制台的“权限 x/y”就是角色 permissions 的长度,保存一个角色是改一个条目,而不是每个权限一条绑定。

验证 ​

wiki/examples/roles 里的场景在每次构建时运行:

令牌请求结果
acme,角色 night-shift读取仓库Permit
acme,角色 night-shift编辑仓库Deny:角色只列了 warehouse.read
globex,角色 night-shift读取仓库Deny:night-shift 是 acme 文档里的角色
globex,系统角色 manager编辑仓库Permit:系统角色对每个租户生效
acme,角色 draft读取仓库Deny:没有权限的角色什么也不授予
acme,自定义角色 dispatcher发出入库单 42Permit

控制台流程 ​

控制台的每个动作都是租户文档上的一次变更,基于控制台显示的那个修订提出。调用的是控制 API;租户的主体只能访问自己的租户文档。

控制台动作操作
新建角色一条 putRole,permissions: []
另存为一条 putRole,新 id,复制权限列表
保存权限矩阵一条替换该角色的 putRole,可带 ifMatch
删除角色一条 removeRole

前三种的请求体:

json
{
  "document": "tenant:acme",
  "base": 1,
  "note": "New role: Receiver",
  "operations": [
    {
      "op": "putRole",
      "role": {
        "id": "receiver",
        "app": "wms",
        "name": "Receiver",
        "permissions": []
      }
    }
  ]
}
json
{
  "document": "tenant:acme",
  "base": 2,
  "note": "Save night-shift as night-shift-2",
  "operations": [
    {
      "op": "putRole",
      "role": {
        "id": "night-shift-2",
        "app": "wms",
        "name": "Night shift (copy)",
        "description": "Watches the dock",
        "permissions": [
          "warehouse.read"
        ]
      }
    }
  ]
}
json
{
  "document": "tenant:acme",
  "base": 3,
  "note": "Night shift may ship",
  "operations": [
    {
      "op": "putRole",
      "role": {
        "id": "night-shift",
        "app": "wms",
        "name": "Night shift",
        "description": "Watches the dock",
        "permissions": [
          "warehouse.read",
          "inbound.ship"
        ]
      }
    }
  ]
}

putRole 整体替换角色,所以重放是无害的。ifMatch 是角色条目按文档写法计算的 SHA-256;有人先改了这个角色时,保存会以 412 失败。base 不再是文档当前修订则是 409:重新加载后再试。

一次保存是三次调用。$CONTROL 是控制 API 的基础路径(https://host/cosec/control/v1),令牌属于租户的管理员,并为控制面的应用签发(之后修改文档):

bash
ID=$(curl -s -X POST "$CONTROL/changes" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d @save-permissions.json | jq -r .id)
curl -s -X POST "$CONTROL/changes/$ID:skip-shadow" -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"reason":"role matrix saved in the console"}'
curl -s -X POST "$CONTROL/changes/$ID:publish" -H "Authorization: Bearer $TOKEN"
  1. **提出。**控制面校验并编译变更,并分析它授出了什么:管理员自己不持有的权限是一次升级,NOT_COVERED 会指出那个权限。变更列出它的 findings 和 escalations,控制台可以把它们显示在矩阵旁边。
  2. 跳过窗口。cosec:tenant-admin 可以在自己租户的文档上跳过影子窗口,控制台保存时应该这样做:窗口要等流量来比较。跳过窗口不改变审批。
  3. **发布。**没有升级时变更直接发布;有升级时等待审批人(cosec:approve),除非你做了委派,审批人是平台主体(审批)。

管理员能授出什么取决于他们自己持有什么(授权者需要什么):被允许写角色并在租户各作用域授权的权限配置员不需要审批人,而空间管理员只限于自己的空间。租户所有者通过附加策略持有一切:租户所有者。

目录和系统角色在应用文档里,租户的主体无法通过控制 API 读取,所以目前由控制台后端用平台主体列出它们。

**下一步:**空间里的角色,见使用空间的应用。

基于 Apache License 2.0 发布