如何将Cerbos条件AST转换为Ucast CompoundCondition?
实现Cerbos queryPlanner AST到ucast CompoundCondition的转换
核心转换函数实现
要完成Cerbos AST到ucast条件对象的转换,编写一个递归处理AST节点的函数即可,针对复合条件(and/or)和字段比较条件分别处理:
import { CompoundCondition, FieldCondition } from '@ucast/core'; // 定义Cerbos AST的类型(TypeScript可选) type CerbosAstOperand = { name: string } | { value: any }; type CerbosAstNode = { operator: string; operands: CerbosAstOperand[] | CerbosAstNode[]; }; function cerbosAstToUcast(ast: CerbosAstNode, resourceAlias = 'R'): CompoundCondition | FieldCondition { // 处理and/or复合条件,递归转换子节点 if (['and', 'or'].includes(ast.operator)) { const childConditions = (ast.operands as CerbosAstNode[]).map(node => cerbosAstToUcast(node, resourceAlias)); return new CompoundCondition(ast.operator, childConditions); } // 处理字段比较条件(gt/eq/in等) const [left, right] = ast.operands as CerbosAstOperand[]; let field: string; let value: any; // 区分字段和值,处理in操作的顺序反转(Cerbos中in的operands是[value, name]) if ('name' in left) { field = left.name.replace('request.resource.attr.', `${resourceAlias}.attr.`); value = (right as { value: any }).value; } else { field = (right as { name: string }).name.replace('request.resource.attr.', `${resourceAlias}.attr.`); value = (left as { value: any }).value; } return new FieldCondition(ast.operator as any, field, value); }
函数逻辑说明
- 递归处理复合条件:当AST节点的operator是
and或or时,递归转换每个子operand,生成对应的CompoundCondition。 - 字段路径转换:将Cerbos返回的
request.resource.attr.xxx格式字段名,替换为查询时使用的表别名(如u.attr.xxx)。 - 特殊操作符处理:针对
in操作,Cerbos返回的operands顺序是[value, name],需要反转后匹配ucastFieldCondition的(field, value)参数顺序。 - 字段比较条件生成:对于
gt/eq/lt/ge/le/ne等操作符,直接生成对应的FieldCondition。
转换示例验证
使用你提供的Cerbos AST示例,执行转换后将得到预期的ucast Condition对象:
// 你的Cerbos AST示例 const cerbosAst = { "operator": "and", "operands": [ { "operator": "gt", "operands": [{ "name": "request.resource.attr.foo" }, { "value": 4 }] }, { "operator": "or", "operands": [ { "operator": "eq", "operands": [{ "name": "request.resource.attr.bar" }, { "value": 5 }] }, { "operator": "and", "operands": [ { "operator": "eq", "operands": [{ "name": "request.resource.attr.fizz" }, { "value": 6 }] }, { "operator": "in", "operands": [{ "value": "ZZZ" }, { "name": "request.resource.attr.buzz" }] } ] } ] } ] }; // 转换为ucast Condition const condition = cerbosAstToUcast(cerbosAst, 'u'); // 使用'u'作为表别名
生成TypeORM查询构建器
转换完成后,即可通过@ucast/sql生成TypeORM查询构建器:
import { createConnection } from 'typeorm'; import { interpret } from '@ucast/sql'; import { User } from './entities/User'; async function getAuthorizedUsers() { const conn = await createConnection({ type: 'mysql', database: ':memory:', entities: [User] }); const qb = interpret(condition, conn.createQueryBuilder(User, 'u')); const authorizedUsers = await qb.getMany(); return authorizedUsers; }
注意事项
- 该函数已兼容
and/or/gt/eq/lt/ge/le/ne/in等Cerbos常用操作符,可根据需要扩展支持更多操作符。 - 可根据实际查询场景调整
resourceAlias参数,匹配TypeORM查询时使用的表别名。 - TypeScript环境下,完善类型定义可增强代码的类型安全。
内容的提问来源于stack exchange,提问作者Tal
相关产品推荐
相关产品推荐

