ITADN

Adventures in arithmetic

#1058Closeddteleguin 创建于 2026-05-16
D
dteleguincommented
Background: I am trying to integrate CEL Policy (Java) as a policy evaluator for the [Transaction Tokens](https://datatracker.ietf.org/doc/draft-ietf-oauth-transaction-tokens/) [implementation](https://github.com/CarrettiPro/keycloak-tts) in [Keycloak](https://www.keycloak.org/). The example below is taken from the Transaction Tokens draft: ```json "tctx": { "action": "BUY", "ticker": "MSFT", "quantity": 100, "customer_type": { "geo": "US", "level": "VIP" } } ``` The contents of the `tctx` claim can be arbitrary (customer/application specific) and won't be defined by any schema. Therefore, we cannot rely on the Proto schema and the generated class hierarchy - only JSON parsing at runtime. My first attempt was to use Jackson: ```java CelCompiler CEL_COMPILER = CelCompilerFactory.standardCelCompilerBuilder() .addVar("tctx", MapType.create(SimpleType.STRING, SimpleType.ANY)) .build(); var json = """ { "action": "BUY", "ticker": "MSFT", "quantity": 100, "customer_type": { "geo": "US", "level": "FOO" } } """; Map<String, Object> tctx = new ObjectMapper().readValue(json, new TypeReference<>() {}); CelAbstractSyntaxTree ast = CEL_COMPILER.compile("tctx.quantity > 100").getAst(); CelRuntime.Program program = CEL_RUNTIME.createProgram(ast); var result = program.eval(Map.of( "tctx", tctx )); ``` Though CEL was able to understand the data structure, it failed while evaluating `tctx.quantity > 100`: ``` Exception in thread "main" dev.cel.runtime.CelEvaluationException: evaluation error at <input>:14: No matching overload for function '_>_'. Overload candidates: greater_int64 ``` Then it turned out I could not perform any arithmetic with that field at all. After some debugging, I have found the root cause - the Jackson parser converts JSON numbers into Java `Integer`s, and there are no `int32` overloads in CEL. This can be boiled down to the following: ```java CelCompiler CEL_COMPILER = CelCompilerFactory.standardCelCompilerBuilder() .addVar("x", SimpleType.INT) .addVar("a32", ListType.create(SimpleType.INT)) .addVar("a64", ListType.create(SimpleType.INT)) .build(); CelAbstractSyntaxTree ast = CEL_COMPILER.compile("a32[0] > 0").getAst(); // -> No matching overload for function '_>_'. Overload candidates: greater_int64 // CelAbstractSyntaxTree ast = CEL_COMPILER.compile("a64[0] > 0").getAst(); // -> true // CelAbstractSyntaxTree ast = CEL_COMPILER.compile("x > 0").getAst(); // -> true CelRuntime.Program program = CEL_RUNTIME.createProgram(ast); var result = program.eval(Map.of( "a32", List.of(1, 2, 3), "a64", List.of(1L, 2L, 3L), "x", 42 )); ``` Interestingly, an `Integer` value for the variable `x` will be automatically cast to `Long`, but nested `Integer`s (members of `a32`) won't. Nested `Long`s also work fine (as in `a64`). Then, I have decided to switch to Protobuf Value for my JSON, instead of Jackson: ```java Value.Builder builder = Value.newBuilder(); JsonFormat.parser().merge(json, builder); var tctx = builder.build(); ``` This gave a different error: ``` Exception in thread "main" dev.cel.runtime.CelEvaluationException: evaluation error: No matching overload for function '_>=_'. Overload candidates: greater_equals_int64 ``` Turns out that Protobuf converts all JSON numbers to `Double`s, but the right-hand operand in `tctx.quantity > 100` is a `Long` - and again, there's neither automatic casting nor overloads for double vs. int operations. Even a simplest expression like `1.0 > 0` won't work: ``` dev.cel.common.CelValidationException: ERROR: <input>:1:5: found no matching overload for '_>_' applied to '(double, int)' (candidates: (bool, bool),(int, int),(uint, uint),(double, double),(string, string),(bytes, bytes),(google.protobuf.Timestamp, google.protobuf.Timestamp),(google.protobuf.Duration, google.protobuf.Duration)) | 1.0 > 0 ``` While I can mention this double vs. int restriction in the docs, the behavior seems counter-intuitive to me as I haven't seen it in any modern languages; also, this is not observed in CEL-Go. It would be nice if we could make integer and real arithmetic in CEL more seamless and straightforward. Apart from the above - thanks for great software 👏 Looking forward to having CEL in Keycloak in general, not limited to the Transaction Tokens use case.
关闭于 2026-05-20 2 条评论