3. Rego 语言基础(一):Rego 语法入门、变量、标量、复合值、引用与表达式

Rego 是 OPA 的策略语言,专为在复杂 JSON/YAML 数据中表达断言而设计。它并非通用编程语言,而是一种声明式查询语言。理解 Rego 的语法基础,是编写日志访问控制策略的第一步。本章将带你掌握 Rego 的核心词法单元与基本数据类型。

3.1 Rego 语法入门:声明式与逻辑编程

Rego 的语法深受 Datalog 和 Prolog 影响,其核心思想是:你定义“规则”(Rule),OPA 引擎负责“查询”(Query)并返回结果。在日志访问控制场景中,规则通常描述“谁在什么条件下可以访问哪些日志”。

关键概念: Rego 中没有“if-else”控制流,而是使用“规则体”(Rule Body)中的表达式串联(AND)与并联(OR)来实现逻辑判断。所有规则默认都是“真”的,只有当规则体中的条件全部满足时,规则才被激活。

一个最简单的 Rego 规则结构如下:

# 规则定义
allow = true {                     # 规则头:规则名 = 值
    input.user == "admin"          # 规则体:条件表达式
    input.action == "read"         # 条件之间是 AND 关系
}

# 查询
result = allow with input as {"user": "admin", "action": "read"}
# 输出: result = true

3.2 变量(Variables)

Rego 中的变量以大写字母或下划线开头。变量用于绑定数据,并在规则体内传递值。变量一旦绑定,在同一个规则体内不可重新赋值(不可变)。

特性 说明 示例
命名规则 必须以大写字母或下划线开头 User, _role, X
不可变性 变量一旦赋值,在规则体内不能修改 X := 1 后不能再 X := 2
匿名变量 使用 _ 忽略不需要的值 data.users[_].name
作用域 变量在定义它的规则体内可见 不同规则间的变量不共享
避坑指南: 初学者常犯的错误是使用小写字母开头定义变量(如 user)。在 Rego 中,小写开头的标识符会被解析为引用(Reference),而非变量,这会导致意外的查询结果。

3.3 标量(Scalars)与复合值(Composite Values)

Rego 支持所有常见的 JSON 数据类型,并在此基础上扩展了集合(Set)。

3.3.1 标量类型

类型 示例 说明
字符串 "admin", "log-reader" 双引号包裹,支持转义
数字 42, 3.14 整数和浮点数
布尔值 true, false 逻辑真/假
空值 null 表示无值

3.3.2 复合值类型

类型 语法 示例 说明
对象(Object) {} {"name": "alice", "role": "admin"} 键值对,键必须是字符串
数组(Array) [] ["read", "write"] 有序列表,索引从0开始
集合(Set) {} 且元素唯一 {"admin", "editor"} 无序、无重复元素的集合
注意: 集合(Set)与对象(Object)都使用花括号 {}。区别在于:集合的元素是单一值(如字符串、数字),而对象的元素是键值对。Rego 引擎会根据上下文自动区分。

3.4 引用(References)

引用用于访问嵌套在对象或数组中的数据。引用使用点号(.)或方括号([])进行导航。

  • 点号访问: input.user.name 等价于 input["user"]["name"]
  • 方括号访问: 支持变量作为键,如 input[Key]
  • 数组索引: data.logs[0] 访问第一个日志条目
  • 迭代访问: 使用变量遍历数组或集合,如 data.logs[i]
# 示例数据
input := {
    "user": "bob",
    "action": "delete",
    "target": {
        "namespace": "production",
        "resource": "pod"
    }
}

# 引用示例
user_name := input.user                     # "bob"
namespace := input.target.namespace         # "production"
action := input["action"]                   # "delete"

# 使用变量作为键
Key := "target"
target_obj := input[Key]                    # {"namespace": "production", "resource": "pod"}

3.5 表达式(Expressions)

表达式是规则体的基本组成单元。每个表达式必须求值为 true,规则体才成立。常见的表达式类型包括:

表达式类型 运算符/语法 示例
比较 ==, !=, <, >, <=, >= input.role == "admin"
成员关系 in, 数组/集合成员检查 "read" in input.actions
赋值 := (局部变量), = (统一) X := 10, X = 10
否定 not not input.user == "guest"
逻辑与 同一规则体中的多个表达式(隐式 AND) a > 1; b < 5
逻辑或 多个同名规则体(隐式 OR) allow { ... } 和 allow { ... }
重要区别: := 用于赋值,将右侧值绑定到左侧变量;= 用于统一,它既可以是赋值,也可以是等式检查,具体取决于变量是否已绑定。在策略编写中,推荐优先使用 := 以避免歧义。

3.6 知识结构图:Rego 基础语法全景

下图展示了 Rego 基础语法中各元素之间的关系,以及它们在日志访问控制策略中的典型应用位置。

Rego 基础语法全景图 Rego 基础语法全景图 Rego 策略规则 变量 (Variables) 标量 (Scalars) 复合值 (Composites) 引用 (References) 大写开头 不可变性 匿名变量 _ 字符串 数字 布尔/null 对象 {} 数组 [] 集合 {} 点号 . 方括号 [] 变量键 表达式 (Expressions) 比较 | 成员关系 | 赋值 := | 否定 not | 隐式 AND/OR

3.7 实战示例:日志访问控制中的基础语法

将上述语法应用于一个简单的日志访问控制场景:只允许 admin 角色的用户读取 production 命名空间的日志。

# 输入数据
input := {
    "user": "alice",
    "role": "admin",
    "action": "read",
    "log": {
        "namespace": "production",
        "level": "error",
        "message": "Out of memory"
    }
}

# 策略规则
default allow = false

allow = true {
    input.role == "admin"                    # 比较表达式
    input.action == "read"                   # 比较表达式
    input.log.namespace == "production"      # 引用 + 比较
}

# 更灵活的版本:使用变量和成员关系
allowed_roles := {"admin", "supervisor"}     # 集合
allowed_namespaces := ["production", "staging"]  # 数组

allow = true {
    Role := input.role                       # 变量赋值
    Role in allowed_roles                    # 成员关系表达式
    input.action == "read"
    input.log.namespace in allowed_namespaces # 成员关系表达式
}
实践要点: 在编写日志访问控制策略时,建议将角色、命名空间等条件抽象为变量或集合,便于维护和复用。使用 in 表达式可以显著简化多值匹配逻辑。