rbac

package
v0.26.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 14, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package rbac 角色能力矩阵机制:角色→能力集查表(Can)、只读档由矩阵推导、 授权面指纹(Snapshot)、「矩阵不得装饰化」的 lint 骨架。

只收机制,词表是宿主数据

本包**不预设任何具体角色名或能力词**——"admin"、"platform.read"、 "session.write" 这些是宿主领域词汇,经 Matrix 与 writeFace 词表注入。 这条边界是下沉提案 #175 (e) 的核心风险对面:词表一旦长进机制里,包就 落成某一个宿主的专用件。本包的类型与方法对任何词表形状同等工作。

与 permgate 的分工边界(角色面 vs 调用面)

同仓的 permgate 与本包是**权限判定的两半**,不可互相替代:

  • permgate 裁「**这次调用**要不要人确认」——调用面。输入是一次具体的 工具调用(工具名 + 输入内容 + 会话模式 + 规则链),输出 allow/ask/deny, 关心的是交互(弹窗/会话钥匙/白名单 break-glass)。
  • rbac 裁「**这个角色**有没有这个能力」——角色面。输入是身份(角色) 与能力词,输出是静态布尔,关心的是授权真相的单源(矩阵)。

典型组合:handler 侧 requirePerm 用 rbac.Can 做能力预判(角色没这个能力 连工具都不该调到);permgate 管调到了之后的确认链(这次调用要不要人)。 角色面通过的调用仍可能被调用面拦下(permgate 的 ask/deny);反过来, permgate 放行不构成角色授权——两套判定各管各的真相。

fail-closed 缺省

  • Can:未声明角色/能力词 → false(矩阵外没有能力)。
  • ReadOnly:写面词表 nil/空 → 无法证明任何能力是读面 → 一律推导为 只读(写面全禁)——漏声明得到的是「全禁」,不是「全放」。不在矩阵 里的角色同理(能力集为空 = 无写面 = 只读档)。

