mirror of
https://github.com/alibaba/higress.git
synced 2026-06-07 11:47:30 +08:00
feat: Add statusCodeDetails info when returning response in Wasm plugins directly (#1116)
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user