diff --git a/.chloggen/fix-confighttp-auth-error.yaml b/.chloggen/fix-confighttp-auth-error.yaml new file mode 100644 index 00000000000..98247f7ea3b --- /dev/null +++ b/.chloggen/fix-confighttp-auth-error.yaml @@ -0,0 +1,25 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver) +component: confighttp + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Ensure http authentication server failures are handled by the provided error handler" + +# One or more tracking issues or pull requests related to the change +issues: [12666] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/config/confighttp/confighttp.go b/config/confighttp/confighttp.go index 06dd9daa560..43193c957ee 100644 --- a/config/confighttp/confighttp.go +++ b/config/confighttp/confighttp.go @@ -441,7 +441,7 @@ func (hss *ServerConfig) ToServer(_ context.Context, host component.Host, settin return nil, err } - handler = authInterceptor(handler, server, hss.Auth.RequestParameters) + handler = authInterceptor(handler, server, hss.Auth.RequestParameters, serverOpts) } if hss.CORS != nil && len(hss.CORS.AllowedOrigins) > 0 { @@ -537,7 +537,7 @@ func NewDefaultCORSConfig() *CORSConfig { return &CORSConfig{} } -func authInterceptor(next http.Handler, server extensionauth.Server, requestParams []string) http.Handler { +func authInterceptor(next http.Handler, server extensionauth.Server, requestParams []string, serverOpts *internal.ToServerOptions) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sources := r.Header query := r.URL.Query() @@ -548,7 +548,12 @@ func authInterceptor(next http.Handler, server extensionauth.Server, requestPara } ctx, err := server.Authenticate(r.Context(), sources) if err != nil { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + if serverOpts.ErrHandler != nil { + serverOpts.ErrHandler(w, r, err.Error(), http.StatusUnauthorized) + } else { + http.Error(w, err.Error(), http.StatusUnauthorized) + } + return } diff --git a/config/confighttp/confighttp_test.go b/config/confighttp/confighttp_test.go index 9d36ac5c5e1..f8da78b7131 100644 --- a/config/confighttp/confighttp_test.go +++ b/config/confighttp/confighttp_test.go @@ -1212,7 +1212,7 @@ func TestFailedServerAuth(t *testing.T) { host := &mockHost{ ext: map[component.ID]component.Component{ mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { - return ctx, errors.New("Settings failed") + return ctx, errors.New("invalid authorization") }), }, } @@ -1229,6 +1229,44 @@ func TestFailedServerAuth(t *testing.T) { assert.Equal(t, fmt.Sprintf("%v %s", http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized)), response.Result().Status) } +func TestFailedServerAuthWithErrorHandler(t *testing.T) { + // prepare + hss := ServerConfig{ + Endpoint: "localhost:0", + Auth: &AuthConfig{ + Authentication: configauth.Authentication{ + AuthenticatorID: mockID, + }, + }, + } + host := &mockHost{ + ext: map[component.ID]component.Component{ + mockID: newMockAuthServer(func(ctx context.Context, _ map[string][]string) (context.Context, error) { + return ctx, errors.New("invalid authorization") + }), + }, + } + + eh := func(w http.ResponseWriter, _ *http.Request, err string, statusCode int) { + assert.Equal(t, http.StatusUnauthorized, statusCode) + // custom error handler uses real error string + assert.Equal(t, "invalid authorization", err) + // custom error handler changes returned status code + http.Error(w, err, http.StatusInternalServerError) + } + + srv, err := hss.ToServer(context.Background(), host, componenttest.NewNopTelemetrySettings(), http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}), WithErrorHandler(eh)) + require.NoError(t, err) + + // tt + response := &httptest.ResponseRecorder{} + srv.Handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/", nil)) + + // verify + assert.Equal(t, http.StatusInternalServerError, response.Result().StatusCode) + assert.Equal(t, fmt.Sprintf("%v %s", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)), response.Result().Status) +} + func TestServerWithErrorHandler(t *testing.T) { // prepare hss := ServerConfig{