修改规则参数的唯一入口
运行时改规则参数只经一个写入入口,且只接受已批准的变更单
运行时改规则参数只有一个写入入口,它只接受已批准的变更单,键、值、生效时间都取自单上冻结的内容。模型没有另开的通道:它的输出只能成为待审批的草案,规则参数也只经这个入口改。高频的业务单据录入、流水追加有各自的命令与授权判定,不经变更单。
哪类制度数据走哪条路
| 制度数据 | 怎样改 |
|---|---|
| 规则参数的值:时限、提醒起点、开关等 | 变更单:提议、检查、批准,由写入入口追加,只做校验 |
| 可编辑规则定义:新增分支等 | 变更单,另先做演习 |
| 金额档位、审批单型注册表 | 随版本发布追加新行(新的档位版本、更晚的生效时间),旧行由触发器拒改拒删;不经变更单 |
下面讲第一、二行。第三行的行为见审批流转与规则写成带版本的数据。
为什么入口只收单号
常见的想法是:给改规则加一道审批,就算受控了。这还不够。审批和写入若是两件事,调用方可以拿一张批准的单,去写另一组值,批的和写的对不上。
SZV 的写入入口只收一个单号。键、新值、生效时间都从那张单冻结的意图里读,调用方没有机会自报。每一次规则改动,因此都对应一张已批准的单。
提议 检查 审批 追加
图里只有最后一步能碰规则表,进入它的只有一张已批准的单。
| 步 | 做什么 | 守住什么 |
|---|---|---|
| 提议 | 有资格的发起人写下键、新值、期望生效时间、理由 | 提议人有记录 |
| 检查 | 纯函数判定:键是否已登记;类型与范围是否合法;与当前值是否不同;是否违反次序约束 | 不合格当场拒绝,不产生审批单,不留批不过的废单 |
| 建单 | 冻结提议内容 | 之后的审批与写入,都对着这份冻结内容 |
| 审批 | 最高层级一签,发起人不占签核位;驳回即终态,规则表不动 | 提议人不能自批 |
| 终局 | 决审时刻,批准后执行写入的那一步按同一份检查再算一遍 | 提议时合规、批准时已不合规的改动进不了表 |
| 追加 | 写入入口追加一行;生效时间取期望时间与决审时刻中较晚者,不倒填;触发器写历史:前一行、新行、变更人、理由 | 旧行保留,每次改动的来龙去脉可查 |
检查要做两次,是因为从提议到批准之间,别的规则可能已经改了。比如提议时「自动通过线低于复核线」成立,等批准时另一条线被先改动,这条提议再写入就会违反次序。第二次检查把它挡在表外。
谁有写权
| 角色 | 对规则表 |
|---|---|
| 业务角色 | 只读 |
| 调用写入入口的角色 | 只有执行写入入口函数的权限,不继承其他权限 |
| 写入入口函数 | 以拥有数据的角色身份执行;只接受已批准的单;同一张单重复执行,返回已写入的那一行 |
这是 PostgreSQL 的定义者函数(SECURITY DEFINER):函数以定义者的权限运行,调用者只需要执行权。PostgreSQL 文档对这类函数的通用要求有两条:固定 search_path,把 pg_temp 放在最后,免得被同名的临时对象顶替;新建函数默认对 PUBLIC 开放执行权,要在创建的同一事务里收回。
核对「谁能改规则」时,看数据库里的账号权限与这一个函数即可,不必通读应用代码。
两类改动 检查不同
参数改值,如时限、提醒起点、开关,只做校验:只读判定,不写表。
可编辑规则定义,如新增一个分支,另做演习:在子事务里真写一遍,再整笔回滚,把拒因提前到建单时。演习证明「写得进去」,不证明「改了之后业务会怎样」。
提议与批准分开 但批准只有一个人
发起人不占签核位,提议与批准因此分在两个人手里。这是职责分离里最小的一种形态,对应 NIST SP 800-53 的 AC-5 控制项,授权的一面见谁能做什么。
批准只由最高层级一人签。这比要两位批准人的双人复核(maker-checker)简单,代价是批准人看漏时没有第二道人为的检查,检查靠上一节那份纯函数补上。哪些改动要第二位批准人,应在设计阶段与企业定下。
限制与不适用
- 改规则变慢,批准之前不生效。
- 批准人是最高层级的一个人,没有第二道人为的检查。
- 演习只证明写得进去,不证明改后业务怎样。
- 写入入口防业务账号与应用代码的误写,不防拥有数据库高权限的人。
- 高频的业务单据录入、流水追加不适用变更单。
与常见做法对照
策略即代码是业界常见的取向:策略写成文件,改动走审查与发布。以 OPA 为例,策略与数据打成 bundle 下发,bundle 带 revision,更新时不必重启(OPA 文档)。这类做法通常把审查放在代码仓库,写入入口是发布流程。
SZV 的规则在数据库里,改动面向财务负责人这样的业务角色,不是写文件的人,所以审查放在变更单上,写入入口放在数据库函数里。对应的限度也不同:写入入口防的是业务账号与应用代码的误写,防不了拥有数据库高权限的人。后一类风险属于安全边界页的范围,见安全边界。
其他做法
| 做法 | 它解决什么 | 为什么不用 |
|---|---|---|
| 调用方带着键与值直接写,审批只是旁路的一张单 | 接口简单 | 批的和写的可以不是一回事,事后无从对上 |
| 只靠应用代码自觉不写,不在库层收权 | 不用建角色与函数 | 绕开应用直连数据库,照样改得了 |
| 批准后直接写,不再检查 | 少算一遍 | 提议到批准之间规则已变,提议时合规的改动批准时可能违规 |
验收时可以问的问题
- 请演示:业务角色直接改规则表,会被拒绝。
- 请演示:发起人能否批准自己提的那张变更单。
- 请现场查一条规则的改动历史,确认前一行、新行、变更人、理由都在。
- 哪些改动需要第二位批准人,请在设计阶段与 SZV 约定。
相关页
- 修改一条规则:核心经营系统里改规则的操作与界面。
- 规则写成带版本的数据:被改的那些行长什么样。
- 重放一张旧单的判定:改规则之后,旧单为什么不受影响。
- 谁能做什么:谁有资格提议、谁有资格批准。