cmd/json2pubsub/cel.go (164 lines of code) (raw):
package function
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"fmt"
"net"
"reflect"
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/ext"
jwt "github.com/golang-jwt/jwt/v5"
)
type CelLib struct{}
func GetCelEnv() (*cel.Env, error) {
env, err := cel.NewEnv(
ext.Strings(),
ext.Encoders(),
cel.Variable("request", cel.MapType(cel.StringType, cel.DynType)),
cel.Variable("origin", cel.MapType(cel.StringType, cel.DynType)),
cel.Function("ipInRange",
cel.Overload("string_ipInRange_string",
[]*cel.Type{cel.StringType, cel.StringType},
cel.BoolType,
cel.BinaryBinding(func(ipAddr, ipRange ref.Val) ref.Val {
_ipAddr, err := ipAddr.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("IP address is not a string")
}
_ipRange, err := ipRange.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("IP range is not a string")
}
ipAddrParsed := net.ParseIP(_ipAddr.(string))
if ipAddrParsed == nil {
return types.NewErr("Invalid IP address")
}
_, ipRangeParsed, err := net.ParseCIDR(_ipRange.(string))
if err != nil {
return types.NewErr("Invalid IP range")
}
if ipRangeParsed.Contains(ipAddrParsed) {
return types.Bool(true)
}
return types.Bool(false)
},
),
),
),
cel.Function("parseJSON",
cel.Overload("dynmap_parseJSON_string",
[]*cel.Type{cel.StringType},
cel.MapType(cel.StringType, cel.DynType),
cel.UnaryBinding(func(jsonValue ref.Val) ref.Val {
_jsonValue, err := jsonValue.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("JSON value is not a string")
}
var jsonParsed map[string]interface{}
if !json.Valid([]byte(_jsonValue.(string))) {
return types.NewErr("JSON value is not valid")
}
json.Unmarshal([]byte(_jsonValue.(string)), &jsonParsed)
return types.NewStringInterfaceMap(types.DefaultTypeAdapter, jsonParsed)
},
),
),
),
cel.Function("parseJWT",
cel.Overload("string_parseJWT_string",
[]*cel.Type{cel.StringType, cel.StringType},
cel.MapType(cel.StringType, cel.DynType),
cel.BinaryBinding(func(jwtKey, jwtValue ref.Val) ref.Val {
_jwtKey, err := jwtKey.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("JWT key is not a string")
}
_jwtValue, err := jwtValue.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("JWT value is not a string")
}
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(_jwtValue.(string), claims, func(token *jwt.Token) (interface{}, error) {
return []byte(_jwtKey.(string)), nil
}, jwt.WithIssuedAt())
if err != nil {
return types.NewErr("Failed to parse JWT")
}
if !token.Valid {
return types.NewErr("JWT is not valid")
}
claimsOut := make(map[string]interface{}, 0)
for key, val := range claims {
claimsOut[key] = val
}
return types.NewStringInterfaceMap(types.DefaultTypeAdapter, claimsOut)
},
),
),
),
cel.Function("hmacSHA256",
cel.Overload("string_hmacSHA256_string",
[]*cel.Type{cel.StringType, cel.StringType},
cel.StringType,
cel.BinaryBinding(func(hmacKey, hmacValue ref.Val) ref.Val {
_hmacKey, err := hmacKey.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("HMAC key is not a string")
}
_hmacValue, err := hmacValue.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("HMAC value is not a string")
}
mac := hmac.New(sha256.New, []byte(_hmacKey.(string)))
mac.Write([]byte(_hmacValue.(string)))
macSum := mac.Sum(nil)
return types.String(hex.EncodeToString(macSum))
},
),
),
),
cel.Function("hmacSHA1",
cel.Overload("string_hmacSHA1_string",
[]*cel.Type{cel.StringType, cel.StringType},
cel.StringType,
cel.BinaryBinding(func(hmacKey, hmacValue ref.Val) ref.Val {
_hmacKey, err := hmacKey.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("HMAC key is not a string")
}
_hmacValue, err := hmacValue.ConvertToNative(reflect.TypeOf(""))
if err != nil {
return types.NewErr("HMAC value is not a string")
}
mac := hmac.New(sha1.New, []byte(_hmacKey.(string)))
mac.Write([]byte(_hmacValue.(string)))
macSum := mac.Sum(nil)
return types.String(hex.EncodeToString(macSum))
},
),
),
),
)
return env, err
}
func GetCelProgram(env *cel.Env, expr string, mustBeBool bool) (cel.Program, error) {
celAst, celIss := env.Compile(expr)
if celIss.Err() != nil {
return nil, fmt.Errorf("Encountered error when compiling instance CEL: %s\n", celIss.Err())
}
if mustBeBool {
if celAst.OutputType() != cel.BoolType {
return nil, fmt.Errorf("Error compiling CEL, got %v as return value, wanted type bool", celAst.OutputType())
}
}
celPrg, err := env.Program(celAst)
if err != nil {
return nil, fmt.Errorf("Encountered error when processing instance CEL: %s\n", err.Error())
}
return celPrg, nil
}