Skip to content
Snippets Groups Projects
Select Git revision
  • c6b2d1acd0222924c3772e0542eb16025669f428
  • master default protected
  • greenkeeper/webpack-4.10.1
  • greenkeeper/webpack-4.10.0
  • greenkeeper/webpack-4.9.2
  • greenkeeper/promise-polyfill-8.0.0
  • greenkeeper/webpack-4.9.1
  • greenkeeper/webpack-4.9.0
  • greenkeeper/webpack-manifest-plugin-2.0.3
  • greenkeeper/update-to-node-10
  • gh-pages
  • greenkeeper/webpack-4.8.3
  • greenkeeper/webpack-4.8.2
  • greenkeeper/webpack-4.7.0
  • greenkeeper/webpack-manifest-plugin-2.0.2
  • greenkeeper/webpack-manifest-plugin-2.0.1
  • greenkeeper/style-loader-0.21.0
  • greenkeeper/webpack-4.6.0
  • greenkeeper/sass-loader-7.0.1
  • greenkeeper/sass-loader-7.0.0
  • greenkeeper/webpack-manifest-plugin-2.0.0
  • 2.7.3
  • 2.7.2
  • 2.7.1
  • 2.7.0
  • 2.6.6
  • 2.6.5
  • 2.6.4
  • 2.6.3
  • 2.6.2
  • 2.6.1
  • 2.6.0
  • 2.5.5
  • 2.5.4
  • 2.5.3
  • 2.5.2
  • 2.5.1
  • 2.5.0
  • 2.4.0
  • 2.3.0
  • 2.2.6
41 results

setup.py

Blame
  • middleware_test.go 31.67 KiB
    /*
    Copyright 2015 All rights reserved.
    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 main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"log"
    	"net"
    	"net/http"
    	"strings"
    	"testing"
    	"time"
    
    	"github.com/gambol99/go-oidc/jose"
    	"github.com/go-resty/resty"
    	"github.com/rs/cors"
    	"github.com/stretchr/testify/assert"
    	"go.uber.org/zap"
    )
    
    type fakeRequest struct {
    	BasicAuth               bool
    	Cookies                 []*http.Cookie
    	Expires                 time.Duration
    	FormValues              map[string]string
    	Groups                  []string
    	HasCookieToken          bool
    	HasLogin                bool
    	HasToken                bool
    	Headers                 map[string]string
    	Method                  string
    	NotSigned               bool
    	OnResponse              func(int, *resty.Request, *resty.Response)
    	Password                string
    	ProxyProtocol           string
    	ProxyRequest            bool
    	RawToken                string
    	Redirects               bool
    	Roles                   []string
    	TokenClaims             jose.Claims
    	URI                     string
    	URL                     string
    	Username                string
    	ExpectedCode            int
    	ExpectedContent         string
    	ExpectedContentContains string
    	ExpectedCookies         map[string]string
    	ExpectedHeaders         map[string]string
    	ExpectedProxyHeaders    map[string]string
    	ExpectedLocation        string
    	ExpectedProxy           bool
    }
    
    type fakeProxy struct {
    	config  *Config
    	idp     *fakeAuthServer
    	proxy   *oauthProxy
    	cookies map[string]*http.Cookie
    }
    
    func newFakeProxy(c *Config) *fakeProxy {
    	log.SetOutput(ioutil.Discard)
    	if c == nil {
    		c = newFakeKeycloakConfig()
    	}
    	auth := newFakeAuthServer()
    	c.DiscoveryURL = auth.getLocation()
    	c.RevocationEndpoint = auth.getRevocationURL()
    	c.Verbose = true
    	proxy, err := newProxy(c)
    	if err != nil {
    		panic("failed to create fake proxy service, error: " + err.Error())
    	}
    	proxy.log = zap.NewNop()
    	proxy.upstream = &fakeUpstreamService{}
    	if err = proxy.Run(); err != nil {
    		panic("failed to create the proxy service, error: " + err.Error())
    	}
    	c.RedirectionURL = fmt.Sprintf("http://%s", proxy.listener.Addr().String())
    	// step: we need to update the client configs
    	if proxy.client, proxy.idp, proxy.idpClient, err = proxy.newOpenIDClient(); err != nil {
    		panic("failed to recreate the openid client, error: " + err.Error())
    	}
    
    	return &fakeProxy{c, auth, proxy, make(map[string]*http.Cookie)}
    }
    
    func (f *fakeProxy) getServiceURL() string {
    	return fmt.Sprintf("http://%s", f.proxy.listener.Addr().String())
    }
    
    // RunTests performs a series of requests against a fake proxy service
    func (f *fakeProxy) RunTests(t *testing.T, requests []fakeRequest) {
    	defer func() {
    		f.idp.Close()
    		f.proxy.server.Close()
    	}()
    
    	for i, c := range requests {
    		var upstream fakeUpstreamResponse
    
    		f.config.NoRedirects = !c.Redirects
    		// we need to set any defaults
    		if c.Method == "" {
    			c.Method = http.MethodGet
    		}
    		// create a http client
    		client := resty.New()
    		request := client.SetRedirectPolicy(resty.NoRedirectPolicy()).R()
    
    		if c.ProxyProtocol != "" {
    			client.SetTransport(&http.Transport{
    				Dial: func(network, addr string) (net.Conn, error) {
    					conn, err := net.Dial("tcp", addr)
    					if err != nil {
    						return nil, err
    					}
    					header := fmt.Sprintf("PROXY TCP4 %s 10.0.0.1 1000 2000\r\n", c.ProxyProtocol)
    					conn.Write([]byte(header))
    
    					return conn, nil
    				},
    			})
    		}
    
    		// are we performing a oauth login beforehand
    		if c.HasLogin {
    			if err := f.performUserLogin(c.URI); err != nil {
    				t.Errorf("case %d, unable to login to oauth server, error: %s", i, err)
    				return
    			}
    		}
    		if len(f.cookies) > 0 {
    			for _, k := range f.cookies {
    				client.SetCookie(k)
    			}
    		}
    		if c.ExpectedProxy {
    			request.SetResult(&upstream)
    		}
    		if c.ProxyRequest {
    			request.SetProxy(f.getServiceURL())
    		}
    		if c.BasicAuth {
    			request.SetBasicAuth(c.Username, c.Password)
    		}
    		if c.RawToken != "" {
    			setRequestAuthentication(f.config, client, request, &c, c.RawToken)
    		}
    		if len(c.Cookies) > 0 {
    			client.SetCookies(c.Cookies)
    		}
    		if len(c.Headers) > 0 {
    			request.SetHeaders(c.Headers)
    		}
    		if c.FormValues != nil {
    			request.SetFormData(c.FormValues)
    		}
    		if c.HasToken {
    			token := newTestToken(f.idp.getLocation())
    			if c.TokenClaims != nil && len(c.TokenClaims) > 0 {
    				token.merge(c.TokenClaims)
    			}
    			if len(c.Roles) > 0 {
    				token.addRealmRoles(c.Roles)
    			}
    			if len(c.Groups) > 0 {
    				token.addGroups(c.Groups)
    			}
    			if c.Expires > 0 || c.Expires < 0 {
    				token.setExpiration(time.Now().Add(c.Expires))
    			}
    			if c.NotSigned {
    				authToken := token.getToken()
    				setRequestAuthentication(f.config, client, request, &c, authToken.Encode())
    			} else {
    				signed, _ := f.idp.signToken(token.claims)
    				setRequestAuthentication(f.config, client, request, &c, signed.Encode())
    			}
    		}
    
    		// step: execute the request
    		var resp *resty.Response
    		var err error
    		switch c.URL {
    		case "":
    			resp, err = request.Execute(c.Method, f.getServiceURL()+c.URI)
    		default:
    			resp, err = request.Execute(c.Method, c.URL)
    		}
    		if err != nil {
    			if !strings.Contains(err.Error(), "Auto redirect is disable") {
    				assert.NoError(t, err, "case %d, unable to make request, error: %s", i, err)
    				continue
    			}
    		}
    		status := resp.StatusCode()
    		if c.ExpectedCode != 0 {
    			assert.Equal(t, c.ExpectedCode, status, "case %d, expected status code: %d, got: %d", i, c.ExpectedCode, status)
    		}
    		if c.ExpectedLocation != "" {
    			l := resp.Header().Get("Location")
    			assert.Equal(t, c.ExpectedLocation, l, "case %d, expected location: %s, got: %s", i, c.ExpectedLocation, l)
    		}
    		if len(c.ExpectedHeaders) > 0 {
    			for k, v := range c.ExpectedHeaders {
    				e := resp.Header().Get(k)
    				assert.Equal(t, v, e, "case %d, expected header %s=%s, got: %s", i, k, v, e)
    			}
    		}
    		if c.ExpectedProxy {
    			assert.NotEmpty(t, resp.Header().Get(testProxyAccepted), "case %d, did not proxy request", i)
    		} else {
    			assert.Empty(t, resp.Header().Get(testProxyAccepted), "case %d, should NOT proxy request", i)
    		}
    		if c.ExpectedProxyHeaders != nil && len(c.ExpectedProxyHeaders) > 0 {
    			for k, v := range c.ExpectedProxyHeaders {
    				headers := upstream.Headers
    				assert.Equal(t, v, headers.Get(k), "case %d, expected proxy header %s=%s, got: %s", i, k, v, headers.Get(k))
    			}
    		}
    		if c.ExpectedContent != "" {
    			e := string(resp.Body())
    			assert.Equal(t, c.ExpectedContent, e, "case %d, expected content: %s, got: %s", i, c.ExpectedContent, e)
    		}
    		if c.ExpectedContentContains != "" {
    			e := string(resp.Body())
    			assert.Contains(t, e, c.ExpectedContentContains, "case %d, expected content: %s, got: %s", i, c.ExpectedContentContains, e)
    		}
    		if len(c.ExpectedCookies) > 0 {
    			for k, v := range c.ExpectedCookies {
    				cookie := findCookie(k, resp.Cookies())
    				if !assert.NotNil(t, cookie, "case %d, expected cookie %s not found", i, k) {
    					continue
    				}
    				if v != "" {
    					assert.Equal(t, cookie.Value, v, "case %d, expected cookie value: %s, got: %s", i, v, cookie.Value)
    				}
    			}
    		}
    		if c.OnResponse != nil {
    			c.OnResponse(i, request, resp)
    		}
    	}
    }
    
    func (f *fakeProxy) performUserLogin(uri string) error {
    	resp, err := makeTestCodeFlowLogin(f.getServiceURL() + uri)
    	if err != nil {
    		return err
    	}
    	for _, c := range resp.Cookies() {
    		if c.Name == f.config.CookieAccessName || c.Name == f.config.CookieRefreshName {
    			f.cookies[c.Name] = &http.Cookie{
    				Name:   c.Name,
    				Path:   "/",
    				Domain: "127.0.0.1",
    				Value:  c.Value,
    			}
    		}
    	}
    
    	return nil
    }
    
    func setRequestAuthentication(cfg *Config, client *resty.Client, request *resty.Request, c *fakeRequest, token string) {
    	switch c.HasCookieToken {
    	case true:
    		client.SetCookie(&http.Cookie{
    			Name:  cfg.CookieAccessName,
    			Path:  "/",
    			Value: token,
    		})
    	default:
    		request.SetAuthToken(token)
    	}
    }
    
    func TestMetricsMiddleware(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.EnableMetrics = true
    	cfg.LocalhostMetrics = true
    	requests := []fakeRequest{
    		{
    			URI:                     oauthURL + metricsURL,
    			ExpectedCode:            http.StatusOK,
    			ExpectedContentContains: "http_request_total",
    		},
    		{
    			URI: oauthURL + metricsURL,
    			Headers: map[string]string{
    				"X-Forwarded-For": "10.0.0.1",
    			},
    			ExpectedCode: http.StatusForbidden,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestOauthRequests(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	requests := []fakeRequest{
    		{
    			URI:          "/oauth/authorize",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{
    			URI:          "/oauth/callback",
    			Redirects:    true,
    			ExpectedCode: http.StatusBadRequest,
    		},
    		{
    			URI:          "/oauth/health",
    			Redirects:    true,
    			ExpectedCode: http.StatusOK,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestMethodExclusions(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/post",
    			Methods: []string{http.MethodPost, http.MethodPut},
    		},
    	}
    	requests := []fakeRequest{
    		{ // we should get a 401
    			URI:          "/post",
    			Method:       http.MethodPost,
    			ExpectedCode: http.StatusUnauthorized,
    		},
    		{ // we should be permitted
    			URI:           "/post",
    			Method:        http.MethodGet,
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestStrangeRoutingError(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/api/v1/events/123456789",
    			Methods: allHTTPMethods,
    			Roles:   []string{"user"},
    		},
    		{
    			URL:     "/api/v1/events/404",
    			Methods: allHTTPMethods,
    			Roles:   []string{"monitoring"},
    		},
    		{
    			URL:     "/api/v1/audit/*",
    			Methods: allHTTPMethods,
    			Roles:   []string{"auditor", "dev"},
    		},
    		{
    			URL:     "/*",
    			Methods: allHTTPMethods,
    			Roles:   []string{"dev"},
    		},
    	}
    	requests := []fakeRequest{
    		{ // should work
    			URI:           "/api/v1/events/123456789",
    			HasToken:      true,
    			Redirects:     true,
    			Roles:         []string{"user"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{ // should break with bad role
    			URI:          "/api/v1/events/123456789",
    			HasToken:     true,
    			Redirects:    true,
    			Roles:        []string{"bad_role"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // good
    			URI:           "/api/v1/events/404",
    			HasToken:      true,
    			Redirects:     false,
    			Roles:         []string{"monitoring", "test"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{ // this should fail with no roles - hits catch all
    			URI:          "/api/v1/event/1000",
    			Redirects:    false,
    			ExpectedCode: http.StatusUnauthorized,
    		},
    		{ // this should fail with bad role - hits catch all
    			URI:          "/api/v1/event/1000",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{"bad"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // should work with catch-all
    			URI:           "/api/v1/event/1000",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{"dev"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    	}
    
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestNoProxyingRequests(t *testing.T) {
    	c := newFakeKeycloakConfig()
    	c.Resources = []*Resource{
    		{
    			URL:     "/*",
    			Methods: allHTTPMethods,
    		},
    	}
    	requests := []fakeRequest{
    		{ // check for escaping
    			URI:          "/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for escaping
    			URI:          "/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for escaping
    			URI:          "/../%2e",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for escaping
    			URI:          "",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    	}
    	newFakeProxy(c).RunTests(t, requests)
    }
    
    func TestStrangeAdminRequests(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/admin*",
    			Methods: allHTTPMethods,
    			Roles:   []string{fakeAdminRole},
    		},
    	}
    	requests := []fakeRequest{
    		{ // check for escaping
    			URI:          "//admin%2Ftest",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for escaping
    			URI:          "///admin/../admin//%2Ftest",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for escaping
    			URI:          "/admin%2Ftest",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for prefix slashs
    			URI:          "//admin/test",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for double slashs
    			URI:          "/admin//test",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for double slashs no redirects
    			URI:          "/admin//test",
    			Redirects:    false,
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check for dodgy url
    			URI:          "//admin/../admin/test",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check for it works
    			URI:           "//admin/test",
    			HasToken:      true,
    			Roles:         []string{fakeAdminRole},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{ // check for is doens't work
    			URI:          "//admin//test",
    			HasToken:     true,
    			Roles:        []string{"bad"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:          "/help/../admin/test/21",
    			Redirects:    false,
    			ExpectedCode: http.StatusUnauthorized,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestWhiteListedRequests(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/*",
    			Methods: allHTTPMethods,
    			Roles:   []string{fakeTestRole},
    		},
    		{
    			URL:         "/whitelist*",
    			WhiteListed: true,
    			Methods:     allHTTPMethods,
    		},
    	}
    	requests := []fakeRequest{
    		{ // check whitelisted is passed
    			URI:           "/whitelist",
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // check whitelisted is passed
    			URI:           "/whitelist/test",
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{
    			URI:          "/test",
    			HasToken:     true,
    			Roles:        []string{"nothing"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:          "/",
    			ExpectedCode: http.StatusUnauthorized,
    		},
    		{
    			URI:           "/",
    			HasToken:      true,
    			ExpectedProxy: true,
    			Roles:         []string{fakeTestRole},
    			ExpectedCode:  http.StatusOK,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestGroupPermissionsMiddleware(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/with_role_and_group*",
    			Methods: allHTTPMethods,
    			Groups:  []string{"admin"},
    			Roles:   []string{"admin"},
    		},
    		{
    			URL:     "/with_group*",
    			Methods: allHTTPMethods,
    			Groups:  []string{"admin"},
    		},
    		{
    			URL:     "/with_many_groups*",
    			Methods: allHTTPMethods,
    			Groups:  []string{"admin", "user", "tester"},
    		},
    		{
    			URL:     "/*",
    			Methods: allHTTPMethods,
    			Roles:   []string{"user"},
    		},
    	}
    	requests := []fakeRequest{
    		{
    			URI:          "/",
    			ExpectedCode: http.StatusUnauthorized,
    		},
    		{
    			URI:          "/with_role_and_group/test",
    			HasToken:     true,
    			Roles:        []string{"admin"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:          "/with_role_and_group/test",
    			HasToken:     true,
    			Groups:       []string{"admin"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/with_role_and_group/test",
    			HasToken:      true,
    			Groups:        []string{"admin"},
    			Roles:         []string{"admin"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:          "/with_group/hello",
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:          "/with_groupdd",
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:          "/with_group/hello",
    			HasToken:     true,
    			Groups:       []string{"bad"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/with_group/hello",
    			HasToken:      true,
    			Groups:        []string{"admin"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:           "/with_group/hello",
    			HasToken:      true,
    			Groups:        []string{"test", "admin"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:          "/with_many_groups/test",
    			HasToken:     true,
    			Groups:       []string{"bad"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/with_many_groups/test",
    			HasToken:      true,
    			Groups:        []string{"user"},
    			Roles:         []string{"test"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:           "/with_many_groups/test",
    			HasToken:      true,
    			Groups:        []string{"tester", "user"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:           "/with_many_groups/test",
    			HasToken:      true,
    			Groups:        []string{"bad", "user"},
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestRolePermissionsMiddleware(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/admin*",
    			Methods: allHTTPMethods,
    			Roles:   []string{fakeAdminRole},
    		},
    		{
    			URL:     "/test*",
    			Methods: []string{"GET"},
    			Roles:   []string{fakeTestRole},
    		},
    		{
    			URL:     "/test_admin_role*",
    			Methods: []string{"GET"},
    			Roles:   []string{fakeAdminRole, fakeTestRole},
    		},
    		{
    			URL:     "/section/*",
    			Methods: allHTTPMethods,
    			Roles:   []string{fakeAdminRole},
    		},
    		{
    			URL:     "/section/one",
    			Methods: allHTTPMethods,
    			Roles:   []string{"one"},
    		},
    		{
    			URL:     "/whitelist",
    			Methods: []string{"GET"},
    			Roles:   []string{},
    		},
    		{
    			URL:     "/*",
    			Methods: allHTTPMethods,
    			Roles:   []string{fakeTestRole},
    		},
    	}
    	requests := []fakeRequest{
    		{
    			URI:          "/",
    			ExpectedCode: http.StatusUnauthorized,
    		},
    		{ // check for redirect
    			URI:          "/",
    			Redirects:    true,
    			ExpectedCode: http.StatusTemporaryRedirect,
    		},
    		{ // check with a token but not test role
    			URI:          "/",
    			Redirects:    false,
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check with a token and wrong roles
    			URI:          "/",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{"one", "two"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // token, wrong roles
    			URI:          "/test",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{"bad_role"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // token, but post method
    			URI:           "/test",
    			Method:        http.MethodPost,
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeTestRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // check with correct token
    			URI:           "/test",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeTestRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // check with correct token on base
    			URI:           "/",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeTestRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // check with correct token, not signed
    			URI:          "/",
    			Redirects:    false,
    			HasToken:     true,
    			NotSigned:    true,
    			Roles:        []string{fakeTestRole},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check with correct token, signed
    			URI:          "/admin/page",
    			Method:       http.MethodPost,
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{fakeTestRole},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check with correct token, signed, wrong roles (10)
    			URI:          "/admin/page",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{fakeTestRole},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check with correct token, signed, wrong roles
    			URI:           "/admin/page",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeTestRole, fakeAdminRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // strange url
    			URI:          "/admin/..//admin/page",
    			Redirects:    false,
    			ExpectedCode: http.StatusUnauthorized,
    		},
    		{ // strange url, token
    			URI:          "/admin/../admin",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{"hehe"},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // strange url, token
    			URI:          "/test/../admin",
    			Redirects:    false,
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // strange url, token, role (15)
    			URI:           "/test/../admin",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeAdminRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // strange url, token, but good token
    			URI:           "/test/../admin",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeAdminRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{ // strange url, token, wrong roles
    			URI:          "/test/../admin",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{fakeTestRole},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check with a token admin test role
    			URI:          "/test_admin_role",
    			Redirects:    false,
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{ // check with a token but without both roles
    			URI:          "/test_admin_role",
    			Redirects:    false,
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    			Roles:        []string{fakeAdminRole},
    		},
    		{ // check with a token with both roles (20)
    			URI:           "/test_admin_role",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeAdminRole, fakeTestRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{
    			URI:          "/section/test1",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/section/test",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{fakeTestRole, fakeAdminRole},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    		{
    			URI:          "/section/one",
    			Redirects:    false,
    			HasToken:     true,
    			Roles:        []string{fakeTestRole, fakeAdminRole},
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/section/one",
    			Redirects:     false,
    			HasToken:      true,
    			Roles:         []string{"one"},
    			ExpectedCode:  http.StatusOK,
    			ExpectedProxy: true,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    func TestCrossSiteHandler(t *testing.T) {
    	cases := []struct {
    		Cors    cors.Options
    		Request fakeRequest
    	}{
    		{
    			Cors: cors.Options{
    				AllowedOrigins: []string{"*"},
    			},
    			Request: fakeRequest{
    				URI: fakeAuthAllURL,
    				Headers: map[string]string{
    					"Origin": "127.0.0.1",
    				},
    				ExpectedHeaders: map[string]string{
    					"Access-Control-Allow-Origin": "*",
    				},
    			},
    		},
    		{
    			Cors: cors.Options{
    				AllowedOrigins: []string{"*", "https://examples.com"},
    			},
    			Request: fakeRequest{
    				URI: fakeAuthAllURL,
    				Headers: map[string]string{
    					"Origin": "127.0.0.1",
    				},
    				ExpectedHeaders: map[string]string{
    					"Access-Control-Allow-Origin": "*",
    				},
    			},
    		},
    		{
    			Cors: cors.Options{
    				AllowedOrigins: []string{"*"},
    				AllowedMethods: []string{"GET", "POST"},
    			},
    			Request: fakeRequest{
    				URI:    fakeAuthAllURL,
    				Method: http.MethodOptions,
    				Headers: map[string]string{
    					"Origin":                        "127.0.0.1",
    					"Access-Control-Request-Method": "GET",
    				},
    				ExpectedHeaders: map[string]string{
    					"Access-Control-Allow-Origin":  "*",
    					"Access-Control-Allow-Methods": "GET",
    				},
    			},
    		},
    	}
    
    	for _, c := range cases {
    		cfg := newFakeKeycloakConfig()
    		cfg.CorsCredentials = c.Cors.AllowCredentials
    		cfg.CorsExposedHeaders = c.Cors.ExposedHeaders
    		cfg.CorsHeaders = c.Cors.AllowedHeaders
    		cfg.CorsMaxAge = time.Duration(time.Duration(c.Cors.MaxAge) * time.Second)
    		cfg.CorsMethods = c.Cors.AllowedMethods
    		cfg.CorsOrigins = c.Cors.AllowedOrigins
    
    		newFakeProxy(cfg).RunTests(t, []fakeRequest{c.Request})
    	}
    }
    
    func TestCheckRefreshTokens(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.EnableRefreshTokens = true
    	cfg.EncryptionKey = "ZSeCYDUxIlhDrmPpa1Ldc7il384esSF2"
    	fn := func(no int, req *resty.Request, resp *resty.Response) {
    		if no == 0 {
    			<-time.After(1000 * time.Millisecond)
    		}
    	}
    	p := newFakeProxy(cfg)
    	p.idp.setTokenExpiration(time.Duration(1000 * time.Millisecond))
    
    	requests := []fakeRequest{
    		{
    			URI:           fakeAuthAllURL,
    			HasLogin:      true,
    			Redirects:     true,
    			OnResponse:    fn,
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:             fakeAuthAllURL,
    			Redirects:       false,
    			ExpectedProxy:   true,
    			ExpectedCode:    http.StatusOK,
    			ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
    		},
    	}
    	p.RunTests(t, requests)
    }
    
    func TestCustomHeadersHandler(t *testing.T) {
    	requests := []struct {
    		Match   []string
    		Request fakeRequest
    	}{
    		{
    			Match: []string{"subject", "userid", "email", "username"},
    			Request: fakeRequest{
    				URI:      fakeAuthAllURL,
    				HasToken: true,
    				TokenClaims: jose.Claims{
    					"sub":                "test-subject",
    					"username":           "rohith",
    					"preferred_username": "rohith",
    					"email":              "gambol99@gmail.com",
    				},
    				ExpectedProxyHeaders: map[string]string{
    					"X-Auth-Subject":  "test-subject",
    					"X-Auth-Userid":   "rohith",
    					"X-Auth-Email":    "gambol99@gmail.com",
    					"X-Auth-Username": "rohith",
    				},
    				ExpectedProxy: true,
    				ExpectedCode:  http.StatusOK,
    			},
    		},
    		{
    			Match: []string{"given_name", "family_name"},
    			Request: fakeRequest{
    				URI:      fakeAuthAllURL,
    				HasToken: true,
    				TokenClaims: jose.Claims{
    					"email":              "gambol99@gmail.com",
    					"name":               "Rohith Jayawardene",
    					"family_name":        "Jayawardene",
    					"preferred_username": "rjayawardene",
    					"given_name":         "Rohith",
    				},
    				ExpectedProxyHeaders: map[string]string{
    					"X-Auth-Given-Name":  "Rohith",
    					"X-Auth-Family-Name": "Jayawardene",
    				},
    				ExpectedProxy: true,
    				ExpectedCode:  http.StatusOK,
    			},
    		},
    	}
    	for _, c := range requests {
    		cfg := newFakeKeycloakConfig()
    		cfg.AddClaims = c.Match
    		newFakeProxy(cfg).RunTests(t, []fakeRequest{c.Request})
    	}
    }
    
    func TestAdmissionHandlerRoles(t *testing.T) {
    	cfg := newFakeKeycloakConfig()
    	cfg.NoRedirects = true
    	cfg.Resources = []*Resource{
    		{
    			URL:     "/admin",
    			Methods: allHTTPMethods,
    			Roles:   []string{"admin"},
    		},
    		{
    			URL:     "/test",
    			Methods: []string{"GET"},
    			Roles:   []string{"test"},
    		},
    		{
    			URL:     "/either",
    			Methods: allHTTPMethods,
    			Roles:   []string{"admin", "test"},
    		},
    		{
    			URL:     "/",
    			Methods: allHTTPMethods,
    		},
    	}
    	requests := []fakeRequest{
    		{
    			URI:          "/admin",
    			Roles:        []string{},
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/admin",
    			Roles:         []string{"admin"},
    			HasToken:      true,
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:           "/test",
    			Roles:         []string{"test"},
    			HasToken:      true,
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:           "/either",
    			Roles:         []string{"test", "admin"},
    			HasToken:      true,
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    		{
    			URI:          "/either",
    			Roles:        []string{"no_roles"},
    			HasToken:     true,
    			ExpectedCode: http.StatusForbidden,
    		},
    		{
    			URI:           "/",
    			HasToken:      true,
    			ExpectedProxy: true,
    			ExpectedCode:  http.StatusOK,
    		},
    	}
    	newFakeProxy(cfg).RunTests(t, requests)
    }
    
    // check to see if custom headers are hitting the upstream
    func TestCustomHeaders(t *testing.T) {
    	uri := "/admin/test"
    	requests := []struct {
    		Headers map[string]string
    		Request fakeRequest
    	}{
    		{
    			Headers: map[string]string{
    				"TestHeaderOne": "one",
    			},
    			Request: fakeRequest{
    				URI:           "/gambol99.htm",
    				ExpectedProxy: true,
    				ExpectedProxyHeaders: map[string]string{
    					"TestHeaderOne": "one",
    				},
    			},
    		},
    		{
    			Headers: map[string]string{
    				"TestHeader": "test",
    			},
    			Request: fakeRequest{
    				URI:           uri,
    				HasToken:      true,
    				ExpectedProxy: true,
    				ExpectedProxyHeaders: map[string]string{
    					"TestHeader": "test",
    				},
    			},
    		},
    		{
    			Headers: map[string]string{
    				"TestHeaderOne": "one",
    				"TestHeaderTwo": "two",
    			},
    			Request: fakeRequest{
    				URI:           uri,
    				HasToken:      true,
    				ExpectedProxy: true,
    				ExpectedProxyHeaders: map[string]string{
    					"TestHeaderOne": "one",
    					"TestHeaderTwo": "two",
    				},
    			},
    		},
    	}
    	for _, c := range requests {
    		cfg := newFakeKeycloakConfig()
    		cfg.Resources = []*Resource{{URL: "/admin*", Methods: allHTTPMethods}}
    		cfg.Headers = c.Headers
    		newFakeProxy(cfg).RunTests(t, []fakeRequest{c.Request})
    	}
    }
    
    func TestRolesAdmissionHandlerClaims(t *testing.T) {
    	uri := "/admin/test"
    	requests := []struct {
    		Matches map[string]string
    		Request fakeRequest
    	}{
    		{
    			Matches: map[string]string{"cal": "test"},
    			Request: fakeRequest{
    				URI:          uri,
    				HasToken:     true,
    				ExpectedCode: http.StatusForbidden,
    			},
    		},
    		{
    			Matches: map[string]string{"item": "^tes$"},
    			Request: fakeRequest{
    				URI:          uri,
    				HasToken:     true,
    				ExpectedCode: http.StatusForbidden,
    			},
    		},
    		{
    			Matches: map[string]string{"item": "^tes$"},
    			Request: fakeRequest{
    				URI:           uri,
    				HasToken:      true,
    				TokenClaims:   jose.Claims{"item": "tes"},
    				ExpectedProxy: true,
    				ExpectedCode:  http.StatusOK,
    			},
    		},
    		{
    			Matches: map[string]string{"item": "not_match"},
    			Request: fakeRequest{
    				URI:          uri,
    				HasToken:     true,
    				TokenClaims:  jose.Claims{"item": "test"},
    				ExpectedCode: http.StatusForbidden,
    			},
    		},
    		{
    			Matches: map[string]string{"item": "^test", "found": "something"},
    			Request: fakeRequest{
    				URI:          uri,
    				HasToken:     true,
    				TokenClaims:  jose.Claims{"item": "test"},
    				ExpectedCode: http.StatusForbidden,
    			},
    		},
    		{
    			Matches: map[string]string{"item": "^test", "found": "something"},
    			Request: fakeRequest{
    				URI:      uri,
    				HasToken: true,
    				TokenClaims: jose.Claims{
    					"item":  "tester",
    					"found": "something",
    				},
    				ExpectedProxy: true,
    				ExpectedCode:  http.StatusOK,
    			},
    		},
    		{
    			Matches: map[string]string{"item": ".*"},
    			Request: fakeRequest{
    				URI:           uri,
    				HasToken:      true,
    				TokenClaims:   jose.Claims{"item": "test"},
    				ExpectedProxy: true,
    				ExpectedCode:  http.StatusOK,
    			},
    		},
    		{
    			Matches: map[string]string{"item": "^t.*$"},
    			Request: fakeRequest{
    				URI:           uri,
    				HasToken:      true,
    				TokenClaims:   jose.Claims{"item": "test"},
    				ExpectedProxy: true,
    				ExpectedCode:  http.StatusOK,
    			},
    		},
    	}
    	for _, c := range requests {
    		cfg := newFakeKeycloakConfig()
    		cfg.Resources = []*Resource{{URL: "/admin*", Methods: allHTTPMethods}}
    		cfg.MatchClaims = c.Matches
    		newFakeProxy(cfg).RunTests(t, []fakeRequest{c.Request})
    	}
    }