feat: Implement basic-auth WASM plugin using the Go SDK (#515)

This commit is contained in:
WeixinX
2023-09-18 11:13:08 +08:00
committed by GitHub
parent 8659895a91
commit 54a8a906ae
14 changed files with 1101 additions and 7 deletions

View File

@@ -90,7 +90,8 @@ func (m RuleMatcher[PluginConfig]) GetMatchConfig() (*PluginConfig, error) {
}
func (m *RuleMatcher[PluginConfig]) ParseRuleConfig(config gjson.Result,
parsePluginConfig func(gjson.Result, *PluginConfig) error) error {
parsePluginConfig func(gjson.Result, *PluginConfig) error,
parseOverrideConfig func(gjson.Result, PluginConfig, *PluginConfig) error) error {
var rules []gjson.Result
obj := config.Map()
keyCount := len(obj)
@@ -122,8 +123,15 @@ func (m *RuleMatcher[PluginConfig]) ParseRuleConfig(config gjson.Result,
return fmt.Errorf("parse config failed, no valid rules; global config parse error:%v", globalConfigError)
}
for _, ruleJson := range rules {
var rule RuleConfig[PluginConfig]
err := parsePluginConfig(ruleJson, &rule.config)
var (
rule RuleConfig[PluginConfig]
err error
)
if parseOverrideConfig != nil {
err = parseOverrideConfig(ruleJson, m.globalConfig, &rule.config)
} else {
err = parsePluginConfig(ruleJson, &rule.config)
}
if err != nil {
return err
}

View File

@@ -15,6 +15,7 @@
package matcher
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
@@ -221,7 +222,7 @@ func TestParseRuleConfig(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var actual RuleMatcher[customConfig]
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseConfig)
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseConfig, nil)
if err != nil {
if c.errMsg == "" {
t.Errorf("parse failed: %v", err)
@@ -236,3 +237,96 @@ func TestParseRuleConfig(t *testing.T) {
})
}
}
type completeConfig struct {
// global config
consumers []string
// rule config
allow []string
}
func parseGlobalConfig(json gjson.Result, global *completeConfig) error {
if json.Get("consumers").Exists() && json.Get("allow").Exists() {
return errors.New("consumers and allow should not be configured at the same level")
}
for _, item := range json.Get("consumers").Array() {
global.consumers = append(global.consumers, item.String())
}
return nil
}
func parseOverrideRuleConfig(json gjson.Result, global completeConfig, config *completeConfig) error {
if json.Get("consumers").Exists() && json.Get("allow").Exists() {
return errors.New("consumers and allow should not be configured at the same level")
}
// override config via global
*config = global
for _, item := range json.Get("allow").Array() {
config.allow = append(config.allow, item.String())
}
return nil
}
func TestParseOverrideConfig(t *testing.T) {
cases := []struct {
name string
config string
errMsg string
expected RuleMatcher[completeConfig]
}{
{
name: "override rule config",
config: `{"consumers":["c1","c2","c3"],"_rules_":[{"_match_route_":["r1","r2"],"allow":["c1","c3"]}]}`,
expected: RuleMatcher[completeConfig]{
ruleConfig: []RuleConfig[completeConfig]{
{
category: Route,
routes: map[string]struct{}{
"r1": {},
"r2": {},
},
config: completeConfig{
consumers: []string{"c1", "c2", "c3"},
allow: []string{"c1", "c3"},
},
},
},
globalConfig: completeConfig{
consumers: []string{"c1", "c2", "c3"},
},
hasGlobalConfig: true,
},
},
{
name: "invalid config",
config: `{"consumers":["c1","c2","c3"],"allow":["c1"]}`,
errMsg: "parse config failed, no valid rules; global config parse error:consumers and allow should not be configured at the same level",
},
{
name: "invalid config",
config: `{"_rules_":[{"_match_route_":["r1","r2"],"consumers":["c1","c2"],"allow":["c1"]}]}`,
errMsg: "consumers and allow should not be configured at the same level",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var actual RuleMatcher[completeConfig]
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseGlobalConfig, parseOverrideRuleConfig)
if err != nil {
if c.errMsg == "" {
t.Errorf("parse failed: %v", err)
}
if err.Error() != c.errMsg {
t.Errorf("expect err: %s, actual err: %s", c.errMsg, err.Error())
}
return
}
assert.Equal(t, c.expected, actual)
})
}
}

View File

@@ -44,6 +44,7 @@ type HttpContext interface {
}
type ParseConfigFunc[PluginConfig any] func(json gjson.Result, config *PluginConfig, log Log) error
type ParseRuleConfigFunc[PluginConfig any] func(json gjson.Result, global PluginConfig, config *PluginConfig, log Log) error
type onHttpHeadersFunc[PluginConfig any] func(context HttpContext, config PluginConfig, log Log) types.Action
type onHttpBodyFunc[PluginConfig any] func(context HttpContext, config PluginConfig, body []byte, log Log) types.Action
type onHttpStreamDoneFunc[PluginConfig any] func(context HttpContext, config PluginConfig, log Log)
@@ -54,6 +55,7 @@ type CommonVmCtx[PluginConfig any] struct {
log Log
hasCustomConfig bool
parseConfig ParseConfigFunc[PluginConfig]
parseRuleConfig ParseRuleConfigFunc[PluginConfig]
onHttpRequestHeaders onHttpHeadersFunc[PluginConfig]
onHttpRequestBody onHttpBodyFunc[PluginConfig]
onHttpResponseHeaders onHttpHeadersFunc[PluginConfig]
@@ -73,6 +75,13 @@ func ParseConfigBy[PluginConfig any](f ParseConfigFunc[PluginConfig]) SetPluginF
}
}
func ParseOverrideConfigBy[PluginConfig any](f ParseConfigFunc[PluginConfig], g ParseRuleConfigFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
return func(ctx *CommonVmCtx[PluginConfig]) {
ctx.parseConfig = f
ctx.parseRuleConfig = g
}
}
func ProcessRequestHeadersBy[PluginConfig any](f onHttpHeadersFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
return func(ctx *CommonVmCtx[PluginConfig]) {
ctx.onHttpRequestHeaders = f
@@ -161,9 +170,19 @@ func (ctx *CommonPluginCtx[PluginConfig]) OnPluginStart(int) types.OnPluginStart
}
jsonData = gjson.ParseBytes(data)
}
err = ctx.ParseRuleConfig(jsonData, func(js gjson.Result, cfg *PluginConfig) error {
return ctx.vm.parseConfig(js, cfg, ctx.vm.log)
})
var parseOverrideConfig func(gjson.Result, PluginConfig, *PluginConfig) error
if ctx.vm.parseRuleConfig != nil {
parseOverrideConfig = func(js gjson.Result, global PluginConfig, cfg *PluginConfig) error {
return ctx.vm.parseRuleConfig(js, global, cfg, ctx.vm.log)
}
}
err = ctx.ParseRuleConfig(jsonData,
func(js gjson.Result, cfg *PluginConfig) error {
return ctx.vm.parseConfig(js, cfg, ctx.vm.log)
},
parseOverrideConfig,
)
if err != nil {
ctx.vm.log.Warnf("parse rule config failed: %v", err)
return types.OnPluginStartStatusFailed