mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-29 17:58:15 +08:00
* feat: library for building predicate parsers Took some things I learned while building Login Rules and implemented a library that can be used to quickly implement new and improved predicate parsers. Copied from the package comment: typical (TYPed predICAte Library) is a library for building better predicate expression parsers faster. It is built on top of [predicate](github.com/gravitational/predicate). typical helps you (and forces you) to separate parse and evaluation into two distinct stages so that parsing can be cached and evaluation can be fast. Expressions can also be parsed to check their syntax without needing to evaluate them with some specific input. Functions can be defined by providing implementations accepting and returning values of ordinary types, the library handles the details that enable any function argument to be a subexpression that evaluates to the correct type. By default, typical provides strong type checking at parse time without any reflection during evaluation. If you define a function that evaluates to type any, you are opting in to evaluation-time type checking everywhere that function is called, but types will still be checked for all other parts of the expression. If you define a function that accepts an interface type it will be called via reflection during evaluation, but interface satisfaction is still checked at parse time. * fix typo in error message Co-authored-by: Tim Buckley <tim@goteleport.com> --------- Co-authored-by: Tim Buckley <tim@goteleport.com>
97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
// Copyright 2023 Gravitational, Inc
|
|
//
|
|
// 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.
|
|
|
|
package typical_test
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
"github.com/gravitational/teleport/lib/utils/typical"
|
|
)
|
|
|
|
type expressionEnv struct {
|
|
traits map[string][]string
|
|
labels func(key string) string
|
|
}
|
|
|
|
func Example() {
|
|
parser, err := typical.NewParser[expressionEnv, bool](typical.ParserSpec{
|
|
Variables: map[string]typical.Variable{
|
|
"traits": typical.DynamicVariable(func(e expressionEnv) (map[string][]string, error) {
|
|
return e.traits, nil
|
|
}),
|
|
"labels": typical.DynamicMapFunction(func(e expressionEnv, key string) (string, error) {
|
|
return e.labels(key), nil
|
|
}),
|
|
"true": true,
|
|
"false": false,
|
|
},
|
|
Functions: map[string]typical.Function{
|
|
"contains": typical.BinaryFunction[expressionEnv](func(list []string, item string) (bool, error) {
|
|
return slices.Contains(list, item), nil
|
|
}),
|
|
"contains_all": typical.BinaryVariadicFunction[expressionEnv](func(list []string, strs ...string) (bool, error) {
|
|
for _, str := range strs {
|
|
if !slices.Contains(list, str) {
|
|
return false, nil
|
|
}
|
|
}
|
|
return true, nil
|
|
}),
|
|
},
|
|
})
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
env := expressionEnv{
|
|
traits: map[string][]string{
|
|
"groups": {"devs", "security"},
|
|
},
|
|
labels: func(key string) string {
|
|
if key == "owner" {
|
|
return "devs"
|
|
}
|
|
return ""
|
|
},
|
|
}
|
|
|
|
for _, expr := range []string{
|
|
`contains(traits["groups"], labels["owner"])`,
|
|
`contains_all(traits["groups"], "devs", "admins")`,
|
|
`contains(traits["groups"], false)`,
|
|
} {
|
|
parsed, err := parser.Parse(expr)
|
|
if err != nil {
|
|
fmt.Println("parse error:", err)
|
|
continue
|
|
}
|
|
match, err := parsed.Evaluate(env)
|
|
if err != nil {
|
|
fmt.Println("evaluation error:", err)
|
|
continue
|
|
}
|
|
fmt.Println(match)
|
|
}
|
|
// Output:
|
|
// true
|
|
// false
|
|
// parse error: parsing expression
|
|
// parsing second argument to (contains)
|
|
// expected type string, got value (false) with type (bool)
|
|
}
|