From 9dc90d02d0ac39eae66171c9e2189e9693b35a1f Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Wed, 2 Jun 2021 16:18:02 -0600 Subject: [PATCH] authorize: only redirect for HTML pages (#2264) * authorize: only redirect for HTML pages * authorize: only redirect for HTML pages --- authorize/check_response.go | 26 +++++++++++++++++- authorize/check_response_test.go | 45 ++++++++++++++++++++++++++++++++ authorize/grpc.go | 2 +- go.mod | 1 + go.sum | 2 ++ 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/authorize/check_response.go b/authorize/check_response.go index 9c8efd67c..5a6c43fad 100644 --- a/authorize/check_response.go +++ b/authorize/check_response.go @@ -14,6 +14,7 @@ import ( envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" "github.com/golang/protobuf/ptypes/wrappers" + "github.com/tniswong/go.rfcx/rfc7231" "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/codes" @@ -104,7 +105,7 @@ func (a *Authorize) deniedResponse( }, nil } -func (a *Authorize) redirectResponse(ctx context.Context, in *envoy_service_auth_v3.CheckRequest) (*envoy_service_auth_v3.CheckResponse, error) { +func (a *Authorize) requireLoginResponse(ctx context.Context, in *envoy_service_auth_v3.CheckRequest) (*envoy_service_auth_v3.CheckResponse, error) { opts := a.currentOptions.Load() state := a.state.Load() authenticateURL, err := opts.GetAuthenticateURL() @@ -112,6 +113,10 @@ func (a *Authorize) redirectResponse(ctx context.Context, in *envoy_service_auth return nil, err } + if !shouldRedirect(in) { + return a.deniedResponse(ctx, in, http.StatusUnauthorized, http.StatusText(http.StatusUnauthorized), nil) + } + signinURL := authenticateURL.ResolveReference(&url.URL{ Path: "/.pomerium/sign_in", }) @@ -180,3 +185,22 @@ func (a *Authorize) userInfoEndpointURL(in *envoy_service_auth_v3.CheckRequest) return urlutil.NewSignedURL(a.state.Load().sharedKey, debugEndpoint).Sign(), nil } + +func shouldRedirect(in *envoy_service_auth_v3.CheckRequest) bool { + requestHeaders := in.GetAttributes().GetRequest().GetHttp().GetHeaders() + if requestHeaders == nil { + return true + } + + a, err := rfc7231.ParseAccept(requestHeaders["accept"]) + if err != nil { + return true + } + + mediaType, ok := a.MostAcceptable([]string{"text/html", "application/json", "text/plain"}) + if !ok { + return true + } + + return mediaType == "text/html" +} diff --git a/authorize/check_response_test.go b/authorize/check_response_test.go index a41754811..e722315d9 100644 --- a/authorize/check_response_test.go +++ b/authorize/check_response_test.go @@ -165,3 +165,48 @@ func mustParseWeightedURLs(t *testing.T, urls ...string) []config.WeightedURL { require.NoError(t, err) return wu } + +func TestRequireLogin(t *testing.T) { + opt := config.NewDefaultOptions() + opt.AuthenticateURLString = "https://authenticate.example.com" + opt.DataBrokerURLString = "https://databroker.example.com" + opt.SharedKey = "E8wWIMnihUx+AUfRegAQDNs8eRb3UrB5G3zlJW9XJDM=" + a, err := New(&config.Config{Options: opt}) + require.NoError(t, err) + + t.Run("accept empty", func(t *testing.T) { + res, err := a.requireLoginResponse(context.Background(), &envoy_service_auth_v3.CheckRequest{}) + require.NoError(t, err) + assert.Equal(t, http.StatusFound, int(res.GetDeniedResponse().GetStatus().GetCode())) + }) + t.Run("accept html", func(t *testing.T) { + res, err := a.requireLoginResponse(context.Background(), &envoy_service_auth_v3.CheckRequest{ + Attributes: &envoy_service_auth_v3.AttributeContext{ + Request: &envoy_service_auth_v3.AttributeContext_Request{ + Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{ + Headers: map[string]string{ + "accept": "*/*", + }, + }, + }, + }, + }) + require.NoError(t, err) + assert.Equal(t, http.StatusFound, int(res.GetDeniedResponse().GetStatus().GetCode())) + }) + t.Run("accept json", func(t *testing.T) { + res, err := a.requireLoginResponse(context.Background(), &envoy_service_auth_v3.CheckRequest{ + Attributes: &envoy_service_auth_v3.AttributeContext{ + Request: &envoy_service_auth_v3.AttributeContext_Request{ + Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{ + Headers: map[string]string{ + "accept": "application/json", + }, + }, + }, + }, + }) + require.NoError(t, err) + assert.Equal(t, http.StatusUnauthorized, int(res.GetDeniedResponse().GetStatus().GetCode())) + }) +} diff --git a/authorize/grpc.go b/authorize/grpc.go index fa6558436..5f60bbe37 100644 --- a/authorize/grpc.go +++ b/authorize/grpc.go @@ -84,7 +84,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe return a.deniedResponse(ctx, in, http.StatusForbidden, http.StatusText(http.StatusForbidden), nil) } - return a.redirectResponse(ctx, in) + return a.requireLoginResponse(ctx, in) } func getForwardAuthURL(r *http.Request) *url.URL { diff --git a/go.mod b/go.mod index c663502ca..61e626bfc 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.7.0 github.com/tklauser/go-sysconf v0.3.6 // indirect + github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da go.opencensus.io v0.23.0 diff --git a/go.sum b/go.sum index c9453a536..dcab6e2c9 100644 --- a/go.sum +++ b/go.sum @@ -637,6 +637,8 @@ github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefld github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f h1:C43EMGXFtvYf/zunHR6ivZV7Z6ytg73t0GXwYyicXMQ= +github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f/go.mod h1:N+sR0vLSCTtI6o06PMWsjMB4TVqqDttKNq4iC9wvxVY= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=