mirror of
https://github.com/alibaba/higress.git
synced 2026-03-05 09:00:47 +08:00
230 lines
6.6 KiB
Go
230 lines
6.6 KiB
Go
package main
|
||
|
||
import (
|
||
"errors"
|
||
"ext-auth/expr"
|
||
"fmt"
|
||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||
"github.com/tidwall/gjson"
|
||
"net/http"
|
||
"strings"
|
||
)
|
||
|
||
const (
|
||
DefaultStatusOnError uint32 = http.StatusForbidden
|
||
|
||
DefaultHttpServiceTimeout uint32 = 200
|
||
)
|
||
|
||
type ExtAuthConfig struct {
|
||
httpService HttpService
|
||
failureModeAllow bool
|
||
failureModeAllowHeaderAdd bool
|
||
statusOnError uint32
|
||
}
|
||
|
||
type HttpService struct {
|
||
client wrapper.HttpClient
|
||
requestMethod string
|
||
path string
|
||
timeout uint32
|
||
authorizationRequest AuthorizationRequest
|
||
authorizationResponse AuthorizationResponse
|
||
}
|
||
|
||
type AuthorizationRequest struct {
|
||
// allowedHeaders In addition to the user’s supplied matchers,
|
||
// Host, Method, Path, Content-Length, and Authorization are automatically included to the list.
|
||
allowedHeaders expr.Matcher
|
||
headersToAdd map[string]string
|
||
withRequestBody bool
|
||
}
|
||
|
||
type AuthorizationResponse struct {
|
||
allowedUpstreamHeaders expr.Matcher
|
||
allowedClientHeaders expr.Matcher
|
||
}
|
||
|
||
func parseConfig(json gjson.Result, config *ExtAuthConfig, log wrapper.Log) error {
|
||
httpServiceConfig := json.Get("http_service")
|
||
if !httpServiceConfig.Exists() {
|
||
return errors.New("missing http_service in config")
|
||
}
|
||
err := parseHttpServiceConfig(httpServiceConfig, config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
failureModeAllow := json.Get("failure_mode_allow")
|
||
if failureModeAllow.Exists() {
|
||
config.failureModeAllow = failureModeAllow.Bool()
|
||
}
|
||
|
||
failureModeAllowHeaderAdd := json.Get("failure_mode_allow_header_add")
|
||
if failureModeAllowHeaderAdd.Exists() {
|
||
config.failureModeAllowHeaderAdd = failureModeAllowHeaderAdd.Bool()
|
||
}
|
||
|
||
statusOnError := json.Get("status_on_error")
|
||
if statusOnError.Exists() {
|
||
config.statusOnError = uint32(statusOnError.Uint())
|
||
} else {
|
||
config.statusOnError = DefaultStatusOnError
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func parseHttpServiceConfig(json gjson.Result, config *ExtAuthConfig) error {
|
||
var httpService HttpService
|
||
|
||
if err := parseEndpointConfig(json, &httpService); err != nil {
|
||
return err
|
||
}
|
||
|
||
timeout := uint32(json.Get("timeout").Uint())
|
||
if timeout == 0 {
|
||
timeout = DefaultHttpServiceTimeout
|
||
}
|
||
httpService.timeout = timeout
|
||
|
||
if err := parseAuthorizationRequestConfig(json, &httpService); err != nil {
|
||
return err
|
||
}
|
||
|
||
if err := parseAuthorizationResponseConfig(json, &httpService); err != nil {
|
||
return err
|
||
}
|
||
|
||
config.httpService = httpService
|
||
|
||
return nil
|
||
}
|
||
|
||
func parseEndpointConfig(json gjson.Result, httpService *HttpService) error {
|
||
endpointConfig := json.Get("endpoint")
|
||
if !endpointConfig.Exists() {
|
||
return errors.New("missing endpoint in config")
|
||
}
|
||
|
||
serviceSource := endpointConfig.Get("service_source").String()
|
||
serviceName := endpointConfig.Get("service_name").String()
|
||
servicePort := endpointConfig.Get("service_port").Int()
|
||
if serviceName == "" || servicePort == 0 {
|
||
return errors.New("invalid service config")
|
||
}
|
||
switch serviceSource {
|
||
case "k8s":
|
||
namespace := json.Get("namespace").String()
|
||
httpService.client = wrapper.NewClusterClient(wrapper.K8sCluster{
|
||
ServiceName: serviceName,
|
||
Namespace: namespace,
|
||
Port: servicePort,
|
||
})
|
||
return nil
|
||
case "nacos":
|
||
namespace := json.Get("namespace").String()
|
||
httpService.client = wrapper.NewClusterClient(wrapper.NacosCluster{
|
||
ServiceName: serviceName,
|
||
NamespaceID: namespace,
|
||
Port: servicePort,
|
||
})
|
||
return nil
|
||
case "ip":
|
||
httpService.client = wrapper.NewClusterClient(wrapper.StaticIpCluster{
|
||
ServiceName: serviceName,
|
||
Port: servicePort,
|
||
})
|
||
case "dns":
|
||
domain := endpointConfig.Get("domain").String()
|
||
httpService.client = wrapper.NewClusterClient(wrapper.DnsCluster{
|
||
ServiceName: serviceName,
|
||
Port: servicePort,
|
||
Domain: domain,
|
||
})
|
||
default:
|
||
return errors.New("unknown service source: " + serviceSource)
|
||
}
|
||
|
||
requestMethodConfig := endpointConfig.Get("request_method")
|
||
if !requestMethodConfig.Exists() {
|
||
httpService.requestMethod = http.MethodGet
|
||
} else {
|
||
httpService.requestMethod = strings.ToUpper(requestMethodConfig.String())
|
||
}
|
||
|
||
pathConfig := endpointConfig.Get("path")
|
||
if !pathConfig.Exists() {
|
||
return errors.New("missing path in config")
|
||
}
|
||
httpService.path = pathConfig.String()
|
||
|
||
return nil
|
||
}
|
||
|
||
func parseAuthorizationRequestConfig(json gjson.Result, httpService *HttpService) error {
|
||
authorizationRequestConfig := json.Get("authorization_request")
|
||
if authorizationRequestConfig.Exists() {
|
||
var authorizationRequest AuthorizationRequest
|
||
|
||
headersToAdd := map[string]string{}
|
||
headersToAddConfig := authorizationRequestConfig.Get("headers_to_add")
|
||
if headersToAddConfig.Exists() {
|
||
for key, value := range headersToAddConfig.Map() {
|
||
headersToAdd[key] = value.Str
|
||
}
|
||
}
|
||
authorizationRequest.headersToAdd = headersToAdd
|
||
|
||
withRequestBody := authorizationRequestConfig.Get("with_request_body")
|
||
if withRequestBody.Exists() {
|
||
// withRequestBody is true and the request method is GET, OPTIONS or HEAD
|
||
if withRequestBody.Bool() &&
|
||
(httpService.requestMethod == http.MethodGet || httpService.requestMethod == http.MethodOptions || httpService.requestMethod == http.MethodHead) {
|
||
return errors.New(fmt.Sprintf("requestMethod %s does not support with_request_body set to true", httpService.requestMethod))
|
||
}
|
||
authorizationRequest.withRequestBody = withRequestBody.Bool()
|
||
}
|
||
|
||
allowedHeaders := authorizationRequestConfig.Get("allowed_headers")
|
||
if allowedHeaders.Exists() {
|
||
result, err := expr.BuildRepeatedStringMatcherIgnoreCase(allowedHeaders.Array())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
authorizationRequest.allowedHeaders = result
|
||
}
|
||
|
||
httpService.authorizationRequest = authorizationRequest
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func parseAuthorizationResponseConfig(json gjson.Result, httpService *HttpService) error {
|
||
authorizationResponseConfig := json.Get("authorization_response")
|
||
if authorizationResponseConfig.Exists() {
|
||
var authorizationResponse AuthorizationResponse
|
||
|
||
allowedUpstreamHeaders := authorizationResponseConfig.Get("allowed_upstream_headers")
|
||
if allowedUpstreamHeaders.Exists() {
|
||
result, err := expr.BuildRepeatedStringMatcherIgnoreCase(allowedUpstreamHeaders.Array())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
authorizationResponse.allowedUpstreamHeaders = result
|
||
}
|
||
|
||
allowedClientHeaders := authorizationResponseConfig.Get("allowed_client_headers")
|
||
if allowedClientHeaders.Exists() {
|
||
result, err := expr.BuildRepeatedStringMatcherIgnoreCase(allowedClientHeaders.Array())
|
||
if err != nil {
|
||
return err
|
||
}
|
||
authorizationResponse.allowedClientHeaders = result
|
||
}
|
||
|
||
httpService.authorizationResponse = authorizationResponse
|
||
}
|
||
return nil
|
||
}
|