feat: Add statusCodeDetails info when returning response in Wasm plugins directly (#1116)

This commit is contained in:
Kent Dong
2024-07-16 09:52:46 +08:00
committed by GitHub
parent 85219b6c53
commit f069ad5b0d
111 changed files with 231 additions and 182 deletions

View File

@@ -7,7 +7,7 @@ replace github.com/alibaba/higress/plugins/wasm-go => ../..
require (
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230807053545-d307d0e755f1
github.com/go-jose/go-jose/v3 v3.0.0
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f
github.com/tidwall/gjson v1.14.3
golang.org/x/oauth2 v0.11.0
)

View File

@@ -17,6 +17,7 @@ github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a h1
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226064518-b3dc4646a35a/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240318034951-d5306e367c43/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240327114451-d6b7174a84fc/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240711023527-ba358c48772f/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo=
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@@ -168,7 +168,7 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config OidcConfig, log wrappe
cookieString, _ := proxywasm.GetHttpRequestHeader("cookie")
oidcCookieValue, code, state, err := oc.GetParams(config.CookieName, cookieString, ctx.Path(), config.CookieSecret)
if err != nil {
oc.SendError(&log, fmt.Sprintf("GetParams err : %v", err), http.StatusBadRequest)
oc.SendError(&log, fmt.Sprintf("GetParams err : %v", err), http.StatusBadRequest, "oidc.get_params_failed")
return types.ActionContinue
}
nonce, _ := oc.Nonce(32)
@@ -208,7 +208,7 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config OidcConfig, log wrappe
if oidcCookieValue == "" {
if code == "" {
if err := defaultHandler.ProcessRedirect(&log, cfg); err != nil {
oc.SendError(&log, fmt.Sprintf("ProcessRedirect error : %v", err), http.StatusInternalServerError)
oc.SendError(&log, fmt.Sprintf("ProcessRedirect error : %v", err), http.StatusInternalServerError, "oidc.process_redirect_failed")
return types.ActionContinue
}
return types.ActionPause
@@ -216,44 +216,44 @@ func onHttpRequestHeaders(ctx wrapper.HttpContext, config OidcConfig, log wrappe
if strings.Contains(ctx.Path(), OAUTH2CALLBACK) {
parts := strings.Split(state, ".")
if len(parts) != 2 {
oc.SendError(&log, "State signature verification failed", http.StatusUnauthorized)
oc.SendError(&log, "State signature verification failed", http.StatusUnauthorized, "oidc.bad_state")
return types.ActionContinue
}
stateVal, signature := parts[0], parts[1]
if err := oc.VerifyState(stateVal, signature, cfg.ClientSecret, cfg.RedirectURL); err != nil {
oc.SendError(&log, fmt.Sprintf("State signature verification failed : %v", err), http.StatusUnauthorized)
oc.SendError(&log, fmt.Sprintf("State signature verification failed : %v", err), http.StatusUnauthorized, "oidc.invalid_state")
return types.ActionContinue
}
cfg.Option.Code = code
cfg.Option.Mod = oc.SenBack
if err := defaultHandler.ProcessExchangeToken(&log, cfg); err != nil {
oc.SendError(&log, fmt.Sprintf("ProcessExchangeToken error : %v", err), http.StatusInternalServerError)
oc.SendError(&log, fmt.Sprintf("ProcessExchangeToken error : %v", err), http.StatusInternalServerError, "oidc.process_exchange_token_failed")
return types.ActionContinue
}
return types.ActionPause
}
oc.SendError(&log, fmt.Sprintf("redirect URL must end with oauth2/callback"), http.StatusBadRequest)
oc.SendError(&log, fmt.Sprintf("redirect URL must end with oauth2/callback"), http.StatusBadRequest, "oidc.bad_redirect_url")
return types.ActionContinue
}
cookiedata, err := oc.DeserializedeCookieData(oidcCookieValue)
cookieData, err := oc.DeserializeCookieData(oidcCookieValue)
if err != nil {
oc.SendError(&log, fmt.Sprintf("DeserializedeCookieData err : %v", err), http.StatusInternalServerError)
oc.SendError(&log, fmt.Sprintf("DeserializeCookieData err : %v", err), http.StatusInternalServerError, "oidc.bad_cookie_value")
return types.ActionContinue
}
cfg.CookieData = &oc.CookieData{
IDToken: cookiedata.IDToken,
IDToken: cookieData.IDToken,
Secret: cfg.CookieOption.Secret,
Nonce: cookiedata.Nonce,
CreatedAt: cookiedata.CreatedAt,
ExpiresOn: cookiedata.ExpiresOn,
Nonce: cookieData.Nonce,
CreatedAt: cookieData.CreatedAt,
ExpiresOn: cookieData.ExpiresOn,
}
cfg.Option.RawIdToken = cfg.CookieData.IDToken
cfg.Option.Mod = oc.Access
if err := defaultHandler.ProcessVerify(&log, cfg); err != nil {
oc.SendError(&log, fmt.Sprintf("ProcessVerify error : %v", err), http.StatusUnauthorized)
oc.SendError(&log, fmt.Sprintf("ProcessVerify error : %v", err), http.StatusUnauthorized, "oidc.unauthorized")
return types.ActionContinue
}

View File

@@ -52,8 +52,8 @@ func SerializeAndEncryptCookieData(data *CookieData, keySecret string, cookieSet
return buildSecureCookieHeader(data, keySecret, cookieSettings)
}
// DeserializedeCookieData 将一个安全的cookie header解密并反序列化为 CookieData 对象
func DeserializedeCookieData(cookievalue string) (*CookieData, error) {
// DeserializeCookieData 将一个安全的cookie header解密并反序列化为 CookieData 对象
func DeserializeCookieData(cookievalue string) (*CookieData, error) {
data, err := retrieveCookieData(cookievalue)
if err != nil {

View File

@@ -60,7 +60,7 @@ func ProcessHTTPCall(log *wrapper.Log, cfg *Oatuh2Config, callback func(response
if err := cfg.Client.Get(wellKnownPath, nil, func(statusCode int, responseHeaders http.Header, responseBody []byte) {
if err := ValidateHTTPResponse(statusCode, responseHeaders, responseBody); err != nil {
cleanedBody := re.ReplaceAllString(string(responseBody), "")
SendError(log, fmt.Sprintf("ValidateHTTPResponse failed , status : %v err : %v err_info: %v ", statusCode, err, cleanedBody), statusCode)
SendError(log, fmt.Sprintf("ValidateHTTPResponse failed , status : %v err : %v err_info: %v ", statusCode, err, cleanedBody), statusCode, "oidc.bad_well_known_response")
return
}
callback(responseBody)
@@ -78,7 +78,7 @@ func (d *DefaultOAuthHandler) ProcessRedirect(log *wrapper.Log, cfg *Oatuh2Confi
statStr := GenState(state, cfg.ClientSecret, cfg.RedirectURL)
cfg.Endpoint.AuthURL = gjson.ParseBytes(responseBody).Get("authorization_endpoint").String()
if cfg.Endpoint.AuthURL == "" {
SendError(log, "Missing 'authorization_endpoint' in the OpenID configuration response.", http.StatusInternalServerError)
SendError(log, "Missing 'authorization_endpoint' in the OpenID configuration response.", http.StatusInternalServerError, "oidc.auth_endpoint_missing")
return
}
@@ -87,7 +87,7 @@ func (d *DefaultOAuthHandler) ProcessRedirect(log *wrapper.Log, cfg *Oatuh2Confi
opts = SetNonce(string(cfg.CookieData.Nonce))
}
codeURL := cfg.AuthCodeURL(statStr, opts)
proxywasm.SendHttpResponse(http.StatusFound, [][2]string{
proxywasm.SendHttpResponseWithDetail(http.StatusFound, "oidc.authed", [][2]string{
{"Location", codeURL},
}, nil, -1)
return
@@ -100,18 +100,18 @@ func (d *DefaultOAuthHandler) ProcessExchangeToken(log *wrapper.Log, cfg *Oatuh2
PvRJson := gjson.ParseBytes(responseBody)
cfg.Endpoint.TokenURL = PvRJson.Get("token_endpoint").String()
if cfg.Endpoint.TokenURL == "" {
SendError(log, "Missing 'token_endpoint' in the OpenID configuration response.", http.StatusInternalServerError)
SendError(log, "Missing 'token_endpoint' in the OpenID configuration response.", http.StatusInternalServerError, "oidc.token_endpoint_missing")
return
}
cfg.JwksURL = PvRJson.Get("jwks_uri").String()
if cfg.JwksURL == "" {
SendError(log, "Missing 'jwks_uri' in the OpenID configuration response.", http.StatusInternalServerError)
SendError(log, "Missing 'jwks_uri' in the OpenID configuration response.", http.StatusInternalServerError, "oidc.jwks_uri_missing")
return
}
cfg.Option.AuthStyle = AuthStyle(cfg.Endpoint.AuthStyle)
if err := processToken(log, cfg); err != nil {
SendError(log, fmt.Sprintf("ProcessToken failed : err %v", err), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("ProcessToken failed : err %v", err), http.StatusInternalServerError, "oidc.process_token_failed")
return
}
})
@@ -123,7 +123,7 @@ func (d *DefaultOAuthHandler) ProcessVerify(log *wrapper.Log, cfg *Oatuh2Config)
cfg.JwksURL = PvRJson.Get("jwks_uri").String()
if cfg.JwksURL == "" {
SendError(log, "Missing 'token_endpoint' in the OpenID configuration response.", http.StatusInternalServerError)
SendError(log, "Missing 'token_endpoint' in the OpenID configuration response.", http.StatusInternalServerError, "oidc.token_endpoint_missing")
return
}
var algs []string
@@ -134,7 +134,7 @@ func (d *DefaultOAuthHandler) ProcessVerify(log *wrapper.Log, cfg *Oatuh2Config)
}
cfg.SupportedSigningAlgs = algs
if err := processTokenVerify(log, cfg); err != nil {
SendError(log, fmt.Sprintf("failed to verify token: %v", err), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("failed to verify token: %v", err), http.StatusInternalServerError, "oidc.verify_token_failed")
return
}
})
@@ -161,13 +161,13 @@ func processToken(log *wrapper.Log, cfg *Oatuh2Config) error {
cb := func(statusCode int, responseHeaders http.Header, responseBody []byte) {
if err := ValidateHTTPResponse(statusCode, responseHeaders, responseBody); err != nil {
cleanedBody := re.ReplaceAllString(string(responseBody), "")
SendError(log, fmt.Sprintf("Valid failed , status : %v err : %v err_info: %v ", statusCode, err, cleanedBody), statusCode)
SendError(log, fmt.Sprintf("Valid failed , status : %v err : %v err_info: %v ", statusCode, err, cleanedBody), statusCode, "oidc.bad_token_response")
return
}
tk, err := UnmarshalToken(&token, responseHeaders, responseBody)
if err != nil {
SendError(log, fmt.Sprintf("UnmarshalToken error: %v", err), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("UnmarshalToken error: %v", err), http.StatusInternalServerError, "oidc.extract_token_failed")
return
}
@@ -179,14 +179,14 @@ func processToken(log *wrapper.Log, cfg *Oatuh2Config) error {
rawIDToken, ok := betoken.Extra("id_token").(string)
if !ok {
SendError(log, fmt.Sprintf("No id_token field in oauth2 token."), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("No id_token field in oauth2 token."), http.StatusInternalServerError, "oidc.id_token_missing")
return
}
cfg.Option.RawIdToken = rawIDToken
err = processTokenVerify(log, cfg)
if err != nil {
SendError(log, fmt.Sprintf("failed to verify token: %v", err), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("failed to verify token: %v", err), http.StatusInternalServerError, "oidc.verify_token_failed")
return
}
@@ -218,7 +218,7 @@ func processTokenVerify(log *wrapper.Log, cfg *Oatuh2Config) error {
cb := func(statusCode int, responseHeaders http.Header, responseBody []byte) {
if err := ValidateHTTPResponse(statusCode, responseHeaders, responseBody); err != nil {
cleanedBody := re.ReplaceAllString(string(responseBody), "")
SendError(log, fmt.Sprintf("Valid failed , status : %v err : %v err_info: %v ", statusCode, err, cleanedBody), statusCode)
SendError(log, fmt.Sprintf("Valid failed , status : %v err : %v err_info: %v ", statusCode, err, cleanedBody), statusCode, "oidc.bad_validate_response")
return
}
@@ -227,7 +227,7 @@ func processTokenVerify(log *wrapper.Log, cfg *Oatuh2Config) error {
jsw, err := GenJswkey(val)
if err != nil {
log.Errorf("err: %v", err)
SendError(log, fmt.Sprintf("GenJswkey error:%v", err), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("GenJswkey error:%v", err), http.StatusInternalServerError, "oidc.gen_jsw_key_failed")
return
}
keySet.Keys = append(keySet.Keys, *jsw)
@@ -262,10 +262,10 @@ func processTokenVerify(log *wrapper.Log, cfg *Oatuh2Config) error {
cookieHeader, err := SerializeAndEncryptCookieData(cfg.CookieData, cfg.CookieOption.Secret, cfg.CookieOption)
if err != nil {
SendError(log, fmt.Sprintf("SerializeAndEncryptCookieData failed : %v", err), http.StatusInternalServerError)
SendError(log, fmt.Sprintf("SerializeAndEncryptCookieData failed : %v", err), http.StatusInternalServerError, "oidc.gen_cookie_failed")
return
}
proxywasm.SendHttpResponse(http.StatusFound, [][2]string{
proxywasm.SendHttpResponseWithDetail(http.StatusFound, "oidc.token_verified", [][2]string{
{"Location", cfg.ClientUrl},
{"Set-Cookie", cookieHeader},
}, nil, -1)

View File

@@ -68,9 +68,9 @@ func GetParams(name, cookie, path, key string) (oidcCookieValue, code, state str
return oidcCookieValue, code, state, nil
}
func SendError(log *wrapper.Log, errMsg string, status int) {
func SendError(log *wrapper.Log, errMsg string, status int, statusDetail string) {
log.Errorf(errMsg)
proxywasm.SendHttpResponse(uint32(status), nil, []byte(errMsg), -1)
proxywasm.SendHttpResponseWithDetail(uint32(status), statusDetail, nil, []byte(errMsg), -1)
}
type jsonTime time.Time