From e6f0e014c0ad280fc67557704a2015924caf76eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraci=20Paix=C3=A3o=20Kr=C3=B6hling?= Date: Mon, 20 Dec 2021 20:12:40 +0100 Subject: [PATCH] Remove interceptors from configauth interface (#4583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove interceptors from configauth interface Fixes #4582 Signed-off-by: Juraci Paixão Kröhling * Add changelog entry Signed-off-by: Juraci Paixão Kröhling * Update CHANGELOG.md Co-authored-by: Bogdan Drutu --- CHANGELOG.md | 2 +- config/configauth/mock_serverauth.go | 25 --- config/configauth/mock_serverauth_test.go | 11 -- config/configauth/serverauth.go | 92 ----------- config/configauth/serverauth_test.go | 187 ---------------------- config/configgrpc/configgrpc.go | 43 ++++- config/configgrpc/configgrpc_test.go | 161 +++++++++++++++++++ config/confighttp/confighttp.go | 14 +- config/confighttp/confighttp_test.go | 1 - 9 files changed, 216 insertions(+), 320 deletions(-) delete mode 100644 config/configauth/serverauth_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index f4ab1f84be5..6207f36b2cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,13 @@ - Remove `configmapprovider.NewInMemory()` (#4507) - Disallow direct implementation of `configmapprovider.Retrieved` (#4577) +- `configauth`: remove interceptor functions from the ServerAuthenticator interface (#4583) ## 💡 Enhancements 💡 - `confighttp`: add client-side compression support. (#4441) - Each exporter should remove `compression` field if they have and should use `confighttp.HTTPClientSettings` - Allow more zap logger configs: `disable_caller`, `disable_stacktrace`, `output_paths`, `error_output_paths`, `initial_fields` (#1048) -- `configauth`: add ServerAuthenticator interfaces for HTTP receivers. (#4506) - Collector self-metrics may now be configured through the configuration file. (#4069) - CLI flags for configuring self-metrics are deprecated and will be removed in a future release. diff --git a/config/configauth/mock_serverauth.go b/config/configauth/mock_serverauth.go index 09eab9a744f..5aed5a36e27 100644 --- a/config/configauth/mock_serverauth.go +++ b/config/configauth/mock_serverauth.go @@ -16,9 +16,6 @@ package configauth // import "go.opentelemetry.io/collector/config/configauth" import ( "context" - "net/http" - - "google.golang.org/grpc" "go.opentelemetry.io/collector/component" ) @@ -32,10 +29,6 @@ var ( type MockServerAuthenticator struct { // AuthenticateFunc to use during the authentication phase of this mock. Optional. AuthenticateFunc AuthenticateFunc - - // HTTPInterceptor to use in the test - HTTPInterceptorFunc HTTPInterceptorFunc - // TODO: implement the other funcs } // Authenticate executes the mock's AuthenticateFunc, if provided, or just returns the given context unchanged. @@ -46,24 +39,6 @@ func (m *MockServerAuthenticator) Authenticate(ctx context.Context, headers map[ return m.AuthenticateFunc(ctx, headers) } -// GRPCUnaryServerInterceptor isn't currently implemented and always returns nil. -func (m *MockServerAuthenticator) GRPCUnaryServerInterceptor(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) { - return nil, nil -} - -// GRPCStreamServerInterceptor isn't currently implemented and always returns nil. -func (m *MockServerAuthenticator) GRPCStreamServerInterceptor(interface{}, grpc.ServerStream, *grpc.StreamServerInfo, grpc.StreamHandler) error { - return nil -} - -// HTTPInterceptor isn't currently implemented and always returns nil. -func (m *MockServerAuthenticator) HTTPInterceptor(next http.Handler) http.Handler { - if m.HTTPInterceptorFunc == nil { - return next - } - return m.HTTPInterceptorFunc(next, m.AuthenticateFunc) -} - // Start isn't currently implemented and always returns nil. func (m *MockServerAuthenticator) Start(context.Context, component.Host) error { return nil diff --git a/config/configauth/mock_serverauth_test.go b/config/configauth/mock_serverauth_test.go index 3ae9efc37bb..7cfb783fd5a 100644 --- a/config/configauth/mock_serverauth_test.go +++ b/config/configauth/mock_serverauth_test.go @@ -52,17 +52,6 @@ func TestNilOperations(t *testing.T) { assert.NotNil(t, ctx) } - { - ret, err := m.GRPCUnaryServerInterceptor(origCtx, nil, nil, nil) - assert.Nil(t, ret) - assert.NoError(t, err) - } - - { - err := m.GRPCStreamServerInterceptor(nil, nil, nil, nil) - assert.NoError(t, err) - } - { err := m.Start(origCtx, nil) assert.NoError(t, err) diff --git a/config/configauth/serverauth.go b/config/configauth/serverauth.go index 4befd7038e8..e1d32620fbc 100644 --- a/config/configauth/serverauth.go +++ b/config/configauth/serverauth.go @@ -16,18 +16,8 @@ package configauth // import "go.opentelemetry.io/collector/config/configauth" import ( "context" - "errors" - "net/http" - - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/internal/middleware" -) - -var ( - errMetadataNotFound = errors.New("no request metadata found") ) // ServerAuthenticator is an Extension that can be used as an authenticator for the configauth.Authentication option. @@ -47,90 +37,8 @@ type ServerAuthenticator interface { // on tenancy as determined by the group membership, or passing through the authentication data to the next collector/backend. // The context keys to be used are not defined yet. Authenticate(ctx context.Context, headers map[string][]string) (context.Context, error) - - // GRPCUnaryServerInterceptor is a helper method to provide a gRPC-compatible UnaryServerInterceptor, typically calling the authenticator's Authenticate method. - // While the context is the typical source of authentication data, the interceptor is free to determine where the auth data should come from. For instance, some - // receivers might implement an interceptor that looks into the payload instead. - // Once the authentication succeeds, the interceptor is expected to call the handler. - // See https://pkg.go.dev/google.golang.org/grpc#UnaryServerInterceptor. - GRPCUnaryServerInterceptor(ctx context.Context, req interface{}, srvInfo *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) - - // GRPCStreamServerInterceptor is a helper method to provide a gRPC-compatible StreamServerInterceptor, typically calling the authenticator's Authenticate method. - // While the context is the typical source of authentication data, the interceptor is free to determine where the auth data should come from. For instance, some - // receivers might implement an interceptor that looks into the payload instead. - // Once the authentication succeeds, the interceptor is expected to call the handler. - // See https://pkg.go.dev/google.golang.org/grpc#StreamServerInterceptor. - GRPCStreamServerInterceptor(srv interface{}, stream grpc.ServerStream, srvInfo *grpc.StreamServerInfo, handler grpc.StreamHandler) error - - // HTTPInterceptor is a helper method to provide an HTTP handler responsible for intercepting the incoming HTTP requests, using the - // request's meta data as source of data for the authentication. Once the authentication succeeds, the interceptor is expected to call - // the next handler. - HTTPInterceptor(next http.Handler) http.Handler } // AuthenticateFunc defines the signature for the function responsible for performing the authentication based on the given headers map. // See ServerAuthenticator.Authenticate. type AuthenticateFunc func(ctx context.Context, headers map[string][]string) (context.Context, error) - -// GRPCUnaryInterceptorFunc defines the signature for the function intercepting unary gRPC calls, useful for authenticators to use as -// types for internal structs, making it easier to mock them in tests. -// See ServerAuthenticator.GRPCUnaryServerInterceptor. -type GRPCUnaryInterceptorFunc func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, authenticate AuthenticateFunc) (interface{}, error) - -// GRPCStreamInterceptorFunc defines the signature for the function intercepting streaming gRPC calls, useful for authenticators to use as -// types for internal structs, making it easier to mock them in tests. -// See ServerAuthenticator.GRPCStreamServerInterceptor. -type GRPCStreamInterceptorFunc func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler, authenticate AuthenticateFunc) error - -// HTTPInterceptorFunc defines the signature for the function intercepting HTTP calls, useful for authenticators to use as -// types for internal structs, making it easier to mock them in tests. -type HTTPInterceptorFunc func(handler http.Handler, authenticate AuthenticateFunc) http.Handler - -// DefaultGRPCUnaryServerInterceptor provides a default implementation of GRPCUnaryInterceptorFunc, useful for most authenticators. -// It extracts the headers from the incoming request, under the assumption that the credentials will be part of the resulting map. -func DefaultGRPCUnaryServerInterceptor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler, authenticate AuthenticateFunc) (interface{}, error) { - headers, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, errMetadataNotFound - } - - ctx, err := authenticate(ctx, headers) - if err != nil { - return nil, err - } - - return handler(ctx, req) -} - -// DefaultGRPCStreamServerInterceptor provides a default implementation of GRPCStreamInterceptorFunc, useful for most authenticators. -// It extracts the headers from the incoming request, under the assumption that the credentials will be part of the resulting map. -func DefaultGRPCStreamServerInterceptor(srv interface{}, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler, authenticate AuthenticateFunc) error { - ctx := stream.Context() - headers, ok := metadata.FromIncomingContext(ctx) - if !ok { - return errMetadataNotFound - } - - ctx, err := authenticate(ctx, headers) - if err != nil { - return err - } - - wrapped := middleware.WrapServerStream(stream) - wrapped.WrappedContext = ctx - return handler(srv, wrapped) -} - -// DefaultHTTPInterceptor provides a default implementation of HTTPInterceptorFunc, useful for most authenticators. -// It passes the headers from the incoming request as it is, under the assumption that the credentials are part of it. -func DefaultHTTPInterceptor(next http.Handler, authenticate AuthenticateFunc) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx, err := authenticate(r.Context(), r.Header) - if err != nil { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return - } - - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} diff --git a/config/configauth/serverauth_test.go b/config/configauth/serverauth_test.go deleted file mode 100644 index b8e8f6ce516..00000000000 --- a/config/configauth/serverauth_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright The OpenTelemetry 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 configauth - -import ( - "context" - "fmt" - "net" - "testing" - - "github.com/stretchr/testify/assert" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" - - "go.opentelemetry.io/collector/client" -) - -func TestDefaultUnaryInterceptorAuthSucceeded(t *testing.T) { - // prepare - handlerCalled := false - authCalled := false - authFunc := func(context.Context, map[string][]string) (context.Context, error) { - authCalled = true - ctx := client.NewContext(context.Background(), client.Info{ - Addr: &net.IPAddr{IP: net.IPv4(1, 2, 3, 4)}, - }) - - return ctx, nil - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - handlerCalled = true - cl := client.FromContext(ctx) - assert.Equal(t, "1.2.3.4", cl.Addr.String()) - return nil, nil - } - ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) - - // test - res, err := DefaultGRPCUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, authFunc) - - // verify - assert.Nil(t, res) - assert.NoError(t, err) - assert.True(t, authCalled) - assert.True(t, handlerCalled) -} - -func TestDefaultUnaryInterceptorAuthFailure(t *testing.T) { - // prepare - authCalled := false - expectedErr := fmt.Errorf("not authenticated") - authFunc := func(context.Context, map[string][]string) (context.Context, error) { - authCalled = true - return context.Background(), expectedErr - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - assert.FailNow(t, "the handler should not have been called on auth failure!") - return nil, nil - } - ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) - - // test - res, err := DefaultGRPCUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, authFunc) - - // verify - assert.Nil(t, res) - assert.Equal(t, expectedErr, err) - assert.True(t, authCalled) -} - -func TestDefaultUnaryInterceptorMissingMetadata(t *testing.T) { - // prepare - authFunc := func(context.Context, map[string][]string) (context.Context, error) { - assert.FailNow(t, "the auth func should not have been called!") - return context.Background(), nil - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - assert.FailNow(t, "the handler should not have been called!") - return nil, nil - } - - // test - res, err := DefaultGRPCUnaryServerInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, handler, authFunc) - - // verify - assert.Nil(t, res) - assert.Equal(t, errMetadataNotFound, err) -} - -func TestDefaultStreamInterceptorAuthSucceeded(t *testing.T) { - // prepare - handlerCalled := false - authCalled := false - authFunc := func(context.Context, map[string][]string) (context.Context, error) { - authCalled = true - ctx := client.NewContext(context.Background(), client.Info{ - Addr: &net.IPAddr{IP: net.IPv4(1, 2, 3, 4)}, - }) - return ctx, nil - } - handler := func(srv interface{}, stream grpc.ServerStream) error { - // ensure that the client information is propagated down to the underlying stream - cl := client.FromContext(stream.Context()) - assert.Equal(t, "1.2.3.4", cl.Addr.String()) - handlerCalled = true - return nil - } - ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) - streamServer := &mockServerStream{ - ctx: ctx, - } - - // test - err := DefaultGRPCStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc) - - // verify - assert.NoError(t, err) - assert.True(t, authCalled) - assert.True(t, handlerCalled) -} - -func TestDefaultStreamInterceptorAuthFailure(t *testing.T) { - // prepare - authCalled := false - expectedErr := fmt.Errorf("not authenticated") - authFunc := func(context.Context, map[string][]string) (context.Context, error) { - authCalled = true - return context.Background(), expectedErr - } - handler := func(srv interface{}, stream grpc.ServerStream) error { - assert.FailNow(t, "the handler should not have been called on auth failure!") - return nil - } - ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) - streamServer := &mockServerStream{ - ctx: ctx, - } - - // test - err := DefaultGRPCStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc) - - // verify - assert.Equal(t, expectedErr, err) - assert.True(t, authCalled) -} - -func TestDefaultStreamInterceptorMissingMetadata(t *testing.T) { - // prepare - authFunc := func(context.Context, map[string][]string) (context.Context, error) { - assert.FailNow(t, "the auth func should not have been called!") - return context.Background(), nil - } - handler := func(srv interface{}, stream grpc.ServerStream) error { - assert.FailNow(t, "the handler should not have been called!") - return nil - } - streamServer := &mockServerStream{ - ctx: context.Background(), - } - - // test - err := DefaultGRPCStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc) - - // verify - assert.Equal(t, errMetadataNotFound, err) -} - -type mockServerStream struct { - grpc.ServerStream - ctx context.Context -} - -func (m *mockServerStream) Context() context.Context { - return m.ctx -} diff --git a/config/configgrpc/configgrpc.go b/config/configgrpc/configgrpc.go index c574a2dea88..febc1c13abf 100644 --- a/config/configgrpc/configgrpc.go +++ b/config/configgrpc/configgrpc.go @@ -17,6 +17,7 @@ package configgrpc // import "go.opentelemetry.io/collector/config/configgrpc" import ( "context" "crypto/tls" + "errors" "fmt" "net" "strings" @@ -31,6 +32,7 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding/gzip" "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" "go.opentelemetry.io/collector/client" @@ -56,6 +58,8 @@ var ( CompressionSnappy: snappy.Name, CompressionZstd: zstd.Name, } + + errMetadataNotFound = errors.New("no request metadata found") ) // Allowed balancer names to be set in grpclb_policy to discover the servers. @@ -337,8 +341,12 @@ func (gss *GRPCServerSettings) ToServerOption(host component.Host, settings comp return nil, err } - uInterceptors = append(uInterceptors, authenticator.GRPCUnaryServerInterceptor) - sInterceptors = append(sInterceptors, authenticator.GRPCStreamServerInterceptor) + uInterceptors = append(uInterceptors, func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + return authUnaryServerInterceptor(ctx, req, info, handler, authenticator.Authenticate) + }) + sInterceptors = append(sInterceptors, func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + return authStreamServerInterceptor(srv, ss, info, handler, authenticator.Authenticate) + }) } // Enable OpenTelemetry observability plugin. @@ -395,3 +403,34 @@ func contextWithClient(ctx context.Context) context.Context { } return client.NewContext(ctx, cl) } + +func authUnaryServerInterceptor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler, authenticate configauth.AuthenticateFunc) (interface{}, error) { + headers, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, errMetadataNotFound + } + + ctx, err := authenticate(ctx, headers) + if err != nil { + return nil, err + } + + return handler(ctx, req) +} + +func authStreamServerInterceptor(srv interface{}, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler, authenticate configauth.AuthenticateFunc) error { + ctx := stream.Context() + headers, ok := metadata.FromIncomingContext(ctx) + if !ok { + return errMetadataNotFound + } + + ctx, err := authenticate(ctx, headers) + if err != nil { + return err + } + + wrapped := middleware.WrapServerStream(stream) + wrapped.WrappedContext = ctx + return handler(srv, wrapped) +} diff --git a/config/configgrpc/configgrpc_test.go b/config/configgrpc/configgrpc_test.go index 316c972f77f..52d98ceabc6 100644 --- a/config/configgrpc/configgrpc_test.go +++ b/config/configgrpc/configgrpc_test.go @@ -16,6 +16,7 @@ package configgrpc import ( "context" + "fmt" "io/ioutil" "net" "os" @@ -27,6 +28,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" "go.opentelemetry.io/collector/client" @@ -748,6 +750,165 @@ func TestClientInfoInterceptors(t *testing.T) { } } +func TestDefaultUnaryInterceptorAuthSucceeded(t *testing.T) { + // prepare + handlerCalled := false + authCalled := false + authFunc := func(context.Context, map[string][]string) (context.Context, error) { + authCalled = true + ctx := client.NewContext(context.Background(), client.Info{ + Addr: &net.IPAddr{IP: net.IPv4(1, 2, 3, 4)}, + }) + + return ctx, nil + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + handlerCalled = true + cl := client.FromContext(ctx) + assert.Equal(t, "1.2.3.4", cl.Addr.String()) + return nil, nil + } + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) + + // test + res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, authFunc) + + // verify + assert.Nil(t, res) + assert.NoError(t, err) + assert.True(t, authCalled) + assert.True(t, handlerCalled) +} + +func TestDefaultUnaryInterceptorAuthFailure(t *testing.T) { + // prepare + authCalled := false + expectedErr := fmt.Errorf("not authenticated") + authFunc := func(context.Context, map[string][]string) (context.Context, error) { + authCalled = true + return context.Background(), expectedErr + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + assert.FailNow(t, "the handler should not have been called on auth failure!") + return nil, nil + } + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) + + // test + res, err := authUnaryServerInterceptor(ctx, nil, &grpc.UnaryServerInfo{}, handler, authFunc) + + // verify + assert.Nil(t, res) + assert.Equal(t, expectedErr, err) + assert.True(t, authCalled) +} + +func TestDefaultUnaryInterceptorMissingMetadata(t *testing.T) { + // prepare + authFunc := func(context.Context, map[string][]string) (context.Context, error) { + assert.FailNow(t, "the auth func should not have been called!") + return context.Background(), nil + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + assert.FailNow(t, "the handler should not have been called!") + return nil, nil + } + + // test + res, err := authUnaryServerInterceptor(context.Background(), nil, &grpc.UnaryServerInfo{}, handler, authFunc) + + // verify + assert.Nil(t, res) + assert.Equal(t, errMetadataNotFound, err) +} + +func TestDefaultStreamInterceptorAuthSucceeded(t *testing.T) { + // prepare + handlerCalled := false + authCalled := false + authFunc := func(context.Context, map[string][]string) (context.Context, error) { + authCalled = true + ctx := client.NewContext(context.Background(), client.Info{ + Addr: &net.IPAddr{IP: net.IPv4(1, 2, 3, 4)}, + }) + return ctx, nil + } + handler := func(srv interface{}, stream grpc.ServerStream) error { + // ensure that the client information is propagated down to the underlying stream + cl := client.FromContext(stream.Context()) + assert.Equal(t, "1.2.3.4", cl.Addr.String()) + handlerCalled = true + return nil + } + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) + streamServer := &mockServerStream{ + ctx: ctx, + } + + // test + err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc) + + // verify + assert.NoError(t, err) + assert.True(t, authCalled) + assert.True(t, handlerCalled) +} + +func TestDefaultStreamInterceptorAuthFailure(t *testing.T) { + // prepare + authCalled := false + expectedErr := fmt.Errorf("not authenticated") + authFunc := func(context.Context, map[string][]string) (context.Context, error) { + authCalled = true + return context.Background(), expectedErr + } + handler := func(srv interface{}, stream grpc.ServerStream) error { + assert.FailNow(t, "the handler should not have been called on auth failure!") + return nil + } + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "some-auth-data")) + streamServer := &mockServerStream{ + ctx: ctx, + } + + // test + err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc) + + // verify + assert.Equal(t, expectedErr, err) + assert.True(t, authCalled) +} + +func TestDefaultStreamInterceptorMissingMetadata(t *testing.T) { + // prepare + authFunc := func(context.Context, map[string][]string) (context.Context, error) { + assert.FailNow(t, "the auth func should not have been called!") + return context.Background(), nil + } + handler := func(srv interface{}, stream grpc.ServerStream) error { + assert.FailNow(t, "the handler should not have been called!") + return nil + } + streamServer := &mockServerStream{ + ctx: context.Background(), + } + + // test + err := authStreamServerInterceptor(nil, streamServer, &grpc.StreamServerInfo{}, handler, authFunc) + + // verify + assert.Equal(t, errMetadataNotFound, err) +} + +type mockServerStream struct { + grpc.ServerStream + ctx context.Context +} + +func (m *mockServerStream) Context() context.Context { + return m.ctx +} + type grpcTraceServer struct { recordedContext context.Context } diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index 74e4f37ccb3..cd249f9e6a3 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -266,7 +266,7 @@ func (hss *HTTPServerSettings) ToServer(host component.Host, settings component. return nil, err } - handler = authenticator.HTTPInterceptor(handler) + handler = authInterceptor(handler, authenticator.Authenticate) } // Enable OpenTelemetry observability plugin. @@ -313,3 +313,15 @@ type CORSSettings struct { // preflight response for. MaxAge int `mapstructure:"max_age,omitempty"` } + +func authInterceptor(next http.Handler, authenticate configauth.AuthenticateFunc) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx, err := authenticate(r.Context(), r.Header) + if err != nil { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} diff --git a/config/confighttp/confighttp_test.go b/config/confighttp/confighttp_test.go index a43a0fd0b38..2a2f3063e31 100644 --- a/config/confighttp/confighttp_test.go +++ b/config/confighttp/confighttp_test.go @@ -782,7 +782,6 @@ func TestServerAuth(t *testing.T) { authCalled = true return ctx, nil }, - HTTPInterceptorFunc: configauth.DefaultHTTPInterceptor, }, }, }