matchers: query now ANDs multiple keys (#6054)

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
This commit is contained in:
Aziz Rmadi 2024-01-21 20:36:44 -06:00 committed by GitHub
parent c0273f1f04
commit ed7e3c906a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 56 additions and 7 deletions

View file

@ -33,6 +33,7 @@ import (
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"golang.org/x/exp/slices"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@ -789,6 +790,12 @@ func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// Match returns true if r matches m. An empty m matches an empty query string.
func (m MatchQuery) Match(r *http.Request) bool {
// If no query keys are configured, this only
// matches an empty query string.
if len(m) == 0 {
return len(r.URL.Query()) == 0
}
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
// parse query string just once, for efficiency
@ -806,19 +813,25 @@ func (m MatchQuery) Match(r *http.Request) bool {
return false
}
// Count the amount of matched keys, to ensure we AND
// between all configured query keys; all keys must
// match at least one value.
matchedKeys := 0
for param, vals := range m {
param = repl.ReplaceAll(param, "")
paramVal, found := parsed[param]
if found {
for _, v := range vals {
v = repl.ReplaceAll(v, "")
if paramVal[0] == v || v == "*" {
return true
}
if !found {
return false
}
for _, v := range vals {
v = repl.ReplaceAll(v, "")
if slices.Contains(paramVal, v) || v == "*" {
matchedKeys++
break
}
}
}
return len(m) == 0 && len(r.URL.Query()) == 0
return matchedKeys == len(m)
}
// CELLibrary produces options that expose this matcher for use in CEL

View file

@ -763,6 +763,42 @@ func TestQueryMatcher(t *testing.T) {
input: "/?somekey=1",
expect: true,
},
{
scenario: "do not match when not all query params are present",
match: MatchQuery{"debug": []string{"1"}, "foo": []string{"bar"}},
input: "/?debug=1",
expect: false,
},
{
scenario: "match when all query params are present",
match: MatchQuery{"debug": []string{"1"}, "foo": []string{"bar"}},
input: "/?debug=1&foo=bar",
expect: true,
},
{
scenario: "do not match when the value of a query param does not match",
match: MatchQuery{"debug": []string{"1"}, "foo": []string{"bar"}},
input: "/?debug=2&foo=bar",
expect: false,
},
{
scenario: "do not match when all the values the query params do not match",
match: MatchQuery{"debug": []string{"1"}, "foo": []string{"bar"}},
input: "/?debug=2&foo=baz",
expect: false,
},
{
scenario: "match against two values for the same key",
match: MatchQuery{"debug": []string{"1"}},
input: "/?debug=1&debug=2",
expect: true,
},
{
scenario: "match against two values for the same key",
match: MatchQuery{"debug": []string{"2", "1"}},
input: "/?debug=2&debug=1",
expect: true,
},
} {
u, _ := url.Parse(tc.input)