Skip to content

Commit

Permalink
feat: return query_parameters_to_remove with OkResponse
Browse files Browse the repository at this point in the history
Envoy External Auth v3 API supports the `query_parameters_to_remove` attribute with
`OkResponse`.

See https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto#service-auth-v3-okhttpresponse

The change allows to return from OPA which query parameters should be removed
from the upstream query.

Signed-off-by: Anton Regeda <regedaster@gmail.com>
Signed-off-by: Anthony Regeda <regedaster@gmail.com>
  • Loading branch information
regeda committed Dec 13, 2024
1 parent 3724eb4 commit fe56692
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 5 deletions.
37 changes: 37 additions & 0 deletions envoyauth/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,43 @@ func (result *EvalResult) IsAllowed() (bool, error) {
return false, result.invalidDecisionErr()
}

// GetRequestQueryParametersToRemove - returns the query parameters to remove from the original request before dispatching
// it to the upstream
func (result *EvalResult) GetRequestQueryParametersToRemove() ([]string, error) {
queryParamsToRemove := []string{}

switch decision := result.Decision.(type) {
case bool:
return queryParamsToRemove, nil
case map[string]interface{}:
var ok bool
var val interface{}

if val, ok = decision["query_parameters_to_remove"]; !ok {
return queryParamsToRemove, nil
}

switch val := val.(type) {
case []string:
return val, nil
case []interface{}:
for _, vval := range val {
param, ok := vval.(string)
if !ok {
return nil, fmt.Errorf("type assertion error, expected query_parameters_to_remove value to be of type 'string' but got '%T'", vval)
}

queryParamsToRemove = append(queryParamsToRemove, param)
}
return queryParamsToRemove, nil
default:
return nil, fmt.Errorf("type assertion error, expected query_parameters_to_remove to be of type '[]string' but got '%T'", val)
}
}

return nil, result.invalidDecisionErr()
}

// GetRequestHTTPHeadersToRemove - returns the http headers to remove from the original request before dispatching
// it to the upstream
func (result *EvalResult) GetRequestHTTPHeadersToRemove() ([]string, error) {
Expand Down
15 changes: 12 additions & 3 deletions internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,11 +525,20 @@ func (p *envoyExtAuthzGrpcServer) check(ctx context.Context, req interface{}) (*
return nil, stop, internalErr
}

var queryParamsToRemove []string
queryParamsToRemove, err = result.GetRequestQueryParametersToRemove()
if err != nil {
err = errors.Wrap(err, "failed to get request query parameters to remove")
internalErr = newInternalError(EnvoyAuthResultErr, err)
return nil, stop, internalErr
}

resp.HttpResponse = &ext_authz_v3.CheckResponse_OkResponse{
OkResponse: &ext_authz_v3.OkHttpResponse{
Headers: responseHeaders,
HeadersToRemove: headersToRemove,
ResponseHeadersToAdd: responseHeadersToAdd,
Headers: responseHeaders,
HeadersToRemove: headersToRemove,
ResponseHeadersToAdd: responseHeadersToAdd,
QueryParametersToRemove: queryParamsToRemove,
},
}
} else {
Expand Down
48 changes: 46 additions & 2 deletions internal/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"context"
"errors"
"fmt"
ext_type_v2 "github.com/envoyproxy/go-control-plane/envoy/type"
ext_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
"net/http"
"net/http/httptest"
"reflect"
Expand All @@ -21,6 +19,8 @@ import (
ext_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
ext_authz_v2 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v2"
ext_authz "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
ext_type_v2 "github.com/envoyproxy/go-control-plane/envoy/type"
ext_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
_structpb "github.com/golang/protobuf/ptypes/struct"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/genproto/googleapis/rpc/code"
Expand Down Expand Up @@ -1518,6 +1518,50 @@ func TestCheckAllowBooleanDecisionDynamicMetadataDecisionID(t *testing.T) {
assertDynamicMetadataDecisionID(t, output.GetDynamicMetadata())
}

func TestCheckAllowObjectDecisionReqQueryParamsToRemove(t *testing.T) {
var req ext_authz.CheckRequest
if err := util.Unmarshal([]byte(exampleAllowedRequest), &req); err != nil {
panic(err)
}

module := `
package envoy.authz
default allow = true
query_parameters_to_remove := ["foo", "bar"]
result["allowed"] = allow
result["query_parameters_to_remove"] = query_parameters_to_remove`

server := testAuthzServerWithModule(module, "envoy/authz/result", nil, withCustomLogger(&testPlugin{}))
ctx := context.Background()
output, err := server.Check(ctx, &req)
if err != nil {
t.Fatal(err)
}

if output.Status.Code != int32(code.Code_OK) {
t.Fatalf("Expected request to be allowed but got: %v", output)
}

response := output.GetOkResponse()
if response == nil {
t.Fatal("Expected OkHttpResponse struct but got nil")
}

queryParams := response.GetQueryParametersToRemove()
if len(queryParams) != 2 {
t.Fatalf("Expected two query params but got %v", len(queryParams))
}

expectedQueryParams := []string{"foo", "bar"}

if !reflect.DeepEqual(expectedQueryParams, queryParams) {
t.Fatalf("Expected query params %v but got %v", expectedQueryParams, queryParams)
}
}

func TestCheckAllowObjectDecisionReqHeadersToRemove(t *testing.T) {
var req ext_authz.CheckRequest
if err := util.Unmarshal([]byte(exampleAllowedRequestParsedPath), &req); err != nil {
Expand Down

0 comments on commit fe56692

Please sign in to comment.