package authn import ( "fmt" "net/http" "strconv" "strings" "time" "forge.cadoles.com/Cadoles/go-proxy/wildcard" "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 } func (l *Layer) applyRules(r *http.Request, options *LayerOptions, user *User) error { rules := options.Rules if len(rules) == 0 { return nil } env := map[string]any{ "user": user, } rulesOptions := l.getHeaderRuleOptions(r) 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) } } return nil }