components-contrib/tests/certification/middleware/http/opa/opa_test.go

181 lines
5.3 KiB
Go

/*
Copyright 2021 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package opa_test
import (
"bytes"
"context"
"fmt"
"net/http"
"strconv"
"testing"
"github.com/dapr/components-contrib/middleware"
opaMw "github.com/dapr/components-contrib/middleware/http/opa"
"github.com/dapr/components-contrib/tests/certification/embedded"
"github.com/dapr/components-contrib/tests/certification/flow"
"github.com/dapr/components-contrib/tests/certification/flow/app"
"github.com/dapr/components-contrib/tests/certification/flow/sidecar"
httpMiddlewareLoader "github.com/dapr/dapr/pkg/components/middleware/http"
"github.com/dapr/dapr/pkg/config/protocol"
httpMiddleware "github.com/dapr/dapr/pkg/middleware/http"
dapr_testing "github.com/dapr/dapr/pkg/testing"
"github.com/dapr/go-sdk/service/common"
"github.com/dapr/kit/logger"
"github.com/stretchr/testify/require"
"io"
"time"
"github.com/stretchr/testify/assert"
)
const (
appID = "myapp"
invokeMethod = "mymethod"
responseHeaderName = "x-custom-header"
)
var (
// Logger
log = logger.NewLogger("dapr.components")
)
func TestHTTPMiddlewareOpa(t *testing.T) {
ports, err := dapr_testing.GetFreePorts(6)
require.NoError(t, err)
grpcPorts := [2]int{ports[0], ports[3]}
httpPorts := [2]int{ports[1], ports[4]}
appPorts := [2]int{ports[2], ports[5]}
client := http.Client{}
// Application setup code
application := func(ctx flow.Context, s common.Service) error {
s.AddServiceInvocationHandler(invokeMethod, func(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) {
return &common.Content{}, nil
})
return nil
}
type sendRequestOpts struct {
Body string
}
sendRequest := func(parentCtx context.Context, port int, opts *sendRequestOpts) (int, string, error) {
invokeUrl := fmt.Sprintf("http://localhost:%d/v1.0/invoke/%s/method/%s", port, appID, invokeMethod)
reqCtx, reqCancel := context.WithTimeout(parentCtx, 150*time.Second)
// reqCtx, reqCancel := context.WithTimeout(parentCtx, 5*time.Second)
defer reqCancel()
req, err := http.NewRequestWithContext(reqCtx, http.MethodPost, invokeUrl, bytes.NewBufferString(opts.Body))
if err != nil {
return 0, "", fmt.Errorf("failed to create request: %w", err)
}
res, err := client.Do(req)
if err != nil {
return 0, "", fmt.Errorf("request error: %w", err)
}
defer func() {
// Drain before closing
_, _ = io.Copy(io.Discard, res.Body)
res.Body.Close()
}()
return res.StatusCode, res.Header.Get(responseHeaderName), nil
}
// Run tests to check if the bearer token is validated correctly
opaTests := func(ctx flow.Context) error {
tests := []struct {
name string
body string
statusCode int
expectedHeaderValue string
}{
{
name: "default response",
body: "",
statusCode: http.StatusTeapot,
expectedHeaderValue: "",
}, {
name: "allowed request",
body: "allow-ok",
statusCode: http.StatusOK,
expectedHeaderValue: "",
}, {
name: "redirected",
body: "redirect",
statusCode: http.StatusMovedPermanently,
expectedHeaderValue: "redirected",
},
}
ctx.T.Run("bearer token validation", func(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Invoke sidecar
resStatus, resHeaderValue, err := sendRequest(ctx.Context, httpPorts[0], &sendRequestOpts{
Body: tt.body,
})
require.NoError(t, err)
assert.Equal(t, tt.statusCode, resStatus)
assert.Equal(t, tt.expectedHeaderValue, resHeaderValue)
})
}
})
return nil
}
flow.New(t, "OPA HTTP Middleware").
// Start app and sidecar
Step(app.Run("Start application", fmt.Sprintf(":%d", appPorts[0]), application)).
Step(sidecar.Run(appID,
append(componentRuntimeOptions(),
embedded.WithAppProtocol(protocol.HTTPProtocol, strconv.Itoa(appPorts[0])),
embedded.WithDaprGRPCPort(strconv.Itoa(grpcPorts[0])),
embedded.WithDaprHTTPPort(strconv.Itoa(httpPorts[0])),
embedded.WithResourcesPath("./resources"),
embedded.WithAPILoggingEnabled(true),
embedded.WithProfilingEnabled(false),
)...,
)).
// Tests
Step("opa validation", opaTests).
// Run
Run()
}
func componentRuntimeOptions() []embedded.Option {
middlewareRegistry := httpMiddlewareLoader.NewRegistry()
middlewareRegistry.Logger = log
middlewareRegistry.RegisterComponent(func(log logger.Logger) httpMiddlewareLoader.FactoryMethod {
return func(metadata middleware.Metadata) (httpMiddleware.Middleware, error) {
return opaMw.NewMiddleware(log).GetHandler(context.Background(), metadata)
}
}, "opa")
return []embedded.Option{
embedded.WithHTTPMiddlewares(middlewareRegistry),
}
}