diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2023-01-25 20:36:40 +1100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-25 20:36:40 +1100 | 
| commit | 65705a646dfd31e4477af3ffb35c584eb49346a4 (patch) | |
| tree | 882b5df73348c5fc6471e57ef6787c4b04cb68f4 /internal/handlers/handler_authz_impl_forwardauth_test.go | |
| parent | 78064dec2e9b48308b71ff8862b27e6f8ded5d56 (diff) | |
feat(server): customizable authz endpoints (#4296)
This allows users to customize the authz endpoints.
Closes #2753, Fixes #3716
Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
Diffstat (limited to 'internal/handlers/handler_authz_impl_forwardauth_test.go')
| -rw-r--r-- | internal/handlers/handler_authz_impl_forwardauth_test.go | 610 | 
1 files changed, 610 insertions, 0 deletions
diff --git a/internal/handlers/handler_authz_impl_forwardauth_test.go b/internal/handlers/handler_authz_impl_forwardauth_test.go new file mode 100644 index 000000000..d7ea3baab --- /dev/null +++ b/internal/handlers/handler_authz_impl_forwardauth_test.go @@ -0,0 +1,610 @@ +package handlers + +import ( +	"fmt" +	"net/url" +	"strings" +	"testing" + +	"github.com/stretchr/testify/assert" +	"github.com/stretchr/testify/suite" +	"github.com/valyala/fasthttp" + +	"github.com/authelia/authelia/v4/internal/authorization" +	"github.com/authelia/authelia/v4/internal/middlewares" +	"github.com/authelia/authelia/v4/internal/mocks" +	"github.com/authelia/authelia/v4/internal/session" +) + +func TestRunForwardAuthAuthzSuite(t *testing.T) { +	suite.Run(t, NewForwardAuthAuthzSuite()) +} + +func NewForwardAuthAuthzSuite() *ForwardAuthAuthzSuite { +	return &ForwardAuthAuthzSuite{ +		AuthzSuite: &AuthzSuite{ +			implementation: AuthzImplForwardAuth, +			setRequest:     setRequestForwardAuth, +		}, +	} +} + +type ForwardAuthAuthzSuite struct { +	*AuthzSuite +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsDeny() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, pairURI := range []urlpair{ +				{s.RequireParseRequestURI("https://one-factor.example.com"), s.RequireParseRequestURI("https://auth.example.com/")}, +				{s.RequireParseRequestURI("https://one-factor.example.com/subpath"), s.RequireParseRequestURI("https://auth.example.com/")}, +				{s.RequireParseRequestURI("https://one-factor.example2.com"), s.RequireParseRequestURI("https://auth.example2.com/")}, +				{s.RequireParseRequestURI("https://one-factor.example2.com/subpath"), s.RequireParseRequestURI("https://auth.example2.com/")}, +			} { +				t.Run(pairURI.TargetURI.String(), func(t *testing.T) { +					expected := s.RequireParseRequestURI(pairURI.AutheliaURI.String()) + +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false) + +					authz.Handler(mock.Ctx) + +					switch method { +					case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead: +						assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode()) +					default: +						assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode()) +					} + +					query := expected.Query() +					query.Set(queryArgRD, pairURI.TargetURI.String()) +					query.Set(queryArgRM, method) +					expected.RawQuery = query.Encode() + +					assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsOverrideAutheliaURLDeny() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, pairURI := range []urlpair{ +				{s.RequireParseRequestURI("https://one-factor.example.com"), s.RequireParseRequestURI("https://auth-from-override.example.com/")}, +				{s.RequireParseRequestURI("https://one-factor.example.com/subpath"), s.RequireParseRequestURI("https://auth-from-override.example.com/")}, +				{s.RequireParseRequestURI("https://one-factor.example2.com"), s.RequireParseRequestURI("https://auth-from-override.example2.com/")}, +				{s.RequireParseRequestURI("https://one-factor.example2.com/subpath"), s.RequireParseRequestURI("https://auth-from-override.example2.com/")}, +			} { +				t.Run(pairURI.TargetURI.String(), func(t *testing.T) { +					expected := s.RequireParseRequestURI(pairURI.AutheliaURI.String()) + +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					mock.Ctx.RequestCtx.QueryArgs().Set("authelia_url", pairURI.AutheliaURI.String()) +					s.setRequest(mock.Ctx, method, pairURI.TargetURI, true, false) + +					authz.Handler(mock.Ctx) + +					switch method { +					case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead: +						assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode()) +					default: +						assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode()) +					} + +					query := expected.Query() +					query.Set(queryArgRD, pairURI.TargetURI.String()) +					query.Set(queryArgRM, method) +					expected.RawQuery = query.Encode() + +					assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsMissingAutheliaURLDeny() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, targetURI := range []*url.URL{ +				s.RequireParseRequestURI("https://bypass.example.com"), +				s.RequireParseRequestURI("https://bypass.example.com/subpath"), +				s.RequireParseRequestURI("https://bypass.example2.com"), +				s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +			} { +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					s.setRequest(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, "", string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsXHRDeny() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for xname, x := range testXHR { +				t.Run(xname, func(t *testing.T) { +					for _, pairURI := range []urlpair{ +						{s.RequireParseRequestURI("https://one-factor.example.com"), s.RequireParseRequestURI("https://auth.example.com/")}, +						{s.RequireParseRequestURI("https://one-factor.example.com/subpath"), s.RequireParseRequestURI("https://auth.example.com/")}, +						{s.RequireParseRequestURI("https://one-factor.example2.com"), s.RequireParseRequestURI("https://auth.example2.com/")}, +						{s.RequireParseRequestURI("https://one-factor.example2.com/subpath"), s.RequireParseRequestURI("https://auth.example2.com/")}, +					} { +						t.Run(pairURI.TargetURI.String(), func(t *testing.T) { +							expected := s.RequireParseRequestURI(pairURI.AutheliaURI.String()) + +							authz := s.Builder().Build() + +							mock := mocks.NewMockAutheliaCtx(t) + +							defer mock.Close() + +							for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +								mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +							} + +							mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +							s.setRequest(mock.Ctx, method, pairURI.TargetURI, x, x) + +							authz.Handler(mock.Ctx) + +							assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) + +							query := expected.Query() +							query.Set(queryArgRD, pairURI.TargetURI.String()) +							query.Set(queryArgRM, method) +							expected.RawQuery = query.Encode() + +							assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))) +						}) +					} +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleInvalidMethodCharsDeny() { +	for _, method := range testRequestMethods { +		method += "z" + +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, targetURI := range []*url.URL{ +				s.RequireParseRequestURI("https://bypass.example.com"), +				s.RequireParseRequestURI("https://bypass.example.com/subpath"), +				s.RequireParseRequestURI("https://bypass.example2.com"), +				s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +			} { +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					s.setRequest(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleMissingHostDeny() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			authz := s.Builder().Build() + +			mock := mocks.NewMockAutheliaCtx(t) + +			defer mock.Close() + +			for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +				mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +			} + +			mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +			mock.Ctx.Request.Header.Set("X-Forwarded-Method", method) +			mock.Ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, "https") +			mock.Ctx.Request.Header.Del(fasthttp.HeaderXForwardedHost) +			mock.Ctx.Request.Header.Set("X-Forwarded-Uri", "/") +			mock.Ctx.Request.Header.Set(fasthttp.HeaderAccept, "text/html; charset=utf-8") + +			authz.Handler(mock.Ctx) + +			assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +			assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsAllow() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, targetURI := range []*url.URL{ +				s.RequireParseRequestURI("https://bypass.example.com"), +				s.RequireParseRequestURI("https://bypass.example.com/subpath"), +				s.RequireParseRequestURI("https://bypass.example2.com"), +				s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +			} { +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					s.setRequest(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsWithMethodsACL() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, methodACL := range testRequestMethods { +				targetURI := s.RequireParseRequestURI(fmt.Sprintf("https://bypass-%s.example.com", strings.ToLower(methodACL))) +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					s.setRequest(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					if method == methodACL { +						assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode()) +						assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +					} else { +						expected := s.RequireParseRequestURI("https://auth.example.com/") + +						switch method { +						case fasthttp.MethodGet, fasthttp.MethodOptions, fasthttp.MethodHead: +							assert.Equal(t, fasthttp.StatusFound, mock.Ctx.Response.StatusCode()) +						default: +							assert.Equal(t, fasthttp.StatusSeeOther, mock.Ctx.Response.StatusCode()) +						} + +						query := expected.Query() +						query.Set(queryArgRD, targetURI.String()) +						query.Set(queryArgRM, method) +						expected.RawQuery = query.Encode() + +						assert.Equal(t, expected.String(), string(mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation))) +					} +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleAllMethodsAllowXHR() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, targetURI := range []*url.URL{ +				s.RequireParseRequestURI("https://bypass.example.com"), +				s.RequireParseRequestURI("https://bypass.example.com/subpath"), +				s.RequireParseRequestURI("https://bypass.example2.com"), +				s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +			} { +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					s.setRequest(mock.Ctx, method, targetURI, true, true) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusOK, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldHandleInvalidURLForCVE202132637() { +	testCases := []struct { +		name         string +		scheme, host []byte +		path         string +		expected     int +	}{ +		{"Should401UnauthorizedWithNullByte", +			[]byte("https"), []byte{0, 110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}, "/path-example", +			fasthttp.StatusUnauthorized, +		}, +		{"Should200OkWithoutNullByte", +			[]byte("https"), []byte{110, 111, 116, 45, 111, 110, 101, 45, 102, 97, 99, 116, 111, 114, 46, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109}, "/path-example", +			fasthttp.StatusOK, +		}, +	} + +	for _, tc := range testCases { +		s.T().Run(tc.name, func(t *testing.T) { +			for _, method := range testRequestMethods { +				t.Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					mock.Ctx.Configuration.AccessControl.DefaultPolicy = testBypass +					mock.Ctx.Providers.Authorizer = authorization.NewAuthorizer(&mock.Ctx.Configuration) + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					mock.Ctx.Request.Header.Set("X-Forwarded-Method", method) +					mock.Ctx.Request.Header.SetBytesKV([]byte(fasthttp.HeaderXForwardedProto), tc.scheme) +					mock.Ctx.Request.Header.SetBytesKV([]byte(fasthttp.HeaderXForwardedHost), tc.host) +					mock.Ctx.Request.Header.Set("X-Forwarded-Uri", tc.path) +					mock.Ctx.Request.Header.Set(fasthttp.HeaderAccept, "text/html; charset=utf-8") + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, tc.expected, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldNotHandleAuthRequestAllMethodsAllow() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("OriginalMethod%s", method), func(t *testing.T) { +			for _, targetURI := range []*url.URL{ +				s.RequireParseRequestURI("https://bypass.example.com"), +				s.RequireParseRequestURI("https://bypass.example.com/subpath"), +				s.RequireParseRequestURI("https://bypass.example2.com"), +				s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +			} { +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					setRequestAuthRequest(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldNotHandleAuthRequestAllMethodsWithMethodsACL() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, methodACL := range testRequestMethods { +				targetURI := s.RequireParseRequestURI(fmt.Sprintf("https://bypass-%s.example.com", strings.ToLower(methodACL))) +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					setRequestAuthRequest(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsAllow() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, targetURI := range []*url.URL{ +				s.RequireParseRequestURI("https://bypass.example.com"), +				s.RequireParseRequestURI("https://bypass.example.com/subpath"), +				s.RequireParseRequestURI("https://bypass.example2.com"), +				s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +			} { +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					setRequestExtAuthz(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsAllowXHR() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for xname, x := range testXHR { +				t.Run(xname, func(t *testing.T) { +					for _, targetURI := range []*url.URL{ +						s.RequireParseRequestURI("https://bypass.example.com"), +						s.RequireParseRequestURI("https://bypass.example.com/subpath"), +						s.RequireParseRequestURI("https://bypass.example2.com"), +						s.RequireParseRequestURI("https://bypass.example2.com/subpath"), +					} { +						t.Run(targetURI.String(), func(t *testing.T) { +							authz := s.Builder().Build() + +							mock := mocks.NewMockAutheliaCtx(t) + +							defer mock.Close() + +							for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +								mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +							} + +							mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +							setRequestExtAuthz(mock.Ctx, method, targetURI, x, x) + +							authz.Handler(mock.Ctx) + +							assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +							assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +						}) +					} +				}) +			} +		}) +	} +} + +func (s *ForwardAuthAuthzSuite) TestShouldNotHandleExtAuthzAllMethodsWithMethodsACL() { +	for _, method := range testRequestMethods { +		s.T().Run(fmt.Sprintf("Method%s", method), func(t *testing.T) { +			for _, methodACL := range testRequestMethods { +				targetURI := s.RequireParseRequestURI(fmt.Sprintf("https://bypass-%s.example.com", strings.ToLower(methodACL))) +				t.Run(targetURI.String(), func(t *testing.T) { +					authz := s.Builder().Build() + +					mock := mocks.NewMockAutheliaCtx(t) + +					defer mock.Close() + +					for i, cookie := range mock.Ctx.Configuration.Session.Cookies { +						mock.Ctx.Configuration.Session.Cookies[i].AutheliaURL = s.RequireParseRequestURI(fmt.Sprintf("https://auth.%s", cookie.Domain)) +					} + +					mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session, nil) + +					setRequestExtAuthz(mock.Ctx, method, targetURI, true, false) + +					authz.Handler(mock.Ctx) + +					assert.Equal(t, fasthttp.StatusUnauthorized, mock.Ctx.Response.StatusCode()) +					assert.Equal(t, []byte(nil), mock.Ctx.Response.Header.Peek(fasthttp.HeaderLocation)) +				}) +			} +		}) +	} +} + +func setRequestForwardAuth(ctx *middlewares.AutheliaCtx, method string, targetURI *url.URL, accept, xhr bool) { +	if method != "" { +		ctx.Request.Header.Set("X-Forwarded-Method", method) +	} + +	if targetURI != nil { +		ctx.Request.Header.Set(fasthttp.HeaderXForwardedProto, targetURI.Scheme) +		ctx.Request.Header.Set(fasthttp.HeaderXForwardedHost, targetURI.Host) +		ctx.Request.Header.Set("X-Forwarded-Uri", targetURI.Path) +	} + +	setRequestXHRValues(ctx, accept, xhr) +}  | 
