谁能做什么

判断一个人能不能做某件事只问一个入口,拒绝先于允许,答案带依据

系统判断一个人能不能做某件事,只问一个入口:授权数据分在多处,入口只有一处,拒绝先于允许,每个答案都带依据。模型起草的单据走的也是这个入口和同一套审批链,没有为模型另开的通道。授权关系本身是一张图时,比如文件夹层层继承、共享链接一路转发,这套策略行不合适,应改用关系型授权。

难点在顺序与不可授予的组合

常见的理解是「权限是一张表」:谁、对什么、做什么、允许还是拒绝。这样的表确实存在,它只是策略行这一层,存进数据库也不难。

难处首先在判定顺序:一次询问常常同时命中允许和拒绝,顺序决定谁赢。其次在不可授予的组合:某些职位不进签核位,个人授权给不出免审终局。后者属于职责分离,是财务类系统权限设计的真难点。

数据分在多处 判定只在一处

判定入口只回答三个问题:能不能做某个动作,能看哪些分支的数据,能不能签这张单的这个位。它读的数据分在下面几处。

来源存什么在判定里的位置
签核规则表每类单据的每个签核位,谁有资格签签核类动作直接问这里
策略行主体、动作、允许或拒绝、范围、生效时间拒绝行一步,允许行一步
职位能力集每个职位带一组能力装配的起点,也是最后一步兜底
特殊身份、任命职位之外带来的能力,如分支负责人任命装配时叠加
个人授权对某个人单独加一项或减一项装配时最后叠加
可见分支集合全部、一份分支名单或空回答「能看哪些分支」
库层行级隔离按会话里的租户与工作区过滤行在数据库里对每次查询生效

策略行的主体用匹配器表达,有角色、职位、职级下限、任命持有人几种;动作名是点分字符串,支持 a.b.* 这样的通配。主体种类和范围种类各是一张字典表,新增一种,字典加一行,解释器加一个匹配器。

前端不自己算资格。会话下发展开后的能力清单,前端只按清单显示入口,判定都在服务端。

核对权限时,核对一个入口和它读的几处数据即可,不必翻遍各功能的分支。

判定按固定全序走完

一次授权询问的判定全序

一次询问至多走五步,第一个给出结论的步骤就是依据。

  1. 签核类动作先转签核规则表。谁能签由「位」与资格决定,规则见审批流转。
  2. 拒绝行先于其余各步。命中一条拒绝行,结论就是拒绝。
  3. 最高身份全开。这是一条明写的例外;它排在拒绝行之后,所以按这个顺序,命中的拒绝行对它同样生效。
  4. 允许行命中,结论是允许。
  5. 都没命中,看职位能力集:职位自带这项能力就允许,没有就拒绝。

每一步得出结论时,都返回两样东西:哪一步判定的,命中的是哪一行。允许有依据,拒绝也有依据。

拒绝排在允许前面,是因为允许的来源很多:职位、身份、任命、个人授权,而且随组织变化不断增加。拒绝行若排在允许之后,每新加一条允许,都可能绕过一条既有限制。拒绝在前,一条拒绝行写下之后,后来追加多少允许都越不过它。Cedar 出于同样的理由,把 forbid 策略称作 permit 越不过的护栏。

这条保证有一个前提:限制针对的不是签核类动作。签核类动作在第一步就转去签核规则表,不经过拒绝行,相应的限制要写在签核规则里。

审一条限制是否有效,看这条拒绝行本身即可,不必比对全部允许行。

缺了什么都按拒绝处理

默认拒绝落在三处:

缺什么结果
会话里缺能力字段当作空能力,哪个动作都不准
缺可见范围当作空集,一行数据也看不到
规则表里没有命中行拒绝

缺省值选拒绝,配置漏一项的后果是有人做不了事。做不了事的人会来报告,多放出去的权限往往没人发现。

求值出错时怎么办,各家的选择不同。Cedar 跳过出错的策略,在诊断信息里报出它的编号;它没有选「一出错就拒绝」,理由是这种失败模式太剧烈,一条写坏的策略就能拒掉大批正常请求。在拒绝优先的体系里,跳过一条出错的拒绝策略,等于这条限制暂时消失。SZV 新增主体种类时,字典与解释器要同步加,两边是否一致由真库用例钉住。

漏配的代价落在「做不了」一侧,问题会自己暴露出来。

能力在会话开始时装配并冻结

一个人的能力由四层按固定顺序叠加:

