github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/multiauth_test.go (about)

     1  package gateway
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	jwt "github.com/dgrijalva/jwt-go"
    15  	"github.com/justinas/alice"
    16  	"github.com/lonelycode/go-uuid/uuid"
    17  
    18  	"github.com/TykTechnologies/tyk/apidef"
    19  	"github.com/TykTechnologies/tyk/test"
    20  	"github.com/TykTechnologies/tyk/user"
    21  )
    22  
    23  const multiAuthDev = `{
    24  	"api_id": "55",
    25  	"org_id": "default",
    26  	"use_basic_auth": true,
    27  	"use_standard_auth": true,
    28  	"base_identity_provided_by": "auth_token",
    29  	"auth_configs": {
    30  		"basic": {"auth_header_name": "Authorization"},
    31  		"authToken": {"auth_header_name": "x-standard-auth"}
    32  	},
    33  	"version_data": {
    34  		"not_versioned": true,
    35  		"versions": {
    36  			"v1": {"name": "v1"}
    37  		}
    38  	},
    39  	"proxy": {
    40  		"listen_path": "/v1",
    41  		"target_url": "` + TestHttpAny + `"
    42  	}
    43  }`
    44  
    45  func createMultiAuthKeyAuthSession(isBench bool) *user.SessionState {
    46  	session := new(user.SessionState)
    47  	session.Rate = 100.0
    48  	session.Allowance = session.Rate
    49  	session.LastCheck = time.Now().Unix()
    50  	session.Per = 1.0
    51  	session.QuotaRenewalRate = 300 // 5 minutes
    52  	session.QuotaRenews = time.Now().Unix()
    53  	if isBench {
    54  		session.QuotaRemaining = 100000000
    55  		session.QuotaMax = 100000000
    56  	} else {
    57  		session.QuotaRemaining = 900
    58  		session.QuotaMax = 10
    59  	}
    60  	session.AccessRights = map[string]user.AccessDefinition{"55": {APIName: "Tyk Multi Key Test", APIID: "55", Versions: []string{"default"}}}
    61  	session.Mutex = &sync.RWMutex{}
    62  	return session
    63  }
    64  
    65  func createMultiBasicAuthSession(isBench bool) *user.SessionState {
    66  	session := new(user.SessionState)
    67  	session.Rate = 8.0
    68  	session.Allowance = session.Rate
    69  	session.LastCheck = time.Now().Unix()
    70  	session.Per = 1.0
    71  	session.QuotaRenewalRate = 300 // 5 minutes
    72  	session.QuotaRenews = time.Now().Unix() + 20
    73  	session.QuotaRemaining = 1
    74  	session.QuotaMax = -1
    75  	session.BasicAuthData = user.BasicAuthData{Password: "TEST"}
    76  	session.AccessRights = map[string]user.AccessDefinition{"55": {APIName: "Tyk Multi Key Test", APIID: "55", Versions: []string{"default"}}}
    77  	session.Mutex = &sync.RWMutex{}
    78  	return session
    79  }
    80  
    81  func getMultiAuthStandardAndBasicAuthChain(spec *APISpec) http.Handler {
    82  	remote, _ := url.Parse(TestHttpAny)
    83  	proxy := TykNewSingleHostReverseProxy(remote, spec, nil)
    84  	proxyHandler := ProxyHandler(proxy, spec)
    85  	baseMid := BaseMiddleware{Spec: spec, Proxy: proxy}
    86  	chain := alice.New(mwList(
    87  		&IPWhiteListMiddleware{baseMid},
    88  		&IPBlackListMiddleware{BaseMiddleware: baseMid},
    89  		&BasicAuthKeyIsValid{baseMid, nil, nil},
    90  		&AuthKey{baseMid},
    91  		&VersionCheck{BaseMiddleware: baseMid},
    92  		&KeyExpired{baseMid},
    93  		&AccessRightsCheck{baseMid},
    94  		&RateLimitAndQuotaCheck{baseMid},
    95  	)...).Then(proxyHandler)
    96  	return chain
    97  }
    98  
    99  func testPrepareMultiSessionBA(t testing.TB, isBench bool) (*APISpec, *http.Request) {
   100  	spec := LoadSampleAPI(multiAuthDev)
   101  
   102  	// Create BA
   103  	baSession := createMultiBasicAuthSession(isBench)
   104  	username := ""
   105  	if isBench {
   106  		username = uuid.New()
   107  	} else {
   108  		username = "0987876"
   109  	}
   110  	password := "TEST"
   111  	keyName := generateToken("default", username)
   112  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   113  	spec.SessionManager.UpdateSession(keyName, baSession, 60, false)
   114  
   115  	// Create key
   116  	session := createMultiAuthKeyAuthSession(isBench)
   117  	customToken := ""
   118  	if isBench {
   119  		customToken = uuid.New()
   120  	} else {
   121  		customToken = "84573485734587384888723487243"
   122  	}
   123  	// AuthKey sessions are stored by {token}
   124  	spec.SessionManager.UpdateSession(customToken, session, 60, false)
   125  
   126  	toEncode := strings.Join([]string{username, password}, ":")
   127  	encodedPass := base64.StdEncoding.EncodeToString([]byte(toEncode))
   128  
   129  	req := TestReq(t, "GET", "/", nil)
   130  	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass))
   131  	req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", customToken))
   132  
   133  	return spec, req
   134  }
   135  
   136  func TestMultiSession_BA_Standard_OK(t *testing.T) {
   137  	spec, req := testPrepareMultiSessionBA(t, false)
   138  
   139  	recorder := httptest.NewRecorder()
   140  	chain := getMultiAuthStandardAndBasicAuthChain(spec)
   141  	chain.ServeHTTP(recorder, req)
   142  
   143  	if recorder.Code != 200 {
   144  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   145  	}
   146  }
   147  
   148  func BenchmarkMultiSession_BA_Standard_OK(b *testing.B) {
   149  	b.ReportAllocs()
   150  
   151  	spec, req := testPrepareMultiSessionBA(b, true)
   152  
   153  	recorder := httptest.NewRecorder()
   154  	chain := getMultiAuthStandardAndBasicAuthChain(spec)
   155  
   156  	for i := 0; i < b.N; i++ {
   157  		chain.ServeHTTP(recorder, req)
   158  		if recorder.Code != 200 {
   159  			b.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   160  		}
   161  	}
   162  }
   163  
   164  func TestMultiSession_BA_Standard_Identity(t *testing.T) {
   165  	spec := LoadSampleAPI(multiAuthDev)
   166  
   167  	// Create BA
   168  	baSession := createMultiBasicAuthSession(false)
   169  	username := "0987876"
   170  	password := "TEST"
   171  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   172  	spec.SessionManager.UpdateSession("default0987876", baSession, 60, false)
   173  
   174  	// Create key
   175  	session := createMultiAuthKeyAuthSession(false)
   176  	customToken := "84573485734587384888723487243"
   177  	// AuthKey sessions are stored by {token}
   178  	spec.SessionManager.UpdateSession(customToken, session, 60, false)
   179  
   180  	to_encode := strings.Join([]string{username, password}, ":")
   181  	encodedPass := base64.StdEncoding.EncodeToString([]byte(to_encode))
   182  
   183  	recorder := httptest.NewRecorder()
   184  	req := TestReq(t, "GET", "/", nil)
   185  	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass))
   186  	req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", customToken))
   187  
   188  	chain := getMultiAuthStandardAndBasicAuthChain(spec)
   189  	chain.ServeHTTP(recorder, req)
   190  
   191  	if recorder.Code != 200 {
   192  		t.Error("Initial request failed with non-200 code, should have gone through!: \n", recorder.Code)
   193  	}
   194  
   195  	if recorder.Header().Get("X-Ratelimit-Remaining") == "-1" {
   196  		t.Error("Expected quota limit but found -1, wrong base identity became context")
   197  		t.Error(recorder.Header().Get("X-Ratelimit-Remaining"))
   198  	}
   199  }
   200  
   201  func TestMultiSession_BA_Standard_FAILBA(t *testing.T) {
   202  	spec := LoadSampleAPI(multiAuthDev)
   203  
   204  	// Create BA
   205  	baSession := createMultiBasicAuthSession(false)
   206  	username := "0987876"
   207  	password := "WRONG"
   208  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   209  	spec.SessionManager.UpdateSession("default0987876", baSession, 60, false)
   210  
   211  	// Create key
   212  	session := createMultiAuthKeyAuthSession(false)
   213  	customToken := "84573485734587384888723487243"
   214  	// AuthKey sessions are stored by {token}
   215  	spec.SessionManager.UpdateSession(customToken, session, 60, false)
   216  
   217  	to_encode := strings.Join([]string{username, password}, ":")
   218  	encodedPass := base64.StdEncoding.EncodeToString([]byte(to_encode))
   219  
   220  	recorder := httptest.NewRecorder()
   221  	req := TestReq(t, "GET", "/", nil)
   222  	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass))
   223  	req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", customToken))
   224  
   225  	chain := getMultiAuthStandardAndBasicAuthChain(spec)
   226  	chain.ServeHTTP(recorder, req)
   227  
   228  	if recorder.Code != 401 {
   229  		t.Error("Wrong response code received, expected 401: \n", recorder.Code)
   230  	}
   231  }
   232  
   233  func TestMultiSession_BA_Standard_FAILAuth(t *testing.T) {
   234  	spec := LoadSampleAPI(multiAuthDev)
   235  
   236  	// Create BA
   237  	baSession := createMultiBasicAuthSession(false)
   238  	username := "0987876"
   239  	password := "TEST"
   240  	// Basic auth sessions are stored as {org-id}{username}, so we need to append it here when we create the session.
   241  	spec.SessionManager.UpdateSession("default0987876", baSession, 60, false)
   242  
   243  	// Create key
   244  	session := createMultiAuthKeyAuthSession(false)
   245  	customToken := "84573485734587384888723487243"
   246  	// AuthKey sessions are stored by {token}
   247  	spec.SessionManager.UpdateSession(customToken, session, 60, false)
   248  
   249  	to_encode := strings.Join([]string{username, password}, ":")
   250  	encodedPass := base64.StdEncoding.EncodeToString([]byte(to_encode))
   251  
   252  	recorder := httptest.NewRecorder()
   253  	req := TestReq(t, "GET", "/", nil)
   254  	req.Header.Set("Authorization", fmt.Sprintf("Basic %s", encodedPass))
   255  	req.Header.Set("x-standard-auth", fmt.Sprintf("Bearer %s", "WRONGTOKEN"))
   256  
   257  	chain := getMultiAuthStandardAndBasicAuthChain(spec)
   258  	chain.ServeHTTP(recorder, req)
   259  
   260  	if recorder.Code != 403 {
   261  		t.Error("Wrong response code received, expected 403: \n", recorder.Code)
   262  	}
   263  }
   264  
   265  func TestJWTAuthKeyMultiAuth(t *testing.T) {
   266  	ts := StartTest()
   267  	defer ts.Close()
   268  
   269  	pID := CreatePolicy()
   270  
   271  	spec := BuildAndLoadAPI(func(spec *APISpec) {
   272  		spec.UseKeylessAccess = false
   273  
   274  		spec.AuthConfigs = make(map[string]apidef.AuthConfig)
   275  
   276  		spec.UseStandardAuth = true
   277  		authConfig := spec.AuthConfigs["authToken"]
   278  		authConfig.AuthHeaderName = "Auth-Token"
   279  		spec.AuthConfigs["authToken"] = authConfig
   280  		spec.BaseIdentityProvidedBy = apidef.AuthToken
   281  
   282  		spec.EnableJWT = true
   283  		spec.JWTSigningMethod = RSASign
   284  		spec.JWTSource = base64.StdEncoding.EncodeToString([]byte(jwtRSAPubKey))
   285  		jwtConfig := spec.AuthConfigs["jwt"]
   286  		jwtConfig.AuthHeaderName = "Auth-JWT"
   287  		spec.AuthConfigs["jwt"] = jwtConfig
   288  		spec.JWTIdentityBaseField = "user_id"
   289  		spec.JWTPolicyFieldName = "policy_id"
   290  		spec.JWTDefaultPolicies = []string{pID}
   291  
   292  		spec.Proxy.ListenPath = "/"
   293  	})[0]
   294  
   295  	LoadAPI(spec)
   296  
   297  	jwtToken := CreateJWKToken(func(t *jwt.Token) {
   298  		t.Claims.(jwt.MapClaims)["user_id"] = "user"
   299  		t.Claims.(jwt.MapClaims)["exp"] = time.Now().Add(time.Hour * 72).Unix()
   300  	})
   301  
   302  	key := CreateSession()
   303  
   304  	ts.Run(t, []test.TestCase{
   305  		{
   306  			Headers: map[string]string{"Auth-JWT": jwtToken, "Auth-Token": key},
   307  			Code:    http.StatusOK,
   308  		},
   309  		{
   310  			Headers: map[string]string{"Auth-JWT": jwtToken, "Auth-Token": key},
   311  			Code:    http.StatusOK,
   312  		},
   313  		{
   314  			Headers:   map[string]string{"Auth-JWT": jwtToken},
   315  			Code:      http.StatusUnauthorized,
   316  			BodyMatch: "Authorization field missing",
   317  		},
   318  		{
   319  			Headers:   map[string]string{"Auth-Token": key},
   320  			Code:      http.StatusBadRequest,
   321  			BodyMatch: "Authorization field missing",
   322  		},
   323  		{
   324  			Headers:   map[string]string{"Auth-JWT": "junk", "Auth-Token": key},
   325  			Code:      http.StatusForbidden,
   326  			BodyMatch: "Key not authorized",
   327  		},
   328  	}...)
   329  }