From 1881f279283c9cbabbd0a1cc0bd0d2b4c8fe202f Mon Sep 17 00:00:00 2001 From: William Petit Date: Wed, 26 Jun 2024 13:52:49 +0200 Subject: [PATCH] refactor: use new rule engine package --- doc/fr/references/layers/authn/README.md | 18 ++- internal/proxy/director/layer/authn/rules.go | 119 +++++-------------- internal/rule/engine.go | 1 - 3 files changed, 48 insertions(+), 90 deletions(-) diff --git a/doc/fr/references/layers/authn/README.md b/doc/fr/references/layers/authn/README.md index fa3d152..e63e835 100644 --- a/doc/fr/references/layers/authn/README.md +++ b/doc/fr/references/layers/authn/README.md @@ -36,16 +36,28 @@ Le comportement des règles par défaut est le suivant: Interdire l'accès à l'utilisateur. -#### `set_header(name string, value string)` +##### `add_header(name string, value string)` -Définir la valeur d'une entête HTTP via son nom `name` et sa valeur `value`. +Ajouter une valeur à un entête HTTP via son nom `name` et sa valeur `value`. -#### `del_headers(pattern string)` +##### `set_header(name string, value string)` + +Définir la valeur d'un entête HTTP via son nom `name` et sa valeur `value`. La valeur précédente est écrasée. + +##### `del_headers(pattern string)` Supprimer un ou plusieurs entêtes HTTP dont le nom correspond au patron `pattern`. Le patron est défini par une chaîne comprenant un ou plusieurs caractères `*`, signifiant un ou plusieurs caractères arbitraires. +##### `set_host(host string)` + +Modifier la valeur de l'entête `Host` de la requête. + +##### `set_url(url string)` + +Modifier l'URL du serveur cible. + ### Environnement Les règles ont accès aux variables suivantes pendant leur exécution. diff --git a/internal/proxy/director/layer/authn/rules.go b/internal/proxy/director/layer/authn/rules.go index fbde325..80c4837 100644 --- a/internal/proxy/director/layer/authn/rules.go +++ b/internal/proxy/director/layer/authn/rules.go @@ -1,70 +1,16 @@ package authn import ( - "fmt" "net/http" - "strconv" - "strings" - "time" - "forge.cadoles.com/Cadoles/go-proxy/wildcard" + "forge.cadoles.com/cadoles/bouncer/internal/rule" + ruleHTTP "forge.cadoles.com/cadoles/bouncer/internal/rule/http" "github.com/expr-lang/expr" "github.com/pkg/errors" ) -func (l *Layer) getHeaderRuleOptions(r *http.Request) []expr.Option { - options := make([]expr.Option, 0) - - setHeader := expr.Function( - "set_header", - func(params ...any) (any, error) { - name := params[0].(string) - rawValue := params[1] - - var value string - switch v := rawValue.(type) { - case []string: - value = strings.Join(v, ",") - case time.Time: - value = strconv.FormatInt(v.UTC().Unix(), 10) - case time.Duration: - value = strconv.FormatInt(int64(v.Seconds()), 10) - default: - value = fmt.Sprintf("%v", rawValue) - } - - r.Header.Set(name, value) - - return true, nil - }, - new(func(string, string) bool), - ) - - options = append(options, setHeader) - - delHeaders := expr.Function( - "del_headers", - func(params ...any) (any, error) { - pattern := params[0].(string) - deleted := false - - for key := range r.Header { - if !wildcard.Match(key, pattern) { - continue - } - - r.Header.Del(key) - deleted = true - } - - return deleted, nil - }, - new(func(string) bool), - ) - - options = append(options, delHeaders) - - return options +type Env struct { + User *User `expr:"user"` } func (l *Layer) applyRules(r *http.Request, options *LayerOptions, user *User) error { @@ -73,38 +19,39 @@ func (l *Layer) applyRules(r *http.Request, options *LayerOptions, user *User) e return nil } - env := map[string]any{ - "user": user, + engine, err := rule.NewEngine[*Env]( + rule.WithRules(options.Rules...), + rule.WithExpr(getAuthnAPI()...), + ruleHTTP.WithRequestFuncs(r), + ) + if err != nil { + return errors.WithStack(err) } - rulesOptions := l.getHeaderRuleOptions(r) + env := &Env{ + User: user, + } - var ruleErr error - forbidden := expr.Function( - "forbidden", - func(params ...any) (any, error) { - ruleErr = errors.WithStack(ErrForbidden) - return true, nil - }, - new(func() bool), - ) - - rulesOptions = append(rulesOptions, forbidden) - - for i, r := range rules { - program, err := expr.Compile(r, rulesOptions...) - if err != nil { - return errors.Wrapf(err, "could not compile rule #%d", i) - } - - if _, err := expr.Run(program, env); err != nil { - return errors.Wrapf(err, "could not execute rule #%d", i) - } - - if ruleErr != nil { - return errors.WithStack(ruleErr) - } + if _, err := engine.Apply(env); err != nil { + return errors.WithStack(err) } return nil } + +func getAuthnAPI() []expr.Option { + options := make([]expr.Option, 0) + + // forbidden() allows the layer to hijack the current request and return a 403 Forbidden HTTP status + forbidden := expr.Function( + "forbidden", + func(params ...any) (any, error) { + return true, errors.WithStack(ErrForbidden) + }, + new(func() bool), + ) + + options = append(options, forbidden) + + return options +} diff --git a/internal/rule/engine.go b/internal/rule/engine.go index f23f4d4..d9c3e54 100644 --- a/internal/rule/engine.go +++ b/internal/rule/engine.go @@ -32,7 +32,6 @@ func NewEngine[E any](funcs ...OptionFunc) (*Engine[E], error) { } for i, r := range opts.Rules { - program, err := expr.Compile(r, opts.Expr...) if err != nil { return nil, errors.Wrapf(err, "could not compile rule #%d", i)