顺序层作用
1职位能力集起点
2特殊身份叠加
3任命叠加
4个人授权可加可减;只追加新记录,不改旧记录;每条留痕

装配结果冻结为会话快照,附一个权限集版本号。撤销个人授权时,系统只收回这条授权自己加上的那一项。删掉加项还是写一条减项,由一支纯函数决定,它的决策表与后端实现逐行对拍。

快照换来这些好处:一次会话内答案稳定;判定不必每次重算四层;前端与服务端读同一份清单。代价是撤权与快照之间存在时间差。业界缩短这段时差的通行做法是:签发短期凭证,到期重算;或者把撤销事件推给持有会话的一方,如 RFC 7009 的令牌撤销与 OpenID CAEP 的会话撤销事件。SZV 的快照带权限集版本号。

说清一个人为什么有某项能力,逐层查四层来源即可。

职责分离的约束

两条组合不可授予:

  • 个人授权不能授予免审终局,即单据不经审批链直接通过的能力。
  • 某些职位不进签核位,签核能力无论从哪条路径来都要剥离。

第二条难在路径多。签核能力可以从职位、特殊身份、任命、个人授权四条路径进来,只在个人授权入口设防,另外三条照样能带进来。SZV 对四条路径一律剥离,并在代码里另设常量白名单与扫描守住这条约束。

单靠拒绝行表达不了「某职位不得签」:签核类动作在判定第一步就转去签核规则表,不经过拒绝行。这类约束管的是一个人身上的能力组合,单看哪一条授权都看不出问题。它对应 NIST SP 800-53 的 AC-5「职责分离」控制项。

核对签核资格时,可以直接审「这个职位能否签」,不必追查每一条授权的来路。

行级隔离的前提

行级范围分两层。库层用 PostgreSQL 的行级安全:数据库按策略过滤每次查询能看到的行,条件是会话里的租户与工作区,对表强制开启。应用层是可见分支集合,个人可以单独指定,覆盖职位的默认值。

PostgreSQL 文档写明:表属主通常不受行级安全约束,除非对表设了 FORCE ROW LEVEL SECURITY;超级用户和带 BYPASSRLS 属性的角色始终绕过。所以「查询漏写条件也看不到别家数据」只在一个前提下成立:应用连接数据库的角色不是表属主,也没有 BYPASSRLS。它防的是业务代码漏写过滤条件,不防数据库管理员。

核对行级隔离时,先问应用用哪个角色连库,再看策略写了什么。

限制与不适用

  • 撤权与会话快照之间存在时间差,时差内按旧快照判定。
  • 行级隔离只在应用连接库的角色不是表属主、没有 BYPASSRLS 时成立。
  • 授权关系本身是一张图时,策略行很难表达层层传递,应改用关系型授权。

与常见做法对照

OpenFGA 的概念页把授权分成三类:按角色(RBAC)、按属性(ABAC)、按关系(ReBAC)。SZV 的策略行处在前两类之间:主体按角色、职位、职级下限匹配,范围按种类匹配。

性质SZVCedarOpenFGA
默认结论三种缺省都落到拒绝没有 permit 满足即拒绝找不到关系路径即不允许
冲突时拒绝行先于允许行forbid 压过 permit模型里用 but not 排除
来源返回判定步骤与命中行返回决定性策略;默认拒绝时为空用 Expand 接口展开关系树
适合的形状职位、身份、任命构成的组织权限角色与属性混合的策略文件夹继承、共享链接这类关系图

前三行里,SZV 与 Cedar 的核心性质同构。OPA 的取法相近:Rego 用 default 规则给出缺省结论,决策日志记下每次判定的输入与结果。授权关系一旦是图,比如「能看文件夹就能看其中文件」,策略行很难表达这种层层传递,这时应换成关系型授权。

可以拿审 Cedar 策略的同一套问题来审这里。

其他做法

让每个功能在自己的代码里判断权限。这样写起来最快,问题是:同一条判据会被抄成多份,改一处漏一处;有人问「这个人为什么能批这笔款」时,没有一个地方答得出。SZV 选了唯一入口,另用代码扫描拦截业务代码里的第二份判断。扫描是启发式的,拦得住照抄的判断,证明不了语义上只剩一份。

相关页

这页有帮助吗谢谢,我们会据此改进这一页。

需要帮助?联系我们

想先知道我们怎么想?关于 SZV

联系我们

企业人员规模(人)

试用工作台