踩坑纪律(#175 (d):随机制一起下沉的两条)

  • **只读档由矩阵推导,不硬编码角色名**:`role == "viewer"` 式硬编码在 矩阵演进(新增只读角色、调整能力词)时静默失真——声明面改了、执行面 没跟上,授权真相从此分叉。判据恒为「能力集不含写面能力」。
  • **矩阵不得装饰化**:矩阵里每条能力词必须有执行面消费点(requirePerm / Can 调用点),否则矩阵退化成「看起来有授权体系」的装饰品——多写的 能力词没人拦、删掉的能力词没人发现。lint 骨架(Matrix.Lint)落实 三条判据:每条能力词有消费点、角色词表闭合、矩阵单调(高层 ⊇ 低层); 消费点回调与角色词表由宿主注入。

并发契约

Matrix 是宿主持有的声明面数据:本包所有方法只读入参、无共享可变状态, 并发安全。矩阵变更属装配期动作——运行期热更请整体替换 Matrix 值(与 permgate.Service.Update 的换值纪律同款),不得原地改切片元素。

零第三方依赖(纯 stdlib + slices)。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Finding

type Finding struct {
	Kind   FindingKind
	Role   string
	Perm   string
	Detail string
}

Finding 一次 lint 的疑点报告。Role/Perm 视疑点类型可空: FindingUnconsumedPerm 填 Perm(Role 为首个授予它的角色,作定位线索)、 FindingUnknownRole 填 Role、FindingNonMonotonic 填 Role(高层)+ Perm。

type FindingKind

type FindingKind string

FindingKind lint 疑点类型。

const (
	// FindingUnconsumedPerm 能力词在矩阵中声明,但宿主报告执行面无消费点
	// (或消费点回调未注入——无法核实视同无消费点,fail-loud:lint 不静默
	// 放过任何一条能力词,「矩阵不得装饰化」判据的死法就是沉默)。
	FindingUnconsumedPerm FindingKind = "perm.unconsumed"
	// FindingUnknownRole 矩阵角色不在宿主声明的角色词表内(词表闭合破坏)
	// ——十有八九是拼写错造出的新角色:它查得到、授权面却分叉了。
	FindingUnknownRole FindingKind = "role.unknown"
	// FindingNonMonotonic 角色层级单调性破坏:高层角色的能力集缺低层角色的
	// 能力(矩阵单调:高层 ⊇ 低层)。单调破坏的典型成因是往低层加了新能力
	// 词而忘了同步高层——高层身份反而比低层少权限。
	FindingNonMonotonic FindingKind = "matrix.nonmonotonic"
)

type LintOptions

type LintOptions struct {
	// ConsumedBy 能力词消费点回调:宿主报告 perm 在执行面是否有真实消费点
	// (requirePerm/Can 调用点注册表、handler 门禁清单……由宿主自持)。
	// nil ⇒ 无法核实 ⇒ **每条**能力词报 FindingUnconsumedPerm(fail-loud,
	// 见该常量注释)。回调可能被逐词调用多次,应轻量;判定口径由宿主定
	// (「至少一处消费点」是推荐语义)。
	ConsumedBy func(perm string) bool
	// Roles 声明面的角色词表全集:矩阵中出现的角色必须都在其中(词表闭合)。
	// len==0 视为未提供,闭合判据跳过(宿主没有角色词表时不要硬凑空表)。
	Roles []string
	// Order 角色层级,从高到低(如 admin, user, viewer):单调性判据用——
	// 相邻两层做包含核对(高层 ⊇ 低层),由传递性覆盖全链。len==0 视为
	// 未提供,单调判据跳过(无层级语义的扁平角色表不需要它)。层级里声明
	// 而矩阵未列的角色按空能力集参与核对(高层缺位即缺能力)。
	Order []string
}

LintOptions lint 骨架的宿主注入面(guardx#175:判据是机制的,事实是 宿主的——消费点在哪、角色词表长什么样,本包无从知晓)。

type Matrix

type Matrix map[string][]string

Matrix 角色→能力集矩阵(guardx#175):宿主注入的声明面数据。键与值都是 宿主领域词汇(角色名、能力词),本包不预设、不归一(大小写敏感、原样 比对——词表的拼写纪律归宿主的词表闭合 lint 管)。

同一角色能力集内的重复词无语义(Can 是存在性判定,Snapshot 会去重); 但建议宿主保持声明面整洁——矩阵是要被审计与指纹化的。

func (Matrix) Can

func (m Matrix) Can(role, perm string) bool

Can 查表:role 的能力集是否含 perm。

fail-closed:矩阵 nil、角色未声明、能力词未授予,一律 false——矩阵外 没有能力(包文档「fail-closed 缺省」)。不存在「默认角色」或前缀匹配: 能力词是封闭词表,词表外的字面量查不到就是查不到。

func (Matrix) Lint

func (m Matrix) Lint(opts LintOptions) []Finding

Lint 「矩阵不得装饰化」lint 骨架(guardx#175):对声明面跑三条判据, 逐条产出 Finding(不 panic、不修改矩阵;返回 nil = 所提供的判据全过)。

输出顺序确定(按角色/能力词字典序),供快照比对与 CI 断言;宿主接自己的 告警/门禁面(测试里 t.Fatal、启动时日志、CI 里指纹比对皆可)。三条判据 相互独立:没提供的输入跳过对应判据(文档见 LintOptions 字段)——但 ConsumedBy 未提供不会跳过,而是逐词报疑点:装饰化判据的失败模式必须是 响亮,不能是安静。

func (Matrix) ReadOnly

func (m Matrix) ReadOnly(role string, writeFace map[string]bool) bool

ReadOnly 报告 role 是否只读档:**由矩阵推导**(guardx#175 的踩坑纪律)—— 能力集内不含任何写面能力即只读,不硬编码 `role == "viewer"`(矩阵演进时 硬编码静默失真,见包文档)。

writeFace 写面能力词集合(改动状态/施加副作用的能力词)。判据方向刻意 声明**写面**而不是读面:nil/空集合 ⇒ 无法证明任何能力是读面 ⇒ 一律推导 为只读(写面全禁)——漏声明得到的是「全禁」,不是「全放」(fail-closed, 与 Can 的矩阵外无能力同一方向)。不在矩阵里的角色能力集为空 ⇒ 只读档。

推导结果的使用方(宿主把只读角色钉进只读档/写面全禁)即便误判也仍有 Can 这层兜底:ReadOnly=false 的角色对未授予的写能力词照样 Can=false。 本判定是 belt,矩阵是 suspenders——两层都取保守侧。

func (Matrix) Snapshot

func (m Matrix) Snapshot() string

Snapshot 授权面指纹:矩阵的**确定性**序列化——角色按字典序,每行 "role=perm1,perm2"(行内能力词去重 + 字典序,逗号连接,LF 换行)。

用途(#175 (b):policyver 类消费):对本串做哈希即得授权面指纹——矩阵 声明的任何增删改都会改变指纹,授权面漂移因此可被指纹比对捕获。序列化 是规范的(同矩阵恒同串),不是实现细节;跨版本依赖它请钉住本方法文档。

nil/空矩阵返回空串(指纹意义上的「空授权面」)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL