github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/mw_basic_auth_test.go (about)

     1  package gateway
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/TykTechnologies/tyk/storage"
    11  
    12  	"github.com/TykTechnologies/tyk/config"
    13  	"github.com/TykTechnologies/tyk/test"
    14  	"github.com/TykTechnologies/tyk/user"
    15  )
    16  
    17  func genAuthHeader(username, password string) string {
    18  	toEncode := strings.Join([]string{username, password}, ":")
    19  	encodedPass := base64.StdEncoding.EncodeToString([]byte(toEncode))
    20  	return fmt.Sprintf("Basic %s", encodedPass)
    21  }
    22  
    23  func testPrepareBasicAuth(cacheDisabled bool) *user.SessionState {
    24  	session := CreateStandardSession()
    25  	session.BasicAuthData.Password = "password"
    26  	session.AccessRights = map[string]user.AccessDefinition{"test": {APIID: "test", Versions: []string{"v1"}}}
    27  	session.OrgID = "default"
    28  
    29  	BuildAndLoadAPI(func(spec *APISpec) {
    30  		spec.UseBasicAuth = true
    31  		spec.BasicAuth.DisableCaching = cacheDisabled
    32  		spec.UseKeylessAccess = false
    33  		spec.Proxy.ListenPath = "/"
    34  		spec.OrgID = "default"
    35  	})
    36  
    37  	return session
    38  }
    39  
    40  func TestBasicAuth(t *testing.T) {
    41  	ts := StartTest()
    42  	defer ts.Close()
    43  
    44  	session := testPrepareBasicAuth(false)
    45  
    46  	validPassword := map[string]string{"Authorization": genAuthHeader("user", "password")}
    47  	wrongPassword := map[string]string{"Authorization": genAuthHeader("user", "wrong")}
    48  	wrongFormat := map[string]string{"Authorization": genAuthHeader("user", "password:more")}
    49  	malformed := map[string]string{"Authorization": "not base64"}
    50  
    51  	ts.Run(t, []test.TestCase{
    52  		// Create base auth based key
    53  		{Method: "POST", Path: "/tyk/keys/defaultuser", Data: session, AdminAuth: true, Code: 200},
    54  		{Method: "GET", Path: "/", Code: 401, BodyMatch: `Authorization field missing`},
    55  		{Method: "GET", Path: "/", Headers: validPassword, Code: 200},
    56  		{Method: "GET", Path: "/", Headers: wrongPassword, Code: 401},
    57  		{Method: "GET", Path: "/", Headers: wrongFormat, Code: 400, BodyMatch: `Attempted access with malformed header, values not in basic auth format`},
    58  		{Method: "GET", Path: "/", Headers: malformed, Code: 400, BodyMatch: `Attempted access with malformed header, auth data not encoded correctly`},
    59  	}...)
    60  }
    61  
    62  func TestBasicAuthFromBody(t *testing.T) {
    63  	ts := StartTest()
    64  	defer ts.Close()
    65  
    66  	session := CreateStandardSession()
    67  	session.BasicAuthData.Password = "password"
    68  	session.AccessRights = map[string]user.AccessDefinition{"test": {APIID: "test", Versions: []string{"v1"}}}
    69  	session.OrgID = "default"
    70  
    71  	BuildAndLoadAPI(func(spec *APISpec) {
    72  		spec.UseBasicAuth = true
    73  		spec.BasicAuth.ExtractFromBody = true
    74  		spec.BasicAuth.BodyUserRegexp = `<User>(.*)</User>`
    75  		spec.BasicAuth.BodyPasswordRegexp = `<Password>(.*)</Password>`
    76  		spec.UseKeylessAccess = false
    77  		spec.Proxy.ListenPath = "/"
    78  		spec.OrgID = "default"
    79  	})
    80  
    81  	validPassword := `<User>user</User><Password>password</Password>`
    82  	wrongPassword := `<User>user</User><Password>wrong</Password>`
    83  	withoutPassword := `<User>user</User>`
    84  	malformed := `<User>User>`
    85  	emptyAuthHeader := map[string]string{"Www-Authenticate": ""}
    86  
    87  	ts.Run(t, []test.TestCase{
    88  		// Create base auth based key
    89  		{Method: "POST", Path: "/tyk/keys/defaultuser", Data: session, AdminAuth: true, Code: 200},
    90  		{Method: "POST", Path: "/", Code: 400, BodyMatch: `Body do not contain username`},
    91  		{Method: "POST", Path: "/", Data: validPassword, Code: 200, HeadersMatch: emptyAuthHeader},
    92  		{Method: "POST", Path: "/", Data: wrongPassword, Code: 401},
    93  		{Method: "POST", Path: "/", Data: withoutPassword, Code: 400, BodyMatch: `Body do not contain password`},
    94  		{Method: "GET", Path: "/", Data: malformed, Code: 400, BodyMatch: `Body do not contain username`},
    95  	}...)
    96  }
    97  
    98  func TestBasicAuthLegacyWithHashFunc(t *testing.T) {
    99  	globalConf := config.Global()
   100  
   101  	globalConf.HashKeys = true
   102  	globalConf.EnableHashedKeysListing = true
   103  	// settings to create BA session with legacy key format
   104  	globalConf.HashKeyFunction = ""
   105  	config.SetGlobal(globalConf)
   106  	defer ResetTestConfig()
   107  
   108  	ts := StartTest()
   109  	defer ts.Close()
   110  
   111  	// create session with legacy key format
   112  	session := testPrepareBasicAuth(false)
   113  
   114  	validPassword := map[string]string{"Authorization": genAuthHeader("user", "password")}
   115  
   116  	ts.Run(t, []test.TestCase{
   117  		// Create base auth based key
   118  		{Method: "POST", Path: "/tyk/keys/defaultuser", Data: session, AdminAuth: true, Code: 200},
   119  		{Method: "GET", Path: "/", Headers: validPassword, Code: 200},
   120  	}...)
   121  
   122  	// set custom hashing function and check if we still do BA session auth with legacy key format
   123  	globalConf.HashKeyFunction = storage.HashMurmur64
   124  	config.SetGlobal(globalConf)
   125  
   126  	ts.Run(t, []test.TestCase{
   127  		// Create base auth based key
   128  		{Method: "GET", Path: "/", Headers: validPassword, Code: 200},
   129  	}...)
   130  }
   131  
   132  func TestBasicAuthCachedUserCollision(t *testing.T) {
   133  	globalConf := config.Global()
   134  	globalConf.HashKeys = true
   135  	globalConf.HashKeyFunction = "murmur64"
   136  	config.SetGlobal(globalConf)
   137  	defer ResetTestConfig()
   138  
   139  	ts := StartTest()
   140  	defer ts.Close()
   141  
   142  	session := testPrepareBasicAuth(false)
   143  
   144  	correct := map[string]string{"Authorization": genAuthHeader("bellbell1", "password")}
   145  	remove1 := map[string]string{"Authorization": genAuthHeader("bellbell", "password")}
   146  	remove2 := map[string]string{"Authorization": genAuthHeader("bellbel", "password")}
   147  	remove3 := map[string]string{"Authorization": genAuthHeader("bellbe", "password")}
   148  	remove4 := map[string]string{"Authorization": genAuthHeader("bellb", "password")}
   149  	remove5 := map[string]string{"Authorization": genAuthHeader("bell", "password")}
   150  	add1 := map[string]string{"Authorization": genAuthHeader("bellbell11", "password")}
   151  	add2 := map[string]string{"Authorization": genAuthHeader("bellbell12", "password")}
   152  	add3 := map[string]string{"Authorization": genAuthHeader("bellbell13", "password")}
   153  
   154  	ts.Run(t, []test.TestCase{
   155  		// Create base auth based key
   156  		{Method: "POST", Path: "/tyk/keys/bellbell1", Data: session, AdminAuth: true, Code: http.StatusOK},
   157  		{Method: "GET", Path: "/", Headers: correct, Code: http.StatusOK},
   158  		{Method: "GET", Path: "/", Headers: remove1, Code: http.StatusUnauthorized},
   159  		{Method: "GET", Path: "/", Headers: remove2, Code: http.StatusUnauthorized},
   160  		{Method: "GET", Path: "/", Headers: remove3, Code: http.StatusUnauthorized},
   161  		{Method: "GET", Path: "/", Headers: remove4, Code: http.StatusUnauthorized},
   162  		{Method: "GET", Path: "/", Headers: remove5, Code: http.StatusUnauthorized},
   163  		{Method: "GET", Path: "/", Headers: add1, Code: http.StatusUnauthorized},
   164  		{Method: "GET", Path: "/", Headers: add2, Code: http.StatusUnauthorized},
   165  		{Method: "GET", Path: "/", Headers: add3, Code: http.StatusUnauthorized},
   166  		{Method: "GET", Path: "/", Headers: correct, Code: http.StatusOK},
   167  	}...)
   168  }
   169  
   170  func TestBasicAuthCachedPasswordCollision(t *testing.T) {
   171  	ts := StartTest()
   172  	defer ts.Close()
   173  
   174  	for _, useCache := range []bool{true, false} {
   175  		correct := map[string]string{"Authorization": genAuthHeader("bellbell1", "password")}
   176  		remove1 := map[string]string{"Authorization": genAuthHeader("bellbell1", "passwor")}
   177  		remove2 := map[string]string{"Authorization": genAuthHeader("bellbell1", "passwo")}
   178  		remove3 := map[string]string{"Authorization": genAuthHeader("bellbell1", "passw")}
   179  		remove4 := map[string]string{"Authorization": genAuthHeader("bellbell1", "pass")}
   180  		remove5 := map[string]string{"Authorization": genAuthHeader("bellbell1", "pas")}
   181  		add1 := map[string]string{"Authorization": genAuthHeader("bellbell1", "password1")}
   182  		add2 := map[string]string{"Authorization": genAuthHeader("bellbell1", "password22")}
   183  		add3 := map[string]string{"Authorization": genAuthHeader("bellbell1", "password333")}
   184  
   185  		t.Run(fmt.Sprintf("Cache disabled:%v", useCache), func(t *testing.T) {
   186  			session := testPrepareBasicAuth(useCache)
   187  
   188  			ts.Run(t, []test.TestCase{
   189  				// Create base auth based key
   190  				{Method: "POST", Path: "/tyk/keys/bellbell1", Data: session, AdminAuth: true, Code: http.StatusOK},
   191  				{Method: "GET", Path: "/", Headers: correct, Code: http.StatusOK},
   192  				{Method: "GET", Path: "/", Headers: remove1, Code: http.StatusUnauthorized},
   193  				{Method: "GET", Path: "/", Headers: remove2, Code: http.StatusUnauthorized},
   194  				{Method: "GET", Path: "/", Headers: remove3, Code: http.StatusUnauthorized},
   195  				{Method: "GET", Path: "/", Headers: remove4, Code: http.StatusUnauthorized},
   196  				{Method: "GET", Path: "/", Headers: remove5, Code: http.StatusUnauthorized},
   197  				{Method: "GET", Path: "/", Headers: add1, Code: http.StatusUnauthorized},
   198  				{Method: "GET", Path: "/", Headers: add2, Code: http.StatusUnauthorized},
   199  				{Method: "GET", Path: "/", Headers: add3, Code: http.StatusUnauthorized},
   200  				{Method: "GET", Path: "/", Headers: correct, Code: http.StatusOK},
   201  			}...)
   202  		})
   203  	}
   204  }
   205  
   206  func BenchmarkBasicAuth(b *testing.B) {
   207  	b.ReportAllocs()
   208  
   209  	ts := StartTest()
   210  	defer ts.Close()
   211  
   212  	session := testPrepareBasicAuth(false)
   213  
   214  	validPassword := map[string]string{"Authorization": genAuthHeader("user", "password")}
   215  	wrongPassword := map[string]string{"Authorization": genAuthHeader("user", "wrong")}
   216  	wrongFormat := map[string]string{"Authorization": genAuthHeader("user", "password:more")}
   217  	malformed := map[string]string{"Authorization": "not base64"}
   218  
   219  	// Create base auth based key
   220  	ts.Run(b, test.TestCase{
   221  		Method:    "POST",
   222  		Path:      "/tyk/keys/defaultuser",
   223  		Data:      session,
   224  		AdminAuth: true,
   225  		Code:      200,
   226  	})
   227  
   228  	for i := 0; i < b.N; i++ {
   229  		ts.Run(b, []test.TestCase{
   230  			{Method: "GET", Path: "/", Code: 401, BodyMatch: `Authorization field missing`},
   231  			{Method: "GET", Path: "/", Headers: validPassword, Code: 200},
   232  			{Method: "GET", Path: "/", Headers: wrongPassword, Code: 401},
   233  			{Method: "GET", Path: "/", Headers: wrongFormat, Code: 400, BodyMatch: `Attempted access with malformed header, values not in basic auth format`},
   234  			{Method: "GET", Path: "/", Headers: malformed, Code: 400, BodyMatch: `Attempted access with malformed header, auth data not encoded correctly`},
   235  		}...)
   236  	}
   237  }
   238  
   239  func BenchmarkBasicAuth_CacheEnabled(b *testing.B) {
   240  	b.ReportAllocs()
   241  
   242  	ts := StartTest()
   243  	defer ts.Close()
   244  
   245  	session := testPrepareBasicAuth(false)
   246  
   247  	validPassword := map[string]string{"Authorization": genAuthHeader("user", "password")}
   248  
   249  	// Create base auth based key
   250  	ts.Run(b, test.TestCase{
   251  		Method:    "POST",
   252  		Path:      "/tyk/keys/defaultuser",
   253  		Data:      session,
   254  		AdminAuth: true,
   255  		Code:      200,
   256  	})
   257  
   258  	for i := 0; i < b.N; i++ {
   259  		ts.Run(b, []test.TestCase{
   260  			{Method: "GET", Path: "/", Headers: validPassword, Code: 200},
   261  		}...)
   262  	}
   263  }
   264  
   265  func BenchmarkBasicAuth_CacheDisabled(b *testing.B) {
   266  	b.ReportAllocs()
   267  
   268  	ts := StartTest()
   269  	defer ts.Close()
   270  
   271  	session := testPrepareBasicAuth(true)
   272  
   273  	validPassword := map[string]string{"Authorization": genAuthHeader("user", "password")}
   274  
   275  	// Create base auth based key
   276  	ts.Run(b, test.TestCase{
   277  		Method:    "POST",
   278  		Path:      "/tyk/keys/defaultuser",
   279  		Data:      session,
   280  		AdminAuth: true,
   281  		Code:      200,
   282  	})
   283  
   284  	for i := 0; i < b.N; i++ {
   285  		ts.Run(b, []test.TestCase{
   286  			{Method: "GET", Path: "/", Headers: validPassword, Code: 200},
   287  		}...)
   288  	}
   289  }