mirror of
https://github.com/alibaba/higress.git
synced 2026-06-02 00:57:28 +08:00
feat: Implement basic-auth WASM plugin using the Go SDK (#515)